# 代理模式:隐藏在框架幕后的核心模式

# 一、模式本质与核心价值

代理模式(Proxy Pattern)是一种结构型设计模式,通过创建代理对象控制对原始对象的访问。在 Java 生态中,代理模式是实现以下能力的基石:

  • 访问控制:权限校验、熔断限流
  • 功能增强:日志记录、性能监控
  • 延迟加载:大文件预加载、数据库连接池
  • 远程调用:RPC 框架通信基础

# 二、Java 实现方式对比

# 2.1 静态代理(显式代理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 用户服务接口
interface UserService {
void saveUser(String user);
}

// 真实实现类
class UserServiceImpl implements UserService {
public void saveUser(String user) {
System.out.println("保存用户:" + user);
}
}

// 静态代理类
class UserServiceProxy implements UserService {
private UserService target;

public UserServiceProxy(UserService target) {
this.target = target;
}

public void saveUser(String user) {
long start = System.currentTimeMillis();
target.saveUser(user); // 委托调用
System.out.println("方法耗时:" + (System.currentTimeMillis()-start));
}
}

// 使用示例
UserService service = new UserServiceProxy(new UserServiceImpl());
service.saveUser("张三");

特点

  • 手动编写代理类
  • 接口方法变化时代码需要同步修改
  • 适合小型固定场景

# 2.2 JDK 动态代理(接口代理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class LogInvocationHandler implements InvocationHandler {
private final Object target;

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

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}

// 创建代理对象
UserService proxyInstance = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new LogInvocationHandler(new UserServiceImpl())
);

特点

  • 基于接口实现
  • 自动生成代理类
  • 反射调用带来性能损耗

# 2.3 CGLIB 动态代理(子类代理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CglibProxyInterceptor implements MethodInterceptor {
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;
}
}

// 生成代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new CglibProxyInterceptor());
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();

特点

  • 通过生成子类实现
  • 不需要接口
  • 需要添加 CGLIB 依赖
  • final 类 / 方法无法代理

# 三、Spring 生态中的代理应用场景

# 3.1 Spring AOP 实现原理

Spring 框架通过代理模式实现 AOP,两种代理选择策略:

条件使用代理类型
目标实现接口JDK 动态代理
目标未实现接口CGLIB 代理
强制使用 CGLIB配置 @EnableAspectJAutoProxy (proxyTargetClass=true)

事务管理的典型实现:

1
2
3
4
5
@Transactional
public void transferMoney() {
// 数据库操作
}
// Spring通过代理添加事务边界控制

# 3.2 Spring Boot 自动配置

在 Spring Boot 启动过程中,Configuration 类通过代理实现:

1
2
3
4
5
6
7
8
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
// 数据源配置
}
}
// 配置类被CGLIB增强,保证@Bean方法单例

# 3.3 Feign 声明式 HTTP 客户端

OpenFeign 通过动态代理生成 HTTP 客户端:

1
2
3
4
5
6
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable Long id);
}
// 运行时生成代理处理HTTP请求

# 3.4 MyBatis Mapper 接口实现

MyBatis 通过 JDK 动态代理将接口转换为 SQL 执行:

1
2
3
4
5
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUser(int id);
}
// SqlSession.getMapper()返回代理对象

# 四、代理模式高级应用场景

# 4.1 分布式追踪(APM 工具)

1
2
3
4
5
6
7
8
9
10
11
// 伪代码示例:链路追踪代理
public class TraceProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
Span span = Tracer.startSpan(method.getName());
try {
return method.invoke(target, args);
} finally {
span.finish();
}
}
}

# 4.2 微服务熔断器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 伪代码示例:Hystrix熔断代理
class CircuitBreakerProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
if (circuit.isOpen()) {
return fallback();
}
try {
return method.invoke(target, args);
} catch (Exception e) {
circuit.recordFailure();
throw e;
}
}
}

# 五、性能优化建议

  1. 代理对象缓存:对相同目标重复使用代理实例
  2. 方法过滤:仅代理需要增强的方法
  3. 选择合适代理类型:
    • 高频率调用方法避免反射
    • 优先选择 CGLIB(Spring Boot 2.x 默认)
  4. 减少代理层级:避免多重代理嵌套

# 六、代理模式局限性

  1. 调试复杂性:调用栈深度增加
  2. 性能损耗:动态代理比直接调用慢 3-5 倍(实测数据)
  3. 对象身份变化instanceofequals() 等方法需要特殊处理
  4. 循环依赖:在 Spring 中需要特殊处理代理对象的依赖注入

# 七、一些特殊处理:

在代理模式中,由于代理对象和真实对象是两个不同的 Java 对象实例,它们在以下两种场景中会出现特殊行为,需要特别注意处理

# 一、 instanceof 判断失效

问题本质
代理对象(Proxy)和真实对象(Target)属于不同的类层次结构。

示例场景

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface UserService {
void save();
}

public class UserServiceImpl implements UserService {
public void save() { /* 实现 */ }
}

// JDK动态代理生成的对象
UserService proxy = (UserService) Proxy.newProxyInstance(...);

// 判断结果:false
boolean isInstance = proxy instanceof UserServiceImpl;

原因分析

  • JDK 动态代理生成的是 Proxy 子类(如 $Proxy0
  • CGLIB 生成的是真实对象的子类(如 UserServiceImpl$$EnhancerByCGLIB$$123456
  • 两者都与原始类存在类型差异

解决方案

1
2
3
4
5
6
7
8
// Spring框架中的处理方式(AopUtils)
if(AopUtils.isAopProxy(proxy)) {
// 获取真实对象
Object target = AopUtils.getTargetObject(proxy);
if(target instanceof UserServiceImpl) {
// 正确逻辑
}
}


# 二、 equals() 方法陷阱

问题本质
默认的 equals() 方法基于对象内存地址比较。

* 示例场景

1
2
3
4
5
UserService real = new UserServiceImpl();
UserService proxy = createProxy(real);

// 判断结果:false(预期可能是true)
boolean equals = proxy.equals(real);

危险后果

  • 将代理对象放入 HashSet/HashMap 时可能出现重复
  • 使用对象相等判断的逻辑可能失效

解决方案

  1. 重写 equals 方法(在真实对象中)

1
2
3
4
5
6
7
8
@Override
public boolean equals(Object obj) {
if (obj instanceof UserServiceImpl) {
// 根据业务属性比较(如ID)
return this.id == ((UserServiceImpl)obj).id;
}
return false;
}

  1. 代理对象特殊处理(Spring 的 AOP 代理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Spring的SingletonTargetSource实现
public class MyProxy extends Proxy implements Advised {
public Object getTarget() {
return targetSource.getTarget();
}

@Override
public boolean equals(Object other) {
if (other == this) return true;
if (other instanceof MyProxy) {
return this.getTarget().equals(((MyProxy)other).getTarget());
}
return this.getTarget().equals(other);
}
}


# 三、框架中的特殊处理(以 Spring 为例)

# 1. @Autowired 注入处理

当注入被代理对象时,Spring 会自动注入代理对象:

1
2
3
4
5
6
7
8
@Autowired 
private UserService userService; // 实际是代理对象

// 正确比较方式:
if(AopProxyUtils.getSingletonTarget(userService) != null) {
// 获取真实对象
Object target = AopProxyUtils.getSingletonTarget(userService);
}

# 2. 事务传播中的自调用问题

1
2
3
4
5
6
7
8
9
10
11
12
public class UserService {
@Transactional
public void methodA() {
this.methodB(); // 直接调用不走代理,事务失效
}

@Transactional
public void methodB() { /* ... */ }
}

// 正确方式:通过代理对象调用
((UserService) AopContext.currentProxy()).methodB();


# 四、最佳实践总结

  1. 避免直接类型判断
    使用 AopUtils.isAopProxy() 替代 instanceof

  2. 慎用对象级 equals()
    始终基于业务属性实现相等性判断

  3. 注意集合操作
    代理对象存入集合前需做归一化处理:

    1
    2
    Set<UserService> set = new HashSet<>();
    set.add(AopUtils.getTargetObject(proxy));

  4. 框架特性利用
    Spring 提供的工具类:

    1
    2
    AopProxyUtils.getSingletonTarget(proxy) // 获取真实对象
    AopContext.currentProxy() // 获取当前代理对象