AOP--代理模式,拦截器的简易实现及原理

上文中提到代理分为静态代理和动态代理,采用代理是为了通过不修改源代码的情况下给程序动态统一添加功能,利用代理技术可以将业务逻辑中一些非业务逻辑的代码分离出来,把他们独立到业务逻辑类外,比如日志记录,性能统计,安全控制,事务处理,异常处理等。这样做,不仅降低了业务逻辑和非业务逻辑的耦合性,提高程序的可重用性,同时提高了开发的效率。

下面以添加日志记录为例,分析静态代理的使用。创建一个用户管理类UserManagerImpl,并创建添加用户方法addUser,为其良好扩展性,创建一个通用接口UserManager,代码分别如下:

接口代码:

[java] 
view plain
 copy

  1. package com.snail.pattern;  
  2.   
  3. public interface UserManager {  
  4.   
  5.     public void addUser(String userId,String userName);  
  6. }  

实现类代码:

[java] 
view plain
 copy

  1. package com.snail.pattern;  
  2.   
  3. public class UserManagerImpl implements UserManager {  
  4.   
  5.     public void addUser(String userId, String userName) {  
  6.           
  7.         try {  
  8.             //System.out.println(“开始执行”);  
  9.             System.out.println(“HelloWorld!”);  
  10.             //System.out.println(“执行成功!”);  
  11.         }catch(Exception e) {  
  12.             e.printStackTrace();  
  13.             //System.out.println(“执行失败!”);  
  14.             throw new RuntimeException();  
  15.         }     
  16.     }  
  17. }  

从代码可以看出,注释里面的日志内容和业务逻辑毫无关系,无形中使耦合性增加,如果很多类中需要添加这些日志代码,工作量不言而喻,修改起来也非常麻烦。如果采用静态代理把打印日志的代码抽取到代理类中,通过代理类和业务逻辑类继承自同一个父类,客户端直接调用代理类完成需求,这样就解决了客户端与业务逻辑类的耦合。示例代码如下:

[java] 
view plain
 copy

  1. package com.snail.pattern;  
  2.   
  3.   
  4. public class UserManagerImplProxy implements UserManager{  
  5.   
  6.   
  7.     private UserManager userManager;  
  8.       
  9.     public UserManagerImplProxy(UserManager userManager){  
  10.         this.userManager = userManager;  
  11.     }  
  12.       
  13.     @Override  
  14.     public void addUser(String userId, String userName) {  
  15.         try {  
  16.             System.out.println(“开始执行”);  
  17.             userManager.addUser(userId, userName);  
  18.             System.out.println(“执行成功!”);  
  19.         }catch(Exception e) {  
  20.             e.printStackTrace();  
  21.             System.out.println(“执行失败!”);  
  22.         }             
  23.     }  
  24. }  

客户端调用代码如下:

[java] 
view plain
 copy

  1. package com.snail.pattern;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());  
  10.         userManager.addUser(“0111”“张三”);  
  11.     }  
  12.   
  13. }  

静态代理虽隔离了与业务逻辑无关的代码,降低了耦合,让业务逻辑类更专注于业务逻辑,但无法减少代码量,系统重复代码过多,加大了程序员工作量。因此,JDK动态代理完美解决了此问题,动态代理支持在系统运行期给类动态添加代理,然后通过操控代理类完成对目标类的调用。

继续演化上面举的例子,将静态代理改为动态代理,抽象类UserManager和目标类UserManagerImpl中的代码不变,将静态代理类UserManagerImplProxy删除,添加LoadHandler类,并让它实现InvocationHandler接口中的invoke方法,代码如下:

[java] 
view plain
 copy

  1. package com.snail.pattern;  
  2.   
  3.   
  4. import java.lang.reflect.InvocationHandler;  
  5. import java.lang.reflect.Method;  
  6. import java.lang.reflect.Proxy;  
  7.   
  8.   
  9. public class LogHandler implements InvocationHandler {  
  10.       
  11.     //保留一份targetObject目标类对象  
  12.     private Object targetObject;  
  13.       
  14.     //Proxy类动态创建一份目标代理类  
  15.     public Object newProxyInstance(Object targetObject){  
  16.         this.targetObject = targetObject;  
  17.         return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);   
  18.     }  
  19.       
  20.     @Override  
  21.     public Object invoke(Object proxy, Method method, Object[] args)  
  22.             throws Throwable {  
  23.         System.out.println(“开始执行!”);  
  24.           
  25.         for(int i=0;i<args.length;i++){  
  26.             System.out.println(args[i]);  
  27.         }  
  28.         Object ret = null;  
  29.           
  30.         try{  
  31.             //调用目标方法  
  32.             ret = method.invoke(targetObject, args);  
  33.             System.out.println(“执行成功!”);  
  34.         }catch(Exception e){  
  35.             e.printStackTrace();  
  36.             System.out.println(“执行失败!”);  
  37.             throw e;  
  38.         }  
  39.         return ret;  
  40.     }  
  41. }  

Proxy类所创建的目标类必须实现至少一个接口,在调用newProxyInstance方法时必须与目标类的类加载器和接口一致;invoke方法非常类似Filter中的doFilter方法,它将调用目标类的所有方法在未到达UserManagerImpl之前截获,根据我们自己的需求进行预处理后,继续调用UserManagerImpl。

为了保持invoke方法的通用性,目标方法中的参数以数组args形式传递,如果方法中有返回值,则返回,没有返回值,则返回null。如此一来,程序员不必为每个目标类设计一个代理类,所有需要打印日志的类都可以共用这个LogHandler,如果不想使用日志功能就可以直接删除LogHandler类,对原功能没有丝毫影响,如同揭去显示器上的保护膜,不会影响显示器的使用一般。

客户端调用代码如下:

[java] 
view plain
 copy

  1. package com.snail.pattern;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.   
  10.         LogHandler logHandler = new LogHandler();  
  11.           
  12.         UserManager userManager = (UserManager)logHandler.newProxyInstance(new UserManagerImpl());  
  13.           
  14.         userManager.addUser(“id”“name”);  
  15.     }  
  16.   
  17. }  

通过以上两篇博文我相信大家对AOP的原理和应用场合会有一知半解,文章中难免会出现错误,还望大家不吝赐教。

转自:http://blog.csdn.net/bjyfb/article/details/7350256

    原文作者:AOP
    原文地址: https://blog.csdn.net/u012038649/article/details/79429279
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞