前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AOP动态代理的区别你造吗?

AOP动态代理的区别你造吗?

作者头像
敲得码黛
发布2021-02-22 11:19:51
4040
发布2021-02-22 11:19:51
举报
文章被收录于专栏:敲得码黛敲得码黛

前言

在一个风和日立的下午,一个java程序员正在愉(tong)快(ku)的修改着bug,旁边的一个好基友突然问我AOP动态代理的区别。楞了一下,心想 " 卧槽,这特喵的就触及到我的知识盲区了"。尽管内心波涛汹涌,表面上还是故作镇定的答道:“我现在还有工作要忙,明天再告诉你”。好基友只能点点头说那好吧,下班回到家后赶紧麻溜的打开笔记本一顿谷歌加百度

  • JDK动态代理是基于接口的代理方式,其实现原理是让代理对象与原生对象实现相同的接口,并且在代理对象内部维护一个原生对象的引用。这样一来,不需要加强的方法,就可以通过原生对象的引用直接返回。需要加强的方法可以在原生对象方法执行前后做相应的处理。
  • CGLIB动态代理是基于继承的代理方式,其实现原理也是在代理对象内部维护一个原生对象的引用,不同的是此方式生成的代理对象是原生对象的子类。因此由于final类中的方法、private方法、final方法不能被继承、因此这些方法都无法使用CGLIB进行增强
Spring相关特性
  • Spring IOC中存放的Bean默认是单例的
  • Spring IOC进行依赖注入时,只有需要被AOP加强的Bean才会注入代理对象,否则注入原生对象
  • Spring MVC中的AOP底层实现默认是JDK动态代理,只有被代理的类没有实现接口时才会使用CGLIB代理。
  • Spring Boot中的AOP的底层实现默认是CGLIB。
  • Spring中的一些注解(@Async@Retry@Transaction)都属于AOP的一种
  • 需要被AOP增强的方法,必须由代理对象直接调用才能生效(内部方法调用不生效)。
  • 如果一个被spring管理的类使用了AOP,那么在IOC容器中维护的就是该类的代理对象。如果采用的是JDK动态代理,那么就只能通过接口的方式进行注入。通过实现类进行注入时将会提示类转换异常。

如果不注意这些点,在实际开发过程中往往会出现一些奇怪的现象。我遇到的问的最多的一个问题就是为什么事务没生效? 感兴趣的可以参考我的另一篇文章:为啥我的@Transaction不生效?

JDKProxy伪代码
代码语言:javascript
复制
/**
 * 接口:TargetService
 * 原生类:Target       指被代理的类
 * 代理类:TargetProxy  指代码增强后产生的类
 * 代理对象:targetService 指代理类所产生的对象
 * 原生对象:target  指代理类内部引用的对象
 *
 * @author hcq
 * @date 2020/3/25 20:57
 */
public class JdkProxy {

   interface TargetService {
       void save();
       void select();
   }

  static class Target implements TargetService {
       @Override
       public void save() {
           System.out.println("我要变强!!!");
       }

       @Override
       public void select() {
           System.out.println("我是咸鱼,我不增强");
       }
   }


  static class TargetProxy implements TargetService{
       private final Target target;

       TargetProxy(Target target){
           this.target = target;
       }

       @Override
       public void save() {
           System.out.println("已充值1000000元");
           target.save();
           System.out.println("小伙子你很强!");
       }

       @Override
       public void select() {
           target.select();
       }
   }

    public static void main(String[] args) {
        //new TargetProxy(new Target())对象的过程其实就相当于是spring注入代理对象的过程
        TargetService targetService=new TargetProxy(new Target());
        //增强后的方法
        targetService.save();
        //未进行增强的方法
        targetService.select();
    }
}
CGLIB伪代码
代码语言:javascript
复制
/**
 * @author hcq
 * @date 2020/3/25 23:19
 */
public class Cglib {

    static class Target{
        void save(){
            System.out.println("我不充钱也要变强!!!");
        }
        void select(){
            System.out.println("我继续咸鱼。。。。。");
        }
    }

    static class TargetProxy extends Target{
        private final Target target;

        TargetProxy(Target target){
            this.target = target;
        }

        @Override
        void save(){
            target.save();
            System.out.println("日复一日,年复一年的辛苦刷经验,终于达到了完成了变强的愿望。\n");
        }
        @Override
        void select(){
            System.out.println("我继续咸鱼。。。。。");
        }
    }

    public static void main(String[] args) {
        //new TargetProxy(new Target())对象的过程相当于是spring注入CGLIB代理对象的过程
        Target targetService=new TargetProxy(new Target());
        //增强后的方法
        targetService.save();
        //未进行增强的方法
        targetService.select();
    }
}

觉得写的还行的话点个赞再走呗,你们的点赞与关注就是我创作的最大动力。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 敲得码黛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • Spring相关特性
      • JDKProxy伪代码
        • CGLIB伪代码
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档