基于JDK动态代理实现的拦截器和责任链模式

在基于jdk的动态代理中被代理的类(我们称之为真实对象,相同)需要实现一个接口,然后实现这个接口中的方法,作为真实方法(即以后被代理的方法)。然后再自定义一个类(MyProxy)作为代理类,这个类要实现InvocationHandler接口,并且实现里面的invoke()方法。这个invoke()方法一般有三个参数,一个是Object(通常表示被代理的类),一个Method(以后将利用这个method对象去反射真实对象的核心方法),一个是Object [] args,表示真实对象的真实方法的参数,Object 型的数组表示有多个参数的情况。在一般的动态代理中,还需要在代理类中定义一个bind()方法,这个方法将返回一个Object类型的对象(这里执行完Proxy.newProxyInstance(参数)后会将真实对象和代理对象绑定在一起,返回的对象可以转换为真实对象的接口类型,也就是真实对象的父类型,即真实对象的类型,执行这个对象的方法就会执行真实对象的方法,因为真实对象的方法也就是它的实现类的方法,但是不同的是,这个方法是被反射执行的,因此会走代理类中的invoke()方法,从而实现被代理的含义),这个方法应该被定义为静态(static)的方法,因为这样在测试类中便可以直接通过代理类名调用之。这个方法只有一个Object的参数,在传参时直接new 被代理的类()传入即可。bind方法的实现是直接返回 Proxy.newProxyInstance()执行的结果(其实就是代理对象)。newProxyInstance()方法有三个参数,第一个参数表示被代理类的类加载器(即可以通过传入对象Object.getClass().getClassLoader()传入),第二个表示被代理类实现的接口(即可以通过Object.getClass().getInterfaces()传入),可以同时实现多个接口。第三个表示代理类的对象,如果传入this,表示代理的类是当前对象。在这个代理类中还应该有一个私有的Object属性,在bind方法中利用this指定将bind方法传入的参数赋值给它以作为method.invoke()方法中的参数,即表示被代理的类(真实对象)。
在拦截器的实现中,拦截器可以有一个,也可以有多个。同理也是需要自定义一个类来作为被代理的类。这个类需要实现一个以上的接口且实现接口中定义的抽象方法。然后编写拦截器。拦截器也是需要实现一个拦截器接口,这个接口中需要定义三个方法,分别是before()方法,around()方法,after()方法。这三个方法的参数个数,种类,寓意是一样的,第一个Object proxy 表示要传入代理类的对象,第二个Object target,表示要传入被代理类的对象,第三个Object [] args,表示要传入的参数。其中before()方法返回值类型为Boolean类型,它在真实对象被代理前调用。如果返回true,则反射真实对象的真实方法,如果返回false,则调用around()方法,但是不管返回什么,最终都会执行after()方法。这也是代理类中invoke()方法的部分实现逻辑。这个有点像try{…}catch(Exception e{…}finally{…}。try相当于before()方法,catch相当于around()方法,finally相当于after()方法。而如果成功反射真实对象的方法,那么就像没有捕获到异常,只执行了try{}代码块里面的代码,如果返回false,那么就像已经捕获到异常了,执行around()方法就类似执行catch{}代码块,最后无论如何也要执行的after()方法就像是不管是否捕获到异常都会执行finally{}代码块一样。在定义完拦截器之后,要写一个代理类,这个代理类和之前的代理类有点区别,但是基本原理不变。同样也是需要实现InvocationHandler接口,并且实现此接口的invoke方法,然后还要定义一个bind方法,用来绑定被代理类和代理类之间的代理关系。
上面我们说过这个代理类和我们开始讲的代理类的实现之间有点不同,那么究竟哪里不同呢?其实可以把将要写的代理类看着是之前讲的代理类的升级处理。首先这个代理类有两个私有的属性,一个是Object target,一个是String interceptorClassPath。那么它们分别表示什么意思呢?顾名思义,第一个表示真实对象,即被代理的类。第二个表示拦截器的类路径,我们要结合java反射的知识利用类路径来生成对象。为什么要生成这个对象呢?因为在代理类的invoke()方法的实现里面,需要用到这个对象,而一般这个对象其实就是一个拦截器,在实现invoke()方法时,就可以根据这个拦截器调用before()方法,再根据其返回结果决定是反射真实对象的真实方法,还是走拦截器的around()方法,从而实现拦截的原理。
bind()方法的这两个参数在调用bind()时传入,但不是利用this赋值给代理类中定义的两个私有的属性。而是通过有参构造器赋值给私有的属性。即bind()方法里面的Proxy.newProxyInstance()方法的最后一个参数中传入的对象为new 这个代理类的有参构造方法并且将bind()方法的形参最为它的实参。这样不仅将真实对象和代理对象绑定在一起,而且还将真实对象和拦截器的类路径成功传入到代理类的对象中。
接下来我们要实现invoke()方法,在invoke方法的实现中,我们首先判断interceptorClassPath是否为空,如果为空,则说明没有拦截器,直接method.invoke()反射真实对象的方法且返回结果即可,否则,就要加入拦截器。首先利用java反射的原理Class.forName(“interceptorClassPath”)获得拦截器对象。然后利用拦截器对象执行before()方法,判断before()方法返回的结果。如果为真则执行method.invoke()反射真实对象的方法,表示未被拦截。否则,执行此拦截器的around()方法,表示已被拦截。最后执行after()方法。当然在上面我们需要定义一个Object obj = null;如果反射真实对象的方法,就将执行返回的结果赋值给obj,然后最终返回obj作为invoke方法的返回结果。如果拦截器有多个,就是责任链模式了。我们可以用层层代理来实现这个模式。第一层(最里面一层)一般代理真实对象,第二层代理第一层返回的代理对象,以此类推,除第一层外,下一层分别代理的是上一层返回的代理对象,类似于迭代代理,这样就形成了一个链式的代理,我们称之为责任链模式。代码执行时,从最外一层开始,先判断最外一层before方法,如果返回值为false,则不会执行次外层以内的代码,直接执行最外层拦截器的around()和after()方法,如果返回值为true,则继续执行并判断次外层的before()返回值,如果为false,则不再判断下一层的before()返回值,会继续执行当前拦截器的around()方法和after方法,执行完后会执行已经执行过before()方法的外层拦截器的after方法。总结一下就是:代码将从最外层开始执行,执行到哪一层就首先判断那一层的before()方法的返回值,如果为true,则会继续判断下一层,一直到最内一层,如果仍然为true,这时就会反射真实方法并且执行,执行结束后一次有内向外执行每一层拦截器的after()方法直至结束。在这个过程中,如果哪一层的before()返回false,则不会继续判断下一层的before()返回值,更不会反射执行真实方法,因为false表示已被拦截,然后around()方法,且只执行before()返回值为false的这层的around()方法,执行完后一次由内到外执行没一层的after()方法,直至最外层,最终执行结束。这有点像闯关,从外到里,一次闯关,只由闯过所有的关卡(所有的before()返回值为true),才会反射执行真实方法(闯关成功),只要有一关没有闯过去(有一个before()返回false),就会停止且被拦截往回走。

你可能感兴趣的:(基于JDK动态代理实现的拦截器和责任链模式)