【javaEE互联网轻量级框架整合开发】学习笔记 第二章 拦截器及责任链模式

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方法则按照从第一个拦截器到最后一个拦截器的加载顺序运行。

责任链模式优点:可以在传递链上加入新的拦截器,增加拦截逻辑。

缺点:会增加代理和反射,而代理和反射性能不高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值