Java 动态代理详解:从原理到实战
1. 什么是代理?
在软件开发中,代理模式(Proxy Pattern) 是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。
简单来说:代理就是帮别人做事的人 。
比如,你想买演唱会门票,可以自己去买(真实对象),也可以找黄牛帮你买(代理对象)。
在 Java 中,代理有两种:
- 静态代理:代理类在编译期就已经生成。
- 动态代理:代理类在运行时动态生成。
今天我们重点讲 动态代理。
2. 为什么需要动态代理?
假设我们有一个接口 UserService
,有多个方法,比如:
java
public interface UserService {
void createUser(String name);
void deleteUser(String name);
}
如果我们想在每个方法执行前后加上 日志、权限验证 等功能,
静态代理需要手动写一个代理类,方法多了就很麻烦。
而动态代理可以让我们一次性编写通用逻辑,在运行时帮我们生成代理类,省时省力。
3. Java 动态代理的原理
Java 动态代理依赖 java.lang.reflect.Proxy
类和 InvocationHandler
接口。
核心思想:
- 接口:被代理的对象必须实现接口(JDK 动态代理的限制)。
- InvocationHandler:定义代理逻辑(方法调用前后做什么)。
- Proxy.newProxyInstance:运行时生成代理对象。
执行流程:
rust
方法调用 -> 代理对象 -> InvocationHandler.invoke() -> 真实对象
4. 动态代理示例
4.1 接口与实现类
java
public interface UserService {
void createUser(String name);
void deleteUser(String name);
}
public class UserServiceImpl implements UserService {
@Override
public void createUser(String name) {
System.out.println("创建用户:" + name);
}
@Override
public void deleteUser(String name) {
System.out.println("删除用户:" + name);
}
}
4.2 动态代理处理器
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LoggingInvocationHandler implements InvocationHandler {
private final Object target;
public LoggingInvocationHandler(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.3 创建代理对象
java
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LoggingInvocationHandler(target)
);
proxy.createUser("Alice");
proxy.deleteUser("Bob");
}
}
运行结果:
css
[日志] 方法开始:createUser
创建用户:Alice
[日志] 方法结束:createUser
[日志] 方法开始:deleteUser
删除用户:Bob
[日志] 方法结束:deleteUser
classDiagram
class UserService {
<>
+createUser(String name)
+deleteUser(String name)
}
class UserServiceImpl {
+createUser(String name)
+deleteUser(String name)
}
class LoggingInvocationHandler {
-Object target
+invoke(Object proxy, Method method, Object[] args) Object
}
class Proxy {
+newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) Object
}
class InvocationHandler {
<>
+invoke(Object proxy, Method method, Object[] args) Object
}
UserService <|.. UserServiceImpl
InvocationHandler <|.. LoggingInvocationHandler
Proxy --> InvocationHandler
Proxy --> UserService
LoggingInvocationHandler --> UserService
5. 动态代理的优缺点
优点:
- 通用性强:一套代理逻辑可以应用到多个类。
- 解耦:业务代码与横切逻辑(如日志、事务)分离。
- 运行时生成:无需手动编写代理类。
缺点:
- 只能代理接口(JDK 动态代理的限制)。
- 反射调用有一定性能损耗(一般可忽略)。
6. 扩展:CGLIB 动态代理
如果目标类没有实现接口 ,JDK 动态代理就无能为力,这时可以用 CGLIB (基于继承生成子类代理)。
Spring AOP 就是根据情况选择 JDK 动态代理或 CGLIB。
7. 总结
Java 动态代理本质上是 在运行时生成代理类,通过反射调用目标对象的方法 ,并在调用前后插入额外逻辑。
它是实现 AOP(面向切面编程) 的重要基础,也是 Spring 框架的核心技术之一。
💡 建议:如果你在项目中需要对多个类的方法统一添加日志、权限、事务等功能,可以优先考虑动态代理,而不是在每个方法里手动写重复代码。