代理
2025/12/3大约 4 分钟
什么是代理?
代理是一种设计模式,通过引入代理对象来控制对真实对象的访问。代理对象持有真实对象的引用,在调用真实对象方法前后可执行额外逻辑(权限检查、日志记录、事务管理等),实现"控制访问"和"增强功能"。
代理的核心作用
- 控制访问:在真实对象方法调用前后添加额外逻辑
- 功能增强:无需修改原始代码即可添加新功能
- 降低耦合:调用方只与代理对象交互,不关心真实对象的具体实现
Java中的代理方式
- 静态代理:代码简洁,但灵活性差,重复代码多
- JDK动态代理:基于接口,Spring默认选择,无需手动实现
- CGLIB代理:基于继承,代理final类/方法失效,性能相对较差
1. 静态代理
编译时确定代理类,代理类和被代理类实现同一接口。
特点
- 代理类在编译前就存在,需要手动编写
- 代理类和被代理类一一对应
- 代码重复度高,接口变更时代理类需要同步修改
代码示例
// 1. 定义接口
public interface UserService {
void saveUser();
void deleteUser();
}
// 2. 真实对象
public class UserServiceImpl implements UserService {
@Override
public void saveUser() {
System.out.println("保存用户到数据库");
}
@Override
public void deleteUser() {
System.out.println("从数据库删除用户");
}
}
// 3. 静态代理
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void saveUser() {
System.out.println("代理前:权限检查");
userService.saveUser();
System.out.println("代理后:记录日志");
}
@Override
public void deleteUser() {
System.out.println("代理前:权限检查");
userService.deleteUser();
System.out.println("代理后:记录日志");
}
}
// 4. 使用
public class Main {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserService proxy = new UserServiceProxy(realService);
proxy.saveUser();
}
}2. 动态代理
运行时动态生成代理类,解决静态代理的代码重复问题。
2.1 JDK动态代理
基于接口的代理,代理类实现目标接口,通过Proxy.newProxyInstance()动态生成。
特点
- 必须有接口,目标类必须实现至少一个接口
- 通过
java.lang.reflect.Proxy实现 - 只能代理接口中的方法
代码示例
// 1. 定义接口和实现类(同静态代理)
public interface UserService {
void saveUser();
void deleteUser();
}
public class UserServiceImpl implements UserService {
@Override
public void saveUser() {
System.out.println("保存用户到数据库");
}
}
// 2. 动态代理处理器
public class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前:开始时间 = " + System.currentTimeMillis());
// 调用目标方法
Object result = method.invoke(target, args);
System.out.println("代理后:结束时间 = " + System.currentTimeMillis());
return result;
}
}
// 3. 创建动态代理
public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new LogInvocationHandler(userService)
);
proxy.saveUser();
}
}2.2 CGLIB动态代理
基于继承的代理,代理类是目标类的子类,通过继承重写目标方法实现代理。
特点
- 不需要接口,目标类可以是普通类
- 通过继承实现,重写父类方法
- 无法代理final类和方法(因为final类不能被继承,final方法不能被重写)
代码示例
// 1. 目标类(不需要实现接口)
public class UserService {
public void saveUser() {
System.out.println("保存用户到数据库");
}
public void deleteUser() {
System.out.println("从数据库删除用户");
}
}
// 2. CGLIB代理处理器
public class CglibLogInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("CGLIB代理前:权限检查");
// 调用目标方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB代理后:记录日志");
return result;
}
}
// 3. 创建CGLIB代理
public class Main {
public static void main(String[] args) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new CglibLogInterceptor());
// 创建代理对象
UserService proxy = (UserService) enhancer.create();
proxy.saveUser();
}
}代理方式对比
| 特性 | 静态代理 | JDK动态代理 | CGLIB代理 |
|---|---|---|---|
| 实现方式 | 实现接口 | 实现接口 | 继承目标类 |
| 是否需要接口 | ✅ 必须 | ✅ 必须 | ❌ 不需要 |
| 代理对象数量 | 1对1 | 1对多 | 1对多 |
| 生成时机 | 编译时 | 运行时 | 运行时 |
| 性能 | 最好 | 较好 | 一般 |
| 适用场景 | 简单场景 | 接口少的场景 | 无接口场景 |
Spring中的代理使用
1. AOP (面向切面编程)
Spring AOP底层使用代理机制:
- JDK动态代理:目标类实现接口时使用
- CGLIB代理:目标类无接口时使用
2. Spring Boot自动配置
@Component
public class MyService {
@Transactional // Spring通过代理添加事务控制
public void saveData() {
// 业务逻辑
}
}