Java 动态代理(Dynamic Proxy)

一、什么是动态代理

Java 动态代理是一种在运行时创建代理类并处理方法调用的机制,它不需要事先写好代理类,而是通过反射 API 动态生成代理对象。

它常用于以下场景:

  • 日志记录
  • 权限控制
  • 性能监控
  • Spring AOP 实现原理

二、JDK 动态代理原理

JDK 动态代理主要依赖两个核心类/接口:

  • java.lang.reflect.Proxy
  • java.lang.reflect.InvocationHandler

JDK 动态代理要求被代理的类 必须实现接口


三、使用步骤

1. 定义接口

1
2
3
4
java复制编辑public interface UserService {
void login(String username);
void logout();
}

2. 实现该接口的类(目标类)

1
2
3
4
5
6
7
8
9
10
11
java复制编辑public class UserServiceImpl implements UserService {
@Override
public void login(String username) {
System.out.println(username + " 登录了系统");
}

@Override
public void logout() {
System.out.println("用户退出系统");
}
}

3. 实现 InvocationHandler 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java复制编辑import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LogHandler implements InvocationHandler {
private final Object target;

public LogHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[日志] 调用方法:" + method.getName());
Object result = method.invoke(target, args); // 反射调用目标方法
System.out.println("[日志] 方法调用结束:" + method.getName());
return result;
}
}

4. 生成代理对象并调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制编辑import java.lang.reflect.Proxy;

public class Main {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
InvocationHandler handler = new LogHandler(target);

UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), // 必须是接口类型
handler
);

proxy.login("张三");
proxy.logout();
}
}

四、运行结果

1
2
3
4
5
6
css复制编辑[日志] 调用方法:login
张三 登录了系统
[日志] 方法调用结束:login
[日志] 调用方法:logout
用户退出系统
[日志] 方法调用结束:logout

五、JDK 动态代理的限制

优点 缺点
灵活,无需编写代理类 只能代理接口,不能代理类
支持运行时增强行为 运行时使用反射,性能略下降
应用于 AOP、RPC、事务等场景

六、CGLIB 动态代理(补充)

如果目标类没有接口,可以使用 CGLIB,它基于继承生成代理类。Spring 在默认配置下也使用它来代理非接口类。

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制编辑Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("[CGLIB日志] 调用方法:" + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("[CGLIB日志] 方法调用结束:" + method.getName());
return result;
}
});
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
proxy.login("李四");

CGLIB 需要额外依赖库(如 cglib-nodep


七、动态代理的应用场景总结

  • AOP(面向切面编程)
  • 日志拦截器
  • 安全控制
  • 缓存管理
  • RPC 调用封装
  • 数据库事务处理