动态代理实现拦截器(转载)

原文:http://www.cnblogs.com/kakaxisir/p/4579110.html?utm_source=tuicool&utm_medium=referral

 

以前一直觉得写博客是给别人看的,所以很少分享自己写的东西。这段时间突然意识到博客是给自己看的。

欢迎各位喜欢java的朋友骚扰。

最近在学习mybatis,看了下源代码。翻到了Interceptor的实现,恰好前不久看过JDK的动态代理和责任链,因此来记录一下。

一:JDK的动态代理

概念性质的东西就不谈了,毕竟网上很多。JDK的动态代理要求接口和接口的实现类

1
2
3
public  interface  Target {
     public  void  execute();
}

 

1
2
3
4
5
6
7
8
9
10
11
/**
  * Target的实现类
  * @author wpr
  *
  */
public  class  TargetImpl  implements  Target {
     @Override
     public  void  execute() {
         System.out.println( "execute" );
     }
}

   a.JDK原生的动态代理写法

 要求实现InvocationHandler接口,在invoke方法内实现拦截的逻辑(不懂得去看JDK的动态代理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  class  TargetProxy  implements  InvocationHandler{
     Target target;
     public  TargetProxy(Target target) {
         this .target = target;
     }
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)
             throws  Throwable {
         System.out.println( "拦截前" );
         Object o= method.invoke(target, args);
         System.out.println( "拦截后" );
         return  o;
     }
}

 测试的类:

1
2
3
4
5
6
7
@Test
public  void  test3(){
     Target target =  new  TargetImpl();
     target = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(),
             target.getClass().getInterfaces(), new  TargetProxy(target));
     target.execute();
}

 以上就是JDK动态代理的实现,但是存在问题,Proxy.newProxyInstance(..)完全可以交给TargetProxy来处理,于是第二版出现

1
2
3
4
5
6
7
public  class  TargetProxy  implements  InvocationHandler{
     //...........上面的代码省略了...............
     public  static  Object bind(Target target){
         return  Proxy.newProxyInstance(target.getClass().getClassLoader(),
                 target.getClass().getInterfaces(),  new  TargetProxy(target));
     }
}

 测试类:

1
2
3
4
5
6
@Test
public  void  test2(){
     Target target =  new  TargetImpl();
     target = (Target) TargetProxy.bind(target);
     target.execute();
}

 但还是存在问题,业务代码如果是execute()的话,所有的逻辑都写死在invoke()方法里面了,不符合设计模式的要求。结合面向切面的编程,做如下说明,target.execute()视为业务代码,在invoke()方法前进行插入切面(例如记录日志、开启事务等),设计Interceptor接口

1
2
3
public  interface  Interceptor {
     public  void  intercept();
}

 intercept()方法负责处理各种前期准备,下面是Interceptor的两个实现类

1
2
3
4
5
6
public  class  LogInterceptor  implements  Interceptor{
     @Override
     public  void  intercept(){
         System.out.println( "日志记录开始" );
     }
}

 

1
2
3
4
5
6
public  class  TransactionInterceptor  implements  Interceptor {
     @Override
     public  void  intercept() {
         System.out.println( "事务开启" );
     }
}

 代理对象进一步改变,为了形象的说明是拦截器栈,所以我用了Stack,但是感觉使用List(ArrayList更合理一点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  class  TargetProxy  implements  InvocationHandler{
     private  Target target;
     private  Stack<Interceptor> interceptorStack;
     
     public  TargetProxy(Target target, Stack<Interceptor> interceptorStack) {
         this .target = target;
         this .interceptorStack = interceptorStack;
     }
 
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)
             throws  Throwable {
         for (Interceptor interceptor:interceptorStack){
             interceptor.intercept();
         }
         return  method.invoke(target, args);
     }
}

 在每次执行业务代码execute(...)之前都会拦截,测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
@org .junit.Test
public  void  test() {
     Stack<Interceptor> interceptorStack = new  Stack<>();
     interceptorStack.add( new  LogInterceptor());
     interceptorStack.add( new  TransactionInterceptor());
     
     Target target =  new  TargetImpl();
     target = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                     target.getClass().getInterfaces(), new  TargetProxy(target, interceptorStack));
     target.execute();
}

 接下来更近一步,根据代码的设计准则,将不变的和变化的分离开。我们设计一个Invocation的类,先看下它的实现:

(其实这个地方还可以这样理解:为了在Interceptor中得到被拦截对象的信息,需要定义一种数据结构来表示被拦截的方法,就是Invocation。这样就实现了拦截器Interceptor和具体的对象之间的解耦)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  class  Invocation {
     private  Object target;
     private  Method method;
     private  Object[] args;
 
     public  Invocation(Object target, Method method, Object[] args) {
         this .target = target;
         this .method = method;
         this .args = args;
     }
     /**
      * 调用代理类的方法
      * @return
      * @throws IllegalAccessException
      * @throws IllegalArgumentException
      * @throws InvocationTargetException
      */
     public  Object process()  throws  IllegalAccessException, IllegalArgumentException, InvocationTargetException{
         return  method.invoke(target, args);
     }
        //省略了getter和setter      
}

 Invocation类就是将被代理的目标类对立出出来,target表示目标类,method是拦截的方法,args是方法参数,于是新的TargetProxy变成了下面的样子。仅仅是invoke

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public  class  TargetProxy  implements  InvocationHandler{
     private  Target target;
     private  Interceptor interceptor;
     
     public  TargetProxy(Target target,Interceptor interceptor) {
         this .target = target;
         this .interceptor= interceptor;
     }
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)
             throws  Throwable {
         Invocation invocation =  new  Invocation(target, method, args);
         return  interceptor.intercpt(invocation);
     }
}

 同时,要改变Interceptor的行为:

1
2
3
4
public  interface  Interceptor {
     
     public  Object intercpt(Invocation invocation)  throws  IllegalAccessException, IllegalArgumentException, InvocationTargetException;
}

 具体的实现如下,一定返回invocation.process();要不然拦截就会断掉

1
2
3
4
5
6
7
8
9
public  class  LogInterceptor  implements  Interceptor{
 
     @Override
     public  Object intercpt(Invocation invocation)  throws  IllegalAccessException, IllegalArgumentException, InvocationTargetException {
         System.out.println( "打印日志" );
         return  invocation.process();
     }
 
}

 但是问题又出现了,我们希望目标类只需要了解拦截它的类就可以,并不需要知道它的代理类,于是把target的拦截过程放在Interceptor接口中完成(实际操作交个TargetProxy)。最终我们的Interceptor接口变成了

1
2
3
4
public  interface  Interceptor {
     public  Object intercept(Invocation invocation)  throws  IllegalAccessException, IllegalArgumentException, InvocationTargetException;
     public  Object register(Object object);
}
1
2
3
4
5
6
7
8
9
10
11
public  class  LogInterceptor  implements  Interceptor{
     @Override
     public  Object intercept(Invocation invocation)  throws  IllegalAccessException, IllegalArgumentException, InvocationTargetException {
         System.out.println( "日志拦截前:" );
         return  invocation.process();
     }
     @Override
     public  Object register(Object target) {
         return  TargetProxy.bind(target,  this );
     }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  class  TargetProxy  implements  InvocationHandler{
     private  Object target;
     private  Interceptor interceptor;
     
     public  TargetProxy(Object target, Interceptor interceptor) {
         this .target = target;
         this .interceptor = interceptor;
     }
 
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)
             throws  Throwable {
         Invocation invocation =  new  Invocation(target, method, args);
         return  interceptor.intercept(invocation);
     }
     
     public  static  Object bind(Object target,Interceptor interceptor){
         return  Proxy.newProxyInstance(target.getClass().getClassLoader(),
                 target.getClass().getInterfaces(), new  TargetProxy(target,interceptor));
     }
}

 到此为止,目标类仅需要知道在执行前应该由谁去拦截它就可以了,测试代码如下:

1
2
3
4
5
6
7
@org .junit.Test
public  void  test() {
     Target target =  new  TargetImpl();
     Interceptor interceptor =  new  LogInterceptor();
     target =(Target) interceptor.register(target);
     target.execute();
}

 好处显而易见,在使用时根本不必知道代理的存在,只要定义业务逻辑,和对业务逻辑的拦截(切面),然后把他们绑定在一起就可以了。

二:责任链

以上代码实现了对一个业务的一次拦截,但如果对其进行多次拦截的话就需要用到责任链了(依然略过概念,自己google吧)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public  class  InterceptorChain {
     private  Stack<Interceptor> interceptors;
     
     public  InterceptorChain(Stack<Interceptor> interceptors) {
         this .interceptors = interceptors;
     }
     public  Object registerAll(Object target){
         for (Interceptor interceptor:interceptors){
             target = TargetProxy.bind(target, interceptor);
         }
         return  target;
     }
     public  void  addInterceptor(Interceptor interceptor){
         interceptors.add(interceptor);
     }
     public  Stack<Interceptor> getInterceptor(){
         return  (Stack<Interceptor>) Collections.unmodifiableCollection(interceptors);
     }
}

 registerAll(...)方法来完成对目标的全部代理,一层一层的包裹,测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
     public  void  interceptorChainTest(){
         Stack<Interceptor> interceptors =  new  Stack<>();
         LogInterceptor logInterceptor =  new  LogInterceptor();
         TransactionInterceptor transactionInterceptor =  new  TransactionInterceptor();
         interceptors.add(logInterceptor);
         interceptors.add(transactionInterceptor);
         InterceptorChain interceptorChain =  new  InterceptorChain(interceptors);
         
         Target target =  new  TargetImpl();
         target= (Target)interceptorChain.registerAll(target);
         target.execute();
     }

 以上内容都比较基础和理论,但mybatis的Interceptor完全是我们这样实现的

三:mybatis的拦截分析

其中大部分和之前的分析一致,Plugin就是TargetProxy,内部实现的代码逻辑也完全相同,Signature是实现对特定方法拦截的,不在今天的记录范围内。之前的工作相当于完成了这个部分的工作。

猜你喜欢

转载自xgtxxxx.iteye.com/blog/2267726