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.Proxy
和 java.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"); } }