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

2021-02-22 11:19:51 浏览数 (1)

前言

在一个风和日立的下午,一个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();
    }
}

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

0 人点赞