动态代理实现日志的写入

来源:互联网 发布:win10引导安装ubuntu 编辑:程序博客网 时间:2024/05/16 17:37
之前在学习设计模式的时候就学习过代理模式,在DRP的学习过程中,又一次遇到了代理模式,但是这次接触到的是动态代理。做项目的时候也听同学们提到过AOP,那么动态代理和AOP是一种什么样的关系呢?

    

   一、代理定义

                 

                                   图1  代理模式类图

                                        

    代理模式:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

    代理模式能够协调调用者和被调用这,在一定的程度上降低了系统的耦合度,但是由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。


   二、动态代理


    动态代理是一种较为高级的代理模式,它的典型应用就是Spring AOP.

    在传统的代理模式中,客户端通过Proxy调用RealSubject类的request()方法,同时还在代理类中封装了其他方法(如preRequest()和postRequest()),可以处理一些其他问题,如果按照这种方式使用代理模式,那么真实主题角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实主题角色必须对应一个代理主题角色,这将导致系统中类的个数急剧增加,因此需要想办法减少系统中类的个数,在实现不知道真实主题角色的情况下使用代理主体角色,这就是动态代理需要解决的问题。


     三、实例代码

    

    1.主题接口

    

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public interface UserManager {  
  2.   
  3.     public void addUser(String userId, String userName);  
  4.       
  5.     public void delUser(String userId);  
  6.       
  7.     public void modifyUser(String userId, String userName);  
  8.       
  9.     public String findUser(String userId);  
  10. }  

    2.真实主题

    

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class UserManagerImpl implements UserManager {  
  2.   
  3.     public void addUser(String userId, String userName) {  
  4.         try {  
  5.             System.out.println("UserManagerImpl.addUser() userId-->>" + userId);  
  6.               
  7.         }catch(Exception e) {  
  8.             e.printStackTrace();  
  9.             throw new RuntimeException();  
  10.         }     
  11.     }  
  12.   
  13.     public void delUser(String userId) {  
  14.         System.out.println("UserManagerImpl.delUser() userId-->>" + userId);  
  15.     }  
  16.   
  17.     public String findUser(String userId) {  
  18.         System.out.println("UserManagerImpl.findUser() userId-->>" + userId);  
  19.         return "张三";  
  20.     }  
  21.   
  22.     public void modifyUser(String userId, String userName) {  
  23.         System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId);  
  24.     }  
  25. }  

    3.动态代理

 

    Java动态代理实现相关类位于java.lang.reflect包,主要涉及两个对象,InvocationHandler接口和Proxy类。

          

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;  
  4.   
  5. public class LogHandler implements InvocationHandler {  
  6.       
  7.     private Object targetObject;  
  8.     //Proxy为动态代理类  
  9.     public Object newProxyInstance(Object targetObject) {  
  10.         this.targetObject = targetObject;  
  11.         return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),  
  12.                                targetObject.getClass().getInterfaces(), this);  
  13.     }  
  14.     /* 
  15.      * @proxy:表示代理类 
  16.      * @method:表示需要代理的方法 
  17.      * @args:表示代理方法的参数数组 
  18.      */  
  19.     public Object invoke(Object proxy, Method method, Object[] args)  
  20.             throws Throwable {  
  21.         System.out.println("start-->>" + method.getName());  
  22.         for (int i=0; i<args.length; i++) {  
  23.             System.out.println(args[i]);  
  24.         }  
  25.         Object ret = null;  
  26.         try {  
  27.             //调用目标方法  
  28.             ret = method.invoke(targetObject, args);  
  29.             System.out.println("success-->>" + method.getName());   
  30.         }catch(Exception e) {  
  31.             e.printStackTrace();  
  32.             System.out.println("error-->>" + method.getName());  
  33.             throw e;  
  34.         }  
  35.         return ret;  
  36.     }  
  37. }  

    在newProxyInstance方法中,目标主题作为参数,我们通过targetObject.getClass().getClassLoader()获取ClassLoader对象,然后通过targetObject.getClass().getInterfaces()获取它实现的所有接口,这样就在内存中建立了一个动态代理对象。但是值得注意的一点是,这个被建立的动态代理类里面只有目标主题的方法,而没有其实现,当客户端调用的动态代理类的方法时,需要调用一个实现了InvocationHandler接口的类,这样才能代用InvocationHandler中的invoke()回调方法,所以要把LogHandler自身作为参数传入。


    4.客户端调用

    

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.         LogHandler logHandler = new LogHandler();  
  5.         UserManager userManager = (UserManager)logHandler.newProxyInstance(new UserManagerImpl());  
  6.   
  7.         String name = userManager.findUser("0001");  
  8.         System.out.println("Client.main() --- " + name);  
  9.     }  
  10. }  

    在这里,userManager作为代理对象,调用userManager.findUser()就会调用InvocationHandler中的invoke方法,所以在invoke()方法中处理相应的日志操作即可。


    总结:

    代理模式的进一步深入学习,越发感觉到面向对象编程的魅力。invoke()可以联想到在学习js的回调函数,所以接收起来并不太陌生。动态代理模式解决了静态代理模式要求创建多个代理类的缺陷,把编译时创建的实例延迟到运行时。AOP将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。无疑,代理模式为AOP的实现提供了良好的解决思路。

0 0