Spring源码深度解析(下)

        接上一篇博文Spring源码深度解析(中),最后讲完了属性属性的依赖注入,即AbstractAutowireCapableBeanFactory#populateBean()方法,这里会处理 @Autowired注解和@Resource的依赖注入,其实这里漏了一点东西没讲,还是在CommonAnnotationBeanPostProcessor类中,除了寻找 @Resource注解外,还处理 @PostConstruct注解和@PreDestroy注解,代码如下:

并且CommonAnnotationBeanPostProcessor继承了InitDestroyAnnotationBeanPostProcessor,真正的处理在其父类,而 InitDestroyAnnotationBeanPostProcessor实现了MergedBeanDefinitionPostProcessor接口。因此,最终是在InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()方法中进行处理的,代码如下:

        ok,至于被@PostConstruct注解和@PreDestroy注解修饰的方法早什么时候被调用,后面聊。然后就是AbstractAutowireCapableBeanFactory#initializeBean()方法,进行bean的初始化操作(这时候的bean对象,属性已经填充完毕),看看该方法的代码:

该方法内,做了这么几件事:

① 调用AbstractAutowireCapableBeanFactory#invokeAwareMethods()方法,代码如下:

可知,就是对一些接口方法的回调。

② 调用AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization()方法,代码如下:

可知,就是拿到后置处理器的集合,调用BeanPostProcessor#postProcessBeforeInitialization()方法,其中就包括InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()方法,代码如下:

就会调用到,被@PostConstruct注解修饰的方法。

③ 调用AbstractAutowireCapableBeanFactory#invokeInitMethods()方法,代码如下:

可知,判断该bean是否实现了InitializingBean接口,如果实现了,强转成 InitializingBean对象,调用 InitializingBean#afterPropertiesSet()方法。因此这里可以下一个结论,即: @PostConstruct注解修饰的方法是先于实现了 InitializingBean接口的afterPropertiesSet()方法调用的

④ AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization()方法,该方法非常重要,因为 AOP的逻辑就是在此方法中实现的,这里会着重聊聊,代码如下:

先看看AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean()方法,代码如下:

可知,在父类 AbstractAdvisorAutoProxyCreator中,先找出所有实现了Advisor(Spring提供的切面)接口的类,并缓存起来。AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors()方法,代码如下:

再看看ReflectiveAspectJAdvisorFactory#getAdvisors()方法,代码如下:

根据上面的代码,这里做一个小结:当调用AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors()方法,会找出所有被@Aspect注解修饰的类,遍历类,找出类中被:@Pointcut、@Around、@Before、@After、@AfterReturning、@AfterThrowing等五种注解修饰的方法,生成AspectJExpressionPointcut对象,存放解析@Pointcut注解的信息。生成InstantiationModelAwarePointcutAdvisorImpl对象,存放aspectName(切面名,比如被@Aspect注解修饰的bean的beanName)、方法、AspectJExpressionPointcut对象等。在 InstantiationModelAwarePointcutAdvisorImpl的构造中,判读是否懒加载,如果不是,则在构造方法中,调用InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice()方法,在该方法中,又会调用 ReflectiveAspectJAdvisorFactory#getAdvice()方法,在该方法中,就会判断,当前方法,是否是被:@Around、@Before、@After、@AfterReturning、@AfterThrowing等注解中的一个所修饰,针对这几个不同的注解,会生成不同的 Advice对象。如:

① @Around => AspectJAroundAdvice;

② @Before => AspectJMethodBeforeAdvice;

③ @After => AspectJAfterAdvice;

④ @AfterReturning => AspectJAfterReturningAdvice;

⑤ @AfterThrowing => AspectJAfterThrowingAdvice

并将aspectName设置给生成的 Advice对象。因此,被以上五种注解修饰的方法,生成的 Advisor对象是一样的,即 InstantiationModelAwarePointcutAdvisorImpl对象,但是针对被这五种不同注解修饰,它的成员属性 instantiatedAdvice对应的对象是不同的,即以上五种 Advice实现类。

到这里为止,针对被 @Aspect注解修饰的类修饰的类,还是实现了 Advisor接口的类,都会被扫描获取到,并缓存起来。回到 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors()方法,看看AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply()方法,这里会多传入的bea进行过滤,看这个bean是否需要AOP,代码如下:

        可以看出,核心的过过滤方法是 AopUtils#canApply()方法,看看代码:

        可以看出,在进行过过滤的时候,有两种情况:

① 实现 IntroductionAdvisor接口,则直接判断调用 IntroductionAdvisor#getClassFilter().matches()判断;

② 实现 PointcutAdvisor接口,这种情况继续看代码:

        可知,是先判断传入的类是否匹配,当类匹配了,再判断方法是否匹配 。当方法也匹配了,那这个类才可以进行AOP代理。上面提到的 InstantiationModelAwarePointcutAdvisorImpl就是实现了 PointcutAdvisor接口,因此需要两次判断。

        总结一下: 当bean在进行初始化的时候,会调用 BeanPostProcessor#postProcessAfterInitialization()方法,在这个方法中,会先找到所有实现了Advisor接口或者被@Aspect注解所修饰的bean,并将其缓存起来,后面其他所有bean在进行初始化调用到 BeanPostProcessor#postProcessAfterInitialization()这里的时候,都会从缓存中拿到 Advisor的集合,进行遍历,判断当前初始化的类,是否需要进行AOP,如果需要,则返回代理bean,如果不需要,则返回原始bean。然后再讲讲AOP具体生成代理对象的逻辑,这里以@Aspect注解为例讲解。我自己建了一个MyAspect类,如下图所示:

        根据@Pointcut注解中的表达式,前置方法会执行作用于 UserService.test()方法,那它是如何作用的呢,请听我细细道来。回到AbstractAutoProxyCreator#wrapIfNecessary()方法来,对了,要使用AOP,还要在配置类上加一个注解,即:@EnableAspectJAutoProxy,该注解的作用是往Spring容器中加一个后置处理器,默认Spring容器是没有这个后置处理器的,因此AOP不生效,代码如下:

        回到正题,再调用到 AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean()方法的时候,会得到一个 Advisor,代码如下:

        specificInterceptors不为空,则进入if代码块,调用 AbstractAutoProxyCreator#createProxy()方法,生成代理bean,看看该方法:

        再看看AbstractAutoProxyCreator#buildAdvisors()方法,代码如下:

        再看看ProxyFactory#getProxy()方法,代码如下:

        以为 JDK的动态代理为例:进入JdkDynamicAopProxy#getProxy()方法,代码如下:

advised对象就是ProxyFactory,即调用 ProxyFactory#getInterceptorsAndDynamicInterceptionAdvice()方法,代码如下:

        然后调用 DefaultAdvisorAdapterRegistry#getInterceptors()方法,进行适配:如果从advisor对象那个中获取到的Advice对象实现了MethodInterceptor接口,则不需需要适配;如果没有实现 MethodInterceptor接口,则需要进行适配。比如 MyAspect#before()方法,是被 @Before注解所修饰的,从前面可知得到对象,是 AspectJMethodBeforeAdvice,但是该类并没有实现 MethodInterceptor接口,因此需要适配,线通过 AdvisorAdapter#supportsAdvice()方法判断,哪个 适配对象是符合的,实际上 MethodBeforeAdviceAdapter是可以对其进行适配的,即调用 MethodBeforeAdviceAdapter#getInterceptor()方法,得到的对象,就是实现了MethodInterceptor,代码如下:

        回到 JdkDynamicAopProxy#invoke()方法中,this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass),这行代码,最终返回的 chain,实际上就是所有经过过滤后,符合要求的 MethodInterceptor的集合。创建ReflectiveMethodInvocation对象,调用有参构造,传入target、method、chain等,调用 ReflectiveMethodInvocation#proceed()方法,调用代理对象的代理方法,代码如下:

        调用MethodInterceptor#invoke()方法,就是调用 MethodBeforeAdviceInterceptor#invoke()方法,代码如下:

        可知这里就是通过反射,调用切面类的通知方法,当所有的 MethodInterceptor#invoke()方法执行完毕之后,在ReflectiveMethodInvocation#proceed()方法中会做判断,代码如下:

        通过反射调用被代理对象的原始方法,这样就完成了对代理对象的处理。以上就是Spring框架AOP的原理解释!如果对CGLib的动态代理感兴趣 ,可以看CglibAopProxy#getProxy()方法,代理逻辑,主要看设置的 Callback对象。代码如下:

有兴趣的自己看,这里我就不介绍了。到这里为止,bean的初始化就讲完了。之前留了一点东西没讲,也就是 @PreDestroy注解,这块的逻辑,就在AbstractAutowireCapableBeanFactory#initializeBean()方法之后,代码如下:

        判断条件为: 

① 该bean不是NullBean类型;

② 该bean实现了 DisposableBean接口或者 AutoCloseable接口,或者该bean对应的BeanDefinition,指明了 destroyMethodName;

③ 或者Spring的后置处理器列表中有 DestructionAwareBeanPostProcessor类型的后置处理器;④ 或者 bean对应的类中,有方法被 @PreDestroy注解所修饰。

        其中,①为必须条件,②、③、④满足其一即可。如果满足①④的情况,当然也会进入if代码块,调用DefaultSingletonBeanRegistry#registerDisposableBean()方法,创建DisposableBeanAdapter对象,作为入参传入,代码如下:

        到目前为止,也只是将DisposableBeanAdapter对象进行注册,也就是放入disposableBeans中,暂时不会调用销毁的相关逻辑。看看disposableBeans属性在那里被使用就知道了,销毁逻辑在哪些情况下会调用,大概有这些地方:

        而AbstractApplicationContext#doClose()方法则在这些地方调用了,代码如下:

        再看看AbstractApplicationContext#destroyBeans()方法,代码如下:

        到这里为止,销毁逻辑就讲完了,讲的比较粗糙,因为销毁这块,在实际开发中使用较少,我也没有很仔细的研究,聊到这个程度我觉得是足够了。

        对了,还有循环依赖,这里我大概讲讲。比如说有A、B两个类,A中有B属性(被@Autowired注解修饰),B中也有A属性(被@Autowired注解修饰),并且A、B两个被都是交给Spring容器管理的。此种情况,会产生循环依赖,一般针对这种属性依赖的相互依赖,Spring是可以自己处理而不报错的。大概是这样:假如说是先创建的A,先将A的beanName放入一个Set属性中,表示当前对象正在被创建,在被实例化话后,将一段λ表达式放入三级缓存中(包括早期的A对象),然后再进行属性的填充,也就是AutowiredAnnotationBeanPostProcessor的处理逻辑。然后在寻找注入点的时候发现了A类依赖B类,这时候会调用getBean()方法,传入B类的beanName去尝试获取B对象,由于此时B还没有被实例化,因此同A一样,会进行B类的实例化以及初始化,在此之前,也会将B的beanName放入一个Set属性中表示当前对象正在被创建,然后也会将一段λ表达式放入三级缓存中(包括早期的B对象)。然后对B对象进行属性填充的时候,发现B类依赖A类,这时候也会调用getBean()方法,传入A类的beanName去尝试获取A对象,这时候,由于在Spring的三级缓存中,已经有A的早期对象了,就可以拿到之前放入的 λ表达式,代码如下:

        三级缓存传入的那段λ表达式我说过,它的逻辑主要是判断传入的bean对象时是否需要进行AOP:需要的话,得到的是代理对象;不需要的话得到的是原始对象。这样的话就完成了将A的早期对象注入到B对象中。当B对象完成对象的初始化后,A就可以得到B对象,然后进行属性的注入(包括其A对象其他的属性注入)。当A对象的初始化完成后,B对象所依赖的A对象就是一个完整的对象了。

        这里要解决循环依赖需要使用到Spring的二三级缓存,如果不考虑AOP,实际上第二级缓存就可以解决循环依赖,三级缓存主要是处理bean的AOP(防止bean被多次AOP)。还有一点,就是属性的依赖注入的时候,注入的这个bean对象不要求是不是完整的(此时bean很多属性可能是null),所以才可以解决循环依赖。如果是通过构造方法进行属性的依赖注入,这时候三级缓存就不行了,因为对象在创建的时候就必须注入该对象,Spring的单例池如果没有这个以来的bean就直接报错。要解决构造方法的循环依赖也有办法,就是通过@Lazy注解就可以搞定,因为根据前面讲的,如果属性被@Lazy注解修饰,Spring会生成一个代理对象进行注入,因此这样是没问题的。

        以上,就是Spring框架的大概原理(后面有时间,会在本博客补充Spring事务的原理),讲得不对或者不清楚的地方望批评指正,谢谢!

  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值