动态代理是代理模式的一种,代理模式指通过代理静默地解决一些业务无关的问题,比如远程、安全、事务、日志、资源关闭等,让开发者可以只关心业务。分为静态、动态代理:
静态代理:事先写好代理类
-
实现简单,且不侵入原码
-
缺点是每个业务类都需要写一个,非常不灵活,而且有冗余问题
// 静态代理,给 UserService 添加日志处理 public class UserServiceProxy implements UserService { // 被代理对象 private UserService target; // 注入被代理对象 public UserServiceProxy(UserService target) { this.target = target; } @Override public void select() { before(); target.select(); after(); } // ...... }
为解决这个问题,有了动态代理:
- 优点是运行时自动生成代理对象
类能动态生成,依赖 JVM 的类加载机制,
加载阶段能通过运行时计算,生成代理类字节码加载到 JVM 中。
JVM 类加载过程分为:加载、验证、准备、解析、初始化。
其中加载又分为:
- 通过类全限定名获取类二进制字节流,途径可以是运行时计算生成,这是动态代理的基础
- 将字节流的静态存储转换为方法区的运行时数据结构
- 生成类的 Class 对象
动态代理就是根据接口或目标对象,计算代理类的字节码,再加载到 JVM 中使用。
- 缺点是生成代理对象、调用代理方法要额外花时间。
实现动态代理,有两种方式:
- JDK 动态代理
- 基于反射实现,依赖小,实现简单,只要实现 InvocationHandler 接口,重写 invoke() 即可
- 缺点是只能代理接口,因为代理类本身继承了 Proxy 类。
// 调用处理器:给被代理类提供日志处理
public class MyLogHandler implements InvocationHandler {
// 定义被代理对象
private Object target;
// 注入被代理对象
public MyLogHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
// 调用 target 的 method 方法
Object result = method.invoke(target,args);
after();
return result;
}
// ......
}
public class Client {
public static void main(String[] args) {
// 被代理对象
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 获取 ClassLoader
ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
// 获取所有接口的Class
Class[] interfaces = userServiceImpl.getClass().getInterfaces();
// 引入调用处理器
MyLogHandler myLogHandler = new MyLogHandler(userServiceImpl);
// 动态生成代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader,interfaces,myLogHandler);
// 调用方法
proxy.select();
}
}
- cglib 动态代理:
- 创建业务类的子类作为代理类,不用实现接口,也不侵入源码,性能更高;
- 但实现相对复杂,要实现 MethodInterceptor 接口,重写 intercept() ,通过 Enhancer 类的回调方法实现。
动态代理应用广泛,
常见比如:Spring AOP,Bean 有实现接口用 JDK 动态代理,没有则用 cglib 动态代理。