亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

Java 動態代理詳解:定義、實現與應用場景

標簽:
Java JavaScript

原文来自于:https://zha-ge.cn/java/31

Java 动态代理详解:定义、实现与应用场景

一场"偷梁换柱"的技术探险

上个月接到一个需求:给公司所有的数据库操作加上日志记录,但不能改动现有的 DAO 层代码。我当时就懵了,这不是要我"空手套白狼"吗?

直到老同事神秘一笑:“试试动态代理吧,保证让你体验一把’狸猫换太子’的快感。”

什么是动态代理?一个"替身演员"的故事

想象一下,你是个大明星,但又不想亲自出席每个商业活动。这时候你需要一个替身,这个替身:

  • 长得像你(实现相同接口)
  • 会模仿你的行为(调用真实方法)
  • 还能做一些你不想做的事(添加额外功能)

Java 动态代理就是这样的"替身演员",它能在运行时动态创建一个代理对象,替你干活的同时还能偷偷加点料。

初次尝试:JDK 动态代理登场

我决定先用 JDK 自带的动态代理试试水。核心思路很简单:创建一个 InvocationHandler,在里面做手脚。

public class LoggingHandler implements InvocationHandler {
    private Object target;
  
    public LoggingHandler(Object target) {
        this.target = target;
    }
  
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行方法: " + method.getName());
        long startTime = System.currentTimeMillis();
      
        Object result = method.invoke(target, args);  // 调用真实方法
      
        System.out.println("耗时: " + (System.currentTimeMillis() - startTime) + "ms");
        return result;
    }
}

使用起来也很简单,就是把原来的对象"包装"一下:

// 创建代理对象
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    userService.getClass().getClassLoader(),
    userService.getClass().getInterfaces(),
    new LoggingHandler(userService)
);

// 使用代理对象,自动带日志功能
proxy.getUserById(123);  // 这里会自动记录日志

踩坑瞬间:接口限制让我措手不及

正当我沾沾自喜时,产品经理过来说:“还有个工具类也要加日志,但它没实现接口啊!”

我一查,发现 JDK 动态代理有个硬性要求:被代理的类必须实现接口。这就像替身演员只能模仿有剧本的角色,遇到即兴表演就抓瞎了。

怎么办?这时候 CGLIB 闪亮登场!

转折点:CGLIB 拯救无接口类

CGLIB 是个更厉害的"变身术",它通过字节码技术直接继承目标类,创建子类代理。即使没有接口,也能完美替身。

public class CGLibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB代理 - 执行方法: " + method.getName());
      
        Object result = proxy.invokeSuper(obj, args);  // 调用父类方法
      
        System.out.println("CGLIB代理 - 方法执行完毕");
        return result;
    }
  
    // 创建代理对象的工厂方法
    public static Object createProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new CGLibProxy());
        return enhancer.create();
    }
}

这下好了,不管有没有接口,都能愉快地加日志了。

经验启示:两种代理的选择策略

经过这次折腾,我总结了一套"代理选择法则":

场景 JDK 动态代理 CGLIB 代理
有接口 ✅ 首选,性能更好 ✅ 可用,但略重
无接口 ❌ 无法使用 ✅ 唯一选择
final 类 ❌ 无法使用 ❌ 无法继承
性能要求 🚀 更快 🐌 稍慢

实际项目中,Spring AOP 就是这么干的:有接口用 JDK 代理,没接口用 CGLIB 代理,自动帮你选择最合适的方案。

应用场景:代理的"十八般武艺"

动态代理在实际开发中简直是"万金油":

日志记录:我们刚才演示的场景,无侵入式添加日志

权限控制:在方法执行前检查用户权限

事务管理:Spring 的 @Transactional 注解就是这么实现的

缓存处理:自动缓存方法返回值,提升性能

性能监控:统计方法执行时间,找出性能瓶颈

重试机制:失败时自动重试,提高系统稳定性

收获与感悟

这次动态代理的探险让我深刻体会到:好的技术就像魔法,能让不可能变成可能

原本需要修改几十个类的需求,用动态代理几行代码就搞定了。这种"四两拨千斤"的感觉,就是编程的魅力所在。

更重要的是,我明白了设计模式的精髓:不是为了炫技,而是为了解决实际问题。代理模式让我们能够在不修改原有代码的前提下,灵活地扩展功能,这在企业级开发中简直就是救命稻草。

下次再遇到"不能改代码但要加功能"的需求,我会淡定地说:“小case,动态代理了解一下?”

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消