想象你是一名外卖小哥(真实对象),现在需要:
但公司要求不能修改你的核心送餐流程——这时就需要动态代理这个"分身术"!它能帮你自动生成一个"智能分身",在保持你原有工作方式的同时,悄悄加上这些新功能。
动态代理就像外卖平台的智能调度系统:
// 传统静态代理:一对一服务
class TakeoutProxy extends TakeoutWorker {
public void deliver() {
System.out.println("[记录]开始配送");
super.deliver(); // 调用真实对象
}
}
// 动态代理:智能调度所有骑手
Object proxy = Proxy.newProxyInstance(...);
// 1.定义接口(平台协议)
interface TakeoutService {
void deliverFood();
}
// 2.真实对象(骑手)
class Rider implements TakeoutService {
public void deliverFood() {
System.out.println("骑手正在送餐...");
}
}
// 3.创建代理
TakeoutService proxy = (TakeoutService) Proxy.newProxyInstance(
Rider.class.getClassLoader(),
new Class[]{TakeoutService.class},
(p, method, args) -> {
System.out.println("[系统日志]开始执行" + method.getName());
return method.invoke(new Rider(), args);
}
);
proxy.deliverFood();
// 输出:
// [系统日志]开始执行deliverFood
// 骑手正在送餐...
// 1.引入依赖(基因编辑工具包)
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
// 2.创建增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductDAO.class); // 指定要克隆的类
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
if (method.getName().equals("getById")) {
System.out.println("[缓存]先查缓存...");
}
return proxy.invokeSuper(obj, args);
});
// 3.生成代理对象
ProductDAO proxy = (ProductDAO) enhancer.create();
proxy.getById(101);
// 输出:
// [缓存]先查缓存...
// 执行真实查询...
特性 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
是否需要接口 | ⭐⭐⭐(必须) | ⭐⭐⭐(不需要) |
执行速度 | ⭐⭐ | ⭐⭐⭐ |
创建代理速度 | ⭐⭐⭐ | ⭐⭐ |
内存消耗 | ⭐⭐⭐ | ⭐⭐ |
方法限制 | 仅接口方法 | 不能代理final方法 |
代理方式 | 耗时(ms) |
---|---|
直接调用 | 15 |
JDK动态代理 | 180 |
CGLIB代理 | 120 |
Spring就像经验丰富的教练:
// Spring配置强制使用CGLIB
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {}
public Object invoke(Object proxy, Method method, Object[] args) {
String key = args[0].toString();
if (cache.containsKey(key)) {
System.out.println("命中缓存!");
return cache.get(key);
}
Object result = method.invoke(target, args);
cache.put(key, result);
return result;
}
private RateLimiter limiter = RateLimiter.create(10.0); // 每秒10次
public Object invoke(Object proxy, Method method, Object[] args) {
if (!limiter.tryAcquire()) {
throw new RuntimeException("操作太频繁!");
}
return method.invoke(target, args);
}
就像《盗梦空间》中的造梦师,动态代理让我们能在运行时"植入"新功能。记住:
思考题:如果要用动态代理实现自动重试机制(比如网络调用失败自动重试3次),代码该怎么写?欢迎在评论区秀出你的实现!