好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受.
代理模式是java中很常见的设计模式,如下将好好讲解下java代理模式相关的知识。本文还是将从:它的前世今生;使用场景;注意事项;代码样例等四个方面,详细阐述下代理模式的细节内容。
1. 前世今生:代理模式的演进史
阶段 | 年代 | 关键词 | 里程碑事件 |
---|---|---|---|
原始形态 | 1995 以前 | 手写包装类 | C/C++ 时代,通过 函数指针 + 结构体 实现“壳”与“核”分离。 |
Java 静态代理 | 1996-2003 | interface + 组合 | Java 1.1 引入反射,RMI 用静态代理做远程调用。 |
JDK 动态代理 | 2004 | java.lang.reflect.Proxy | JDK 1.3 正式加入,Spring 1.x 用其构建 AOP。 |
CGLIB/字节码时代 | 2005-2015 | ASM、Javassist | Hibernate、Spring 3.x 引入 CGLIB,解决“无接口”代理痛点。 |
新时代 | 2016-至今 | ByteBuddy、LambdaMetafactory | Java 14 Proxy.newProxyInstance 性能提升 10×;Spring 6 原生镜像继续演进。 |
一句话总结:从“人肉手写”到“编译期织入”,再到“运行时字节码”,代理模式始终围绕 “在不修改原对象的前提下增加行为” 这一核心目标进化。
2. 使用场景:什么时候该用代理
场景 | 举例 | 代理类型 |
---|---|---|
延迟加载(Lazy Loading) | MyBatis 的 Mapper 代理 | 动态代理 |
权限控制 | Spring Security 的 @PreAuthorize | AOP 代理 |
缓存 | Guava Cache、Spring Cache | 动态代理 + 拦截器 |
远程调用(RPC) | Dubbo、Feign | 动态代理 |
日志 / 监控 | Micrometer、SLF4J 桥接 | 动态代理 |
事务管理 | Spring @Transactional | AOP 代理 |
保护代理 | 只读视图(Collections.unmodifiableList) | 静态代理 |
3. 注意事项:避坑指南
-
接口 vs 类
-
JDK 动态代理只能代理 接口,否则抛
IllegalArgumentException
。 -
需要代理类请用 CGLIB、ByteBuddy。
-
-
性能
-
反射 + 代理调用 ≈ 直接调用的 10~20 倍开销(JDK8 后优化到 2~3 倍)。
-
高频调用场景(如 Netty pipeline)建议用
MethodHandle
或编译期织入。
-
-
循环代理
-
Spring Bean 之间的循环依赖 + AOP 代理会导致 提前暴露半成品代理,需开启
allowRawInjectionDespiteWrapping=true
。
-
-
final / private 方法
-
CGLIB 无法代理
final
和private
方法;JDK 代理压根不会拦截这些方法。
-
-
内存泄漏
-
每次
Proxy.newProxyInstance
都会生成新类,滥用会导致 Metaspace OOM。 -
推荐缓存
Proxy
实例或使用WeakCache
策略。
-
-
调试困难
-
动态代理类名形如
$Proxy0
,IDE 断点时可加-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
导出.class
文件。
-
4. 代码样例:从静态到动态,从 JDK 到 CGLIB
4.1 静态代理
// 1. 业务接口
public interface OrderService {
void createOrder(String orderNo);
}
// 2. 真实实现
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder(String orderNo) {
System.out.println("创建订单:" + orderNo);
}
}
// 3. 静态代理
public class OrderServiceStaticProxy implements OrderService {
private final OrderService target;
public OrderServiceStaticProxy(OrderService target) {
this.target = target;
}
@Override
public void createOrder(String orderNo) {
System.out.println("[日志] 开始创建订单");
target.createOrder(orderNo);
System.out.println("[日志] 订单创建完成");
}
}
// 4. 客户端
public class StaticProxyDemo {
public static void main(String[] args) {
OrderService proxy = new OrderServiceStaticProxy(new OrderServiceImpl());
proxy.createOrder("202507170001");
}
}
输出:
[日志] 开始创建订单
创建订单:202507170001
[日志] 订单创建完成
4.2 JDK 动态代理
public class JdkProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("[JDK动态代理] 前置逻辑");
Object result = method.invoke(target, args);
System.out.println("[JDK动态代理] 后置逻辑");
return result;
});
}
public static void main(String[] args) {
OrderService proxy = createProxy(new OrderServiceImpl());
proxy.createOrder("202507170002");
}
}
4.3 CGLIB 动态代理
public class CglibProxyFactory {
public static <T> T createProxy(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("[CGLIB] 前置");
Object result = proxy.invokeSuper(obj, args);
System.out.println("[CGLIB] 后置");
return result;
});
return clazz.cast(enhancer.create());
}
// 无需接口
public static class PlainOrderService {
public void pay(String orderNo) {
System.out.println("支付订单:" + orderNo);
}
}
public static void main(String[] args) {
PlainOrderService proxy = createProxy(PlainOrderService.class);
proxy.pay("202507170003");
}
}
4.4 Spring AOP 实战(注解驱动)
@Component
public class UserService {
@Cacheable("user")
public User findUser(Long id) {
System.out.println("查询数据库:" + id);
return new User(id, "Alice");
}
}
// 启动类
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class);
UserService userService = ctx.getBean(UserService.class);
userService.findUser(1L); // 第一次:查库
userService.findUser(1L); // 第二次:读缓存
}
}
5. 结语
代理模式是 Java 生态的“隐形发动机”:
-
在框架层,它让 事务、缓存、安全、RPC 透明化;
-
在业务层,它让 延迟加载、监控、灰度发布 开箱即用。
掌握代理模式,不只是会写 Proxy.newProxyInstance
,更是理解 “开闭原则” 如何在字节码层面落地。祝你编码愉快,代理常伴!