Java 动态代理(JDK 动态代理)
适用场景
- 日志记录、权限校验、事务管理、性能监控等横切逻辑(AOP 思想的核心实现)。
- 需在不修改目标对象代码的前提下,对方法进行增强(符合开闭原则)。
不要改代码,就用一个简单示例就可以,把这件事情和流程说清楚就可以了
核心思想
JDK 动态代理的核心是:不手动写代理类,运行时动态生成代理对象,在不修改目标对象代码的前提下,对其方法进行增强(如日志、前置后置操作)。
核心依赖两个关键类 / 接口:
java.lang.reflect.Proxy:负责动态生成代理实例java.lang.reflect.InvocationHandler:负责定义方法增强的逻辑
核心流程(4 步走)
第一步:定义业务接口(被代理的 "规则")
接口是动态代理的基础,定义了目标对象要实现的方法,代理对象会 "伪装" 成这个接口的实现类。
java
// 业务接口:定义要被代理的方法
public interface MyInterface {
void doSomething();
}
第二步:实现接口(目标对象,真实业务逻辑)
这是真正干活的类,实现接口的方法,包含核心业务逻辑,是我们要 "增强" 的对象。
java
// 接口实现类:目标对象,真实业务逻辑的载体
public class MyInterfaceImpl implements MyInterface {
@Override
public void doSomething() {
System.out.println("Doing something..."); // 核心业务
}
}
第三步:实现 InvocationHandler(增强逻辑处理器)
这是代理的 "核心大脑",所有增强操作(方法执行前 / 后做什么)都写在这里。每次调用代理对象的方法,都会触发 invoke 方法。
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 增强处理器:实现 InvocationHandler 接口,定义增强逻辑
public class Proxyer implements InvocationHandler {
private Object target; // 存储目标对象(被代理的真实对象)
// 构造器:传入目标对象,建立关联
public Proxyer(Object target) {
this.target = target;
}
/**
* 代理逻辑触发点:调用代理对象的方法时,自动执行此方法
* @param proxy 动态生成的代理实例(一般不用)
* @param method 当前被调用的目标方法(反射对象)
* @param args 目标方法的参数(无参则为null)
* @return 目标方法的返回值
* @throws Throwable 目标方法可能抛出的异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 方法执行前的增强操作
System.out.println("调用方法前的操作");
// 2. 调用目标对象的真实方法(核心业务逻辑执行)
Object result = method.invoke(target, args); // 反射调用真实方法
// 3. 方法执行后的增强操作
System.out.println("调用方法后的操作");
return result; // 返回目标方法的结果(如果有返回值的话)
}
}
第四步:生成代理对象 + 测试
通过 Proxy.newProxyInstance() 动态生成代理对象,之后调用代理对象的方法,就会自动触发增强逻辑 + 真实业务逻辑。
java
import java.lang.reflect.Proxy;
// 测试类:验证代理效果
public class Test {
public static void main(String[] args) {
// 1. 创建目标对象(真实干活的对象)
MyInterface target = new MyInterfaceImpl();
// 2. 创建增强处理器,传入目标对象(建立代理和目标的关联)
Proxyer handler = new Proxyer(target);
// 3. 动态生成代理对象(核心步骤)
MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(), // 类加载器(用接口的类加载器)
new Class[]{MyInterface.class}, // 代理要实现的接口(和目标对象一致)
handler // 增强逻辑处理器
);
// 4. 调用代理对象的方法(触发增强逻辑 + 真实业务)
proxyInstance.doSomething();
}
}
运行结果
plaintext
erlang
调用方法前的操作
Doing something...
调用方法后的操作
关键流程拆解(一句话看懂)
- 我们调用
proxyInstance.doSomething()(代理对象的方法) - JDK 动态代理会自动把这个调用转发给
Proxyer的invoke方法 - 在
invoke里,先执行 "前置增强操作" - 再通过
method.invoke(target, args)调用目标对象(MyInterfaceImpl)的真实doSomething()方法 - 最后执行 "后置增强操作"
- 返回结果给调用者
核心注意点
- 必须基于接口:JDK 动态代理只能代理 "实现了接口的类",不能直接代理没有接口的类
- 增强逻辑集中:所有接口方法的增强逻辑都写在
invoke里,一次编写,所有方法复用 - 动态生成:代理对象是运行时创建的,编译期看不到这个类的
.class文件 invoke方法会拦截代理对象的 所有接口方法 (包括Object类的方法如toString(),需注意过滤)。- 目标对象的方法若为
final/static,则无法被代理(因为无法通过反射重写)。