【追根究底】使用@Lazy注解为什么会产生两层代理?

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


  • CASE

    • A
      @Component
      public class A {
      
          @Lazy
          @Autowired
          B b;
      
          public void sayA(){
              b.sayB();
          }
      }
      
    • B
      @Component
      public class B {
      
          public void sayB(){}
      }
      
    • C
      @Aspect
      @Component
      public class C {
          @Pointcut("execution(public * com.acme.lazydemo.*.*(..))")
          public void myAnnotationPointcut(){
      
          }
      
          @Before("myAnnotationPointcut()")
          public void before(JoinPoint joinPoint){
              System.out.println(joinPoint.getTarget() + " begin:" + System.currentTimeMillis());
          }
      
          @After("myAnnotationPointcut()")
          public void after(JoinPoint joinPoint){
              System.out.println(joinPoint.getTarget() + " end:" + System.currentTimeMillis());
          }
      }
      

    接着看现象,第一层代理,实例号记一下@4610
    在这里插入图片描述
    接着单步跟进去,发现target是一个代理对象,并且实例号是@4016,和刚才那个不是同一个。按理说,target应该是实例本身才对,为什么还是一个代理对象呢?
    在这里插入图片描述
    接下来我们就带着这个疑问,深入源码一探究竟。

  • 原理分析

    • 第一层代理是什么?
      第一层代理很简单,是因为@Lazy而产生的一个代理。如果不知道的,可以看一下【追根究底】@Lazy注解为什么会失效?这篇文章。

    • 第二层代理是什么?
      第二层代理,其实就是bean的实例化过程创建的。在实例化一个bean的时候,会在最后判断一下,这个bean上是否存在增强器,存在的话,就会创建一个代理。通俗点,有没有被切(AOP),有的话,肯定得用代理才能生效吧。因为这个例子中,使用到了C类,它是一个AOP类,将AB切了,所以AB都需要被代理。
      由代码中的注释可以知道,如果存在增强器,那么就为这个bean创建代理
      在这里插入图片描述
      所以第二层代理就是这么来的

    • 第一层代理是如何拿到第二层代理的?
      就从targetSource.getTarget()里面拿到的。看过【追根究底】@Lazy注解为什么会失效?这篇文章的都知道,在为B创建@Lazy代理对象的时候,创建了TargetSource,里面实现了getTarget方法。这里再把代码贴一下吧。

      protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
      	Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
      			"BeanFactory needs to be a DefaultListableBeanFactory");
      	final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
      	TargetSource ts = new TargetSource() {
      		……
      		@Override
      		public Object getTarget() {
      			Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
      			……
      			return target;
      		}
      		@Override
      		public void releaseTarget(Object target) {
      		}
      	};
      	ProxyFactory pf = new ProxyFactory();
      	pf.setTargetSource(ts);
      	……
      	return pf.getProxy(beanFactory.getBeanClassLoader());
      }
      

      doResolveDependency方法最终会调用createBean,如果该bean已经被创建了,那么从缓存中直接拿,如果没有,就走正常的bean创建流程。所以此时返回的target就是B的第二层代理了。

  • 总结

    • 产生两层代理的原因是@Lazy标记的属性,会为它创建一个代理对象,此时不会创建真实实例
    • 拿到第二层代理的方式,创建第一层代理的时候,创建TargetSource并实现getTarget方法,当第一层代理调用该类的方法的时候,会通过getTarget拿到第二层代理。
    • 思考为什么要为B创建一个代理对象呢?因为spring容器初始化的时候,并没有实例化B,所以,如果你不给一个代理对象,什么都不做,使用到它的时候,会报空指针异常吧?给了一个代理对象,然后再通过一些手段,当使用到它的时候,创建或者拿到B,再用它真实的调用方法,这不就什么事都没了。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值