2.2 责任链模式
2.2.3 拦截器
由于动态代理一般都比较难理解,程序设计者会利用动态代理进行封装,设计出一个拦截器接口供开发者使用,开发者只要知道拦截器接口的方法、含义和作用即可,无须知道动态代理是怎么实现的。下面用JDC动态代理实现一个拦截器的逻辑,先定义拦截器接口Interceptor,如代码2-11。
代码2-11:定义拦截器接口Interceptor
public interface Interceptor {
boolean before(Object proxy, Object target, Method method, Object[] args);
void around(Object proxy, Object target, Method method, Object[] args);
void after(Object proxy, Object target, Method method, Object[] args);
}
这里定义三个方法,befor、around、after。分别给予这些方法如下逻辑定义。
- 三个方案的参数为:proxy代理对象、targer真实对象,method方法、args方法参数
- befor方法返回boolean值,它在真实对象前调用。当返回true时,则反射真实对象方法,当返回false时,则调用around方法。
- 在befor方法返回false情况下,调用aroud方法。
- 在反射真实对象方法或者around方法执行之后,调用after方法。
实现这个Interceptor接口,如代码2-12。
代码2-12:Myinterceptor
public class MyInterceptor implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.err.println("反射方法前逻辑");
return false;// 不反射被代理对象原有方法
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.err.println("反射方法后逻辑。");
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.err.println("取代了被代理对象的方法");
}
}
它使用JDK动态代理,实现了Interceptor接口方案。以代码2-5和代码2-6的接口和实现类为例,在JDK动态代理中使用拦截器,如代码2-13。
代码2-13:在JDK动态代理中使用拦截器
public class InterceptorJdkProxy implements InvocationHandler {
private Object target; //真实对象
private String interceptorClass = null;//拦截器全限定名
public InterceptorJdkProxy(Object target, String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
/**
* 绑定委托对象并返回一个【代理占位】
*
* @param target 真实对象
* @return 代理对象【占位】
*/
public static Object bind(Object target, String interceptorClass) {
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InterceptorJdkProxy(target, interceptorClass));
}
@Override
/**
* 通过代理对象调用方法,首先进入这个方法.
*
* @param proxy --代理对象
* @param method --方法,被调用方法
* @param args -- 方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (interceptorClass == null) {
//没有设置拦截器则直接反射原有方法
return method.invoke(target, args);
}
Object result = null;
//通过反射生成拦截器
Interceptor interceptor =
(Interceptor) Class.forName(interceptorClass).newInstance();
//调用前置方法
if (interceptor.before(proxy, target, method, args)) {
//反射原有对象方法
result = method.invoke(target, args);
} else {//返回false执行around方法
interceptor.around(proxy, target, method, args);
}
//调用后置方法
interceptor.after(proxy, target, method, args);
return result;
}
}
这里有两个属性,一个是target,它是真实对象,另一个是字符串interceptorClass,它是一个拦截器的全限定名。执行步骤如下:
- 第一步,在bind方法中用JDK动态代理绑定了一个对象,然后返回代理对象。
- 第二步,如果没有设置拦截器,则直接反射真实对象的方法,然后结束,否则进行第三步。
- 第三步,通过反射生成拦截器,并准备使用它。
- 第四步,调用拦截器的befor方法,如果返回为true,反射原来的方法;否则运行拦截器的aroud方法。
- 第五步,调用拦截器的after方法。
- 第六步,返回结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JkjXoYI7-1571930111427)(C:/Users/80558/OneDrive/学习笔记/SSM+Redis/第二章/images/拦截器工作流程.png)]
- 由设计者编写拦截器(InterceptorJdkProxy),开发者只要知道拦截器的作用(before,around,after方法定义)就可以编写拦截器(MyInterceptor)实现业务逻辑。
- 设计者可能是精通java的开发人员,他来完成动态代理的逻辑代码编写。
- 设计则只会把拦截器接口暴露给开发者使用,让动态代理的逻辑在开发者视野中"消失"。
拦截器进一步简化动态代理的使用方法,使程序变得更简单,现在我们把自己想象成不懂动态代理的开发人员,现在我们只需要提供已实现业务逻辑代码的拦截器,即可完成动态代理并拦截方法。如代码2-14。
代码2-14:测试MyInterceptor拦截器
public static void testInterceptor() {
HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),
"intercept.MyInterceptor");
proxy.sayHelloWorld();
}
2.2.4 责任链模式
有的时候,拦截器可能有多个。举个例子,一个程序员需要请假一周。如果把请假申请单看成一个对象,那么它需要经过项目经理、部门经理、人事等多个角色的审批,每个角色都有机会通过拦截这个申请单进行审批或者修改。这个时就要考虑提供项目经理、部门经理和人事的处理逻辑,所以需要提供3个拦截器,而传递的则是请假申请单。
当个对象在一条链上被多个拦截器拦截处理(拦截器也可以选择不拦截处理它)时,我们把这样的设计模式称为式,它用于一个对象在多个角色中传递的场景。还是刚才的例子,申请单走到项目经理那,经理可能把申请时间“一周”改为"5天”,从而影响了后面的审批,后面的审批都要根据前面的结果进行。这个时候可以考虑用层层代理来实现,就是当申请单(target)走到项目经理处,使用第一个动态代理proxy1。当它走到部门经理处,部门经理会得到一在项目经理的代理proxy1基础上生成的proxy2来处理部门经理的逻。当它走到人事处,会在proxy2的基础生成proxy3如果还有其他角色,依此类推即可,用下图来描述拦截逻辑会更加清晰。
定义三个拦截器。如代码2-15。
代码2-15:责任链拦截器接口定义
/***********************************拦截器1************************************/
public class Interceptor1 implements Interceptor {
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器1】的before方法");
return true;
}
public void around(Object proxy, Object target, Method method, Object[] args) {
}
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器1】的after方法");
}
}
/***********************************拦截器2************************************/
public class Interceptor2 implements Interceptor {
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器2】的before方法");
return true;
}
public void around(Object proxy, Object target, Method method, Object[] args) {}
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器2】的after方法");
}
}
/***********************************拦截器3************************************/
public class Interceptor3 implements Interceptor {
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器3】的before方法");
return true;
}
public void around(Object proxy, Object target, Method method, Object[] args) {}
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器3】的after方法");
}
}
实现责任链多拦截器,如代码2-16。
代码2-16:测试责任链模式上的多拦截器
public static void testChain() {
HelloWorld proxy1 = (HelloWorld) InterceptorJdkProxy.bind(
new HelloWorldImpl(), "intercept.Interceptor1");
HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(
proxy1, "intercept.Interceptor2");
HelloWorld proxy3 = (HelloWorld) InterceptorJdkProxy.bind(
proxy2, "intercept.Interceptor3");
proxy3.sayHelloWorld();
}
运行结果为:
befor方法按照从最后一个拦截器到第一个拦截器的加载顺序运行,而after方法则按照从第一个拦截器到最后一个拦截器的加载顺序运行。
责任链模式优点:可以在传递链上加入新的拦截器,增加拦截逻辑。
缺点:会增加代理和反射,而代理和反射性能不高。