java动态代理机制

9. java动态代理机制

动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。

本文主要介绍Java中两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理。

9.1 代理模式

给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。 可以分为静态代理、动态代理。

9.2 静态代理

适合已经确定了被代理的服务
例子:代理服务增加日志处理功能

  • 需要代理的服务

    //接口
      public interface UserService {
      	public void select();   
      	public void update();
      } 
      //实现类
      public class UserServiceImpl implements UserService {  
      	public void select() {  
      		System.out.println("查询 selectById");
      	}
      	public void update() {
      		System.out.println("更新 update");
      	}
      }
    
  • 我们将通过静态代理对 UserServiceImpl 进行功能增强,在调用 select 和 update 之前记录一些日志。写一个代理类 UserServiceProxy,代理类需要实现 UserService

    public class UserServiceProxy implements UserService {
      private UserService target; // 被代理的对象
    
      public UserServiceProxy(UserService target) {
          this.target = target;
      }
      public void select() {
          before();
          target.select();    // 这里才实际调用真实主题角色的方法
          after();
      }
      public void update() {
          before();
          target.update();    // 这里才实际调用真实主题角色的方法
          after();
      }
    
      private void before() {     // 在执行方法之前执行
          System.out.println(String.format("log start time [%s] ", new Date()));
      }
      private void after() {      // 在执行方法之后执行
          System.out.println(String.format("log end time [%s] ", new Date()));
      }
    }
    
  • 客户端测试

    public class Client1 {
      public static void main(String[] args) {
          UserService userServiceImpl = new UserServiceImpl();
          UserService proxy = new UserServiceProxy(userServiceImpl);
    
          proxy.select();
          proxy.update();
      }
    }
    /**
    输出
    log start time [Thu Dec 20 14:13:25 CST 2018] 
    查询 selectById
    log end time [Thu Dec 20 14:13:25 CST 2018] 
    log start time [Thu Dec 20 14:13:25 CST 2018] 
    更新 update
    log end time [Thu Dec 20 14:13:25 CST 2018] 
    */
    

9.3 动态代理(JDK自带的方式)

使用动态代理用到了java反射机制,原理就是在代理类中的属性由确定的被代理对象换成Object

动态代理适用于:需要代理多个对象,并且增强的功能一致,比如都要增加日志处理

JDK动态代理主要涉及两个类:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler,我们仍然通过案例来学习

  • 需要代理的服务

    //接口
      public interface UserService {
      	public void select();   
      	public void update();
      } 
      //实现类
      public class UserServiceImpl implements UserService {  
      	public void select() {  
      		System.out.println("查询 selectById");
      	}
      	public void update() {
      		System.out.println("更新 update");
      	}
      }
    
  • 创建动态代理实例,需要实现接口InvocationHandler

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Date;
    
    public class LogHandler implements InvocationHandler {
        Object target;  // 被代理的对象,实际的方法执行者
    
        public LogHandler(Object target) {
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object result = method.invoke(target, args);  // 调用 target 的 method 方法
            after();
            return result;  // 返回方法的执行结果
        }
        // 调用invoke方法之前执行
        private void before() {
            System.out.println(String.format("log start time [%s] ", new Date()));
        }
        // 调用invoke方法之后执行
        private void after() {
            System.out.println(String.format("log end time [%s] ", new Date()));
        }
    }
    
    
  • 客户端实例测试

    import proxy.UserService;
    import proxy.UserServiceImpl;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class Client2 {
        public static void main(String[] args) throws IllegalAccessException, InstantiationException {
            // 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名
            // System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
            // 1. 创建被代理的对象,UserService接口的实现类
            UserServiceImpl userServiceImpl = new UserServiceImpl();
            // 2. 获取对应的 ClassLoader
            ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
            // 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService,
            Class[] interfaces = userServiceImpl.getClass().getInterfaces();
            // 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
            //     这里创建的是一个自定义的日志处理器,须传入实际的执行对象 userServiceImpl
            InvocationHandler logHandler = new LogHandler(userServiceImpl);
            /*
               5.根据上面提供的信息,创建代理对象 在这个过程中,
                   a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
                   b.然后根据相应的字节码转换成对应的class,
                   c.然后调用newInstance()创建代理实例
             */
            UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);
            // 调用代理的方法
            proxy.select();
            proxy.update();
    
            // 保存JDK动态代理生成的代理类,类名保存为 UserServiceProxy
            // ProxyUtils.generateClassFile(userServiceImpl.getClass(), "UserServiceProxy");
        }
    }
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值