一、动态代理
- 动态代理是指在运行时,动态生成代理类。即,代理类的字节码将在运行时生成并载入当前的 ClassLoader
- 动态代理的主要特点就是能够在程序运行时 JVM 才为被代理对象生成代理对象
- 动态代理和静态代理角色一样
- 动态代理分为两大类:基于接口的 JDK 动态代理和基于类的 CGLIB 动态代理
- 通常说的动态代理指的是 JDK 动态代理
与静态代理类想比,动态类有诸多好处。首先,不需要为真是主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也是非常烦人的事,如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;其次,使用一些动态代理的生成方法甚至可以在运行时指定代理类的执行逻辑,从而大大提升系统的灵活性
二、实现 JDK 动态代理四步骤
- 创建被代理的接口和类
- 创建 InvocationHandler 接口的实现类,在 invoke 方法中实现代理逻辑
- 通过 Proxy 的静态方法 newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理对象
- 使用代理对象
三、实战演练
/**
* 抽象角色
*/
interface UserService {
void add();
void delete();
void update();
void select();
}
/**
* 真实角色
*/
class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("添加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void select() {
System.out.println("查询了一个用户");
}
}
class DynamicProxyInvocationHandler implements InvocationHandler {
private Object target; // 被代理的接口 UserService
public void setTarget(Object target) {
this.target = target;
}
/**
* 动态生成得到代理类
*/
public Object getProxy() {
/**
* newProxyInstance 方法参数列表解释
* 1、ClassLoader loader 本地的类加载器
* 只要涉及实例化对象,就一定要加载类的字节码,而加载字节码一定要用到类加载器
* 2、Class<?>[] interfaces 代理类要实现的接口列表
* 3、InvocationHandler 用来处理方法的调用
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 处理代理实例,并返回结果
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
return method.invoke(target, args); // 动态代理的本质,就是使用反射机制实现
}
/**
* 扩展功能
*/
public void log(String msg) {
System.out.println("【Debug --> 】执行了" + msg + "方法");
}
}
public class Client {
public static void main(String[] args) {
DynamicProxyInvocationHandler pih = new DynamicProxyInvocationHandler();
pih.setTarget(new UserServiceImpl()); // 设置要代理的对象 --> 真实角色
UserService proxy = (UserService) pih.getProxy(); // 动态生成代理对象
proxy.delete(); // 调用业务方法,并打印日志
System.out.println(proxy.getClass()); // 打印生成的代理对象的字节码类型,class xx.xx.xx.$Proxy0 匿名内部类
}
}
四、JDK 动态代理总结
- 优点
- 具有静态代理的好处
- 一个动态代理类代理的是一个接口,一般对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口
- 缺点
虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾