责任链模式
责任链模式(Chain of Responsibility Pattern)为申请创立了一个接收者对象的链。
这种模式给予申请的类型,对申请的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都蕴含对另一个接收者的援用。如果一个对象不能解决该申请,那么它会把雷同的申请传给下一个接收者,依此类推。援用w3c对责任链的形容
在理论的开发中咱们也会遇见,咱们须要对外部提供一个接口,两头可能会后果很多非业务的解决办法(日志记录,权限校验,敏感数据荡涤....) 然而对于业务来说是通明的。每个处理器都是独立的,不应该存在耦合关系,才能够让咱们随便的去拼接。
代码大略实现关系依赖关系
首先咱们须要定义一个接口RequestPlugin,前面所有的插件须要去实现这个接口就行
/** * 插件接口定义 */public interface RequestPlugin { /** * 路由 */ void interceptor(InterceptorChainWrapper routeChainWrapper); /** * 是否启用 */ boolean enable();}
每个插件解决的性能都是独立的。然而插件之间可能存在排序关系,先加载执行谁后执行谁,比方申请入口应该保留最原始的参数,所以日志的插件个别是放在第一个。
这个时候咱们每个插件是不是就要有个排序,所以这里定义了一个注解,通过order的数值大小进行排序。
/** * 插件注解 */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface PluginAnno { int order() default Ordered.HIGHEST_PRECEDENCE; String name();}
这里我实现了三个插件去构建一个插件的责任链:日志解决插件,解析解决插件,权限校验插件;
/** * 日志解决 */@PluginAnno(order = 1, name = "LogSavePlugin")public class LogSavePlugin implements RequestPlugin { @Override public void interceptor(InterceptorChainWrapper routeChainWrapper) { System.out.println("日志插件 : LogSavePlugin"); routeChainWrapper.interceptor(); } @Override public boolean enable() { return true; }}/** * 解析解决 */@PluginAnno(order = 2, name = "ParseHandlePlugin")public class ParseHandlePlugin implements RequestPlugin { @Override public void interceptor(InterceptorChainWrapper routeChainWrapper) { System.out.println("解析解决插件 : ParseHandlePlugin"); routeChainWrapper.interceptor(); } @Override public boolean enable() { return false; }}/** * 权限校验 */@PluginAnno(order = 3, name = "AuthorCheckPlugin")public class AuthorCheckPlugin implements RequestPlugin { @Override public void interceptor(InterceptorChainWrapper routeChainWrapper) { System.out.println("权限插件 : AuthorCheckPlugin"); routeChainWrapper.interceptor(); } @Override public boolean enable() { return true; }}
注入三个插件,将他们排序。
/** * 配置插件 */@Configurationpublic class RequestPluginConfig { private List<RequestPlugin> requestPlugins; /** * 注入相干处理器 * 对处理器 */ public RequestPluginConfig(List<RequestPlugin> requestPlugins) { this.requestPlugins = requestPlugins.stream().sorted(Comparator.comparingInt(o -> o.getClass().getAnnotation(PluginAnno.class).order())).collect(Collectors.toList()); } public InterceptorChainWrapper createChainWrapper() { return new InterceptorChainWrapper(requestPlugins); }}
理论启用插件调用类和测试
/** * 插件调用链 */public class InterceptorChainWrapper { private final AtomicInteger atomicInteger = new AtomicInteger(-1); private List<RequestPlugin> requestPlugins; public InterceptorChainWrapper(List<RequestPlugin> requestPlugins) { this.requestPlugins = requestPlugins; } /** * 理论触发 */ public void interceptor() { if (atomicInteger.incrementAndGet() == requestPlugins.size()) { return; } RequestPlugin plugin = requestPlugins.get(atomicInteger.get()); if (!plugin.enable()) { interceptor(); return; } plugin.interceptor(this); }}/*** 模仿调用**/public class StudyApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(StudyApplication.class, args); RequestPluginConfig requestPluginConfig = context.getBean(RequestPluginConfig.class); requestPluginConfig.createChainWrapper().interceptor(); }}
咱们看了下面实现计划,咱们再来看看理论的框架时怎么实现的,这里来搂下mybatis拦截器,看看它和咱们实现的有什么不同。mybatis的拦截器是将对象通过拦截器将理论对象包装为代理类。
老样子定义一个拦挡接口
/** * @author Clinton Begin */public interface Interceptor { /** * 拦挡办法 */ Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties);}
Intercepts注解去标识拦挡类,Signature注解标识拦挡的一些办法,参数,执行类型
/** * @author Clinton Begin */@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Intercepts { Signature[] value();}/** * @author Clinton Begin */@Documented@Retention(RetentionPolicy.RUNTIME)@Target({})public @interface Signature { Class<?> type(); String method(); Class<?>[] args();}
上面就是拦挡链了。InterceptorChain是一个拦挡链,pluginAll()办法会将增加拦挡链一个个的遍历进去,调用拦截器的plugin()办法,plugin()办法中回去调用plugin类的warp()办法,创立一个代理类。就这样理论的调用对象会被包装一遍又一遍最终将代理对象返回进来。前面理论对象执行办法是都会调用invoke()办法,合乎条的调用intercept()办法而咱们的解决逻辑就应该写在intercept()办法中。
/** * @author Clinton Begin */public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<>(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); }}public class Plugin implements InvocationHandler { private final Object target; private final Interceptor interceptor; private final Map<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; } public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> methods = signatureMap.get(method.getDeclaringClass()); if (methods != null && methods.contains(method)) { return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) { Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); // issue #251 if (interceptsAnnotation == null) { throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName()); } Signature[] sigs = interceptsAnnotation.value(); Map<Class<?>, Set<Method>> signatureMap = new HashMap<>(); for (Signature sig : sigs) { Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>()); try { Method method = sig.type().getMethod(sig.method(), sig.args()); methods.add(method); } catch (NoSuchMethodException e) { throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e); } } return signatureMap; } private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { Set<Class<?>> interfaces = new HashSet<>(); while (type != null) { for (Class<?> c : type.getInterfaces()) { if (signatureMap.containsKey(c)) { interfaces.add(c); } } type = type.getSuperclass(); } return interfaces.toArray(new Class<?>[interfaces.size()]); }}
有点惋惜的中央,mybatis拦截器没有实现order,拦截器增加程序须要特地的留神。