乐于分享
好东西不私藏

Spring——AOP之全注解源码探查

Spring——AOP之全注解源码探查

   每天进步一点点,大家好,我是大龄码农。

    今天我们聊聊Spring是如何使用注解来完成AOP功能的

    在开始之前,我们要明确一件事,Spring最先是使用xml配置的,后来才使用注解。那么,在保证Spring整体流程不做大的改动的前提下,如果要引入注解,Spring最应该做的事情是什么?

    大家应该还记得,在前面的源码探查系列中,使用xml配置时,Spring对于不同的标签会有相应的解析器,作用就是将xml配置的内容转换为BeanDefinition对象。那么,使用注解配置时,Spring要做的无非就是想办法将注解标识的类转换成BeanDefinition对象。

    那么,我们就用以下这个程序,来看看Spring是如何处理注解的

    说明:对注解不太熟悉的同学,猛戳这里

    其实,对于上面的程序来说,下面的写法可能会更清晰一些

    说明:使用@Pointcut标注一个方法

一、注解

    1、@Aspect

        这是AspectJ的注解,代表这个类是一个切面

    2、@EnableAspectJAutoProxy

        启用AspectJ的注解支持,使用xml的配置就是<aop:aspectj-autoproxy/>标签

    注:这个注解写在这个类上,其实不太合适。

    说明:这个注解引入了AspectJAutoProxyRegistrar类

    说明:先记住这个类的继承关系,一会儿再介绍

二、处理过程

    说明:在测试类时使用的是AnnotationConfigApplicationContext类的这个构造函数

    说明:和ClassPathXmlApplicationContext一样,它也是AbstractApplicationContext的一个实现类

    1、this()

    说明:这里初始化两个类

    1)AnnotatedBeanDefinitionReader

        代码一路点进去,进入到registerAnnotationConfigProcessors()方法

    说明:这个方法由两部分构成

  • 设置DefaultListableBeanFactory的属性

  • 手动注册PostProcessor

    a、ConfigurationClassPostProcessor

    说明:它是BeanFactoryPostProcessor的实现类,所以会在invokeBeanFactoryPostProcessors(beanFactory)方法中触发postProcessBeanFactory()方法

    b、AutowiredAnnotationBeanPostProcessor

    说明:它是BeanPostProcessor的实现类,所以在对象属性初始化的前后都会调用相应的方法

    c、CommonAnnotationBeanPostProcessor

    说明1:它是BeanPostProcessor的实现类,也是InitDestroyAnnotationBeanPostProcessor的实现类,也就是说,在方法初始化前后以及对象销毁前都会调用相应的方法

    说明2:这个类存在的前提是当前有javax.annotation.Resource类

    d、PersistenceAnnotationBeanPostProcessor

        用于注入持久化信息,相应的JPA资源是EntityManagerFactory和EntityManager

    说明:这个类存在的前提是当前有javax.persistence.EntityManagerFactory类

    e、EventListenerMethodProcessor

    说明:与Spring事件相关,后续会介绍

    f、DefaultEventListenerFactory

    说明:与Spring事件相关,后续会介绍

小结:在AnnotatedBeanDefinitionReader里,主要目的就是注册几个BeanPostProcessor

    2)ClassPathBeanDefinitionScanner

        一路点击,进入到ClassPathBeanDefinitionScanner()方法

    说明:这里先关注registerDefaultFilters()方法

    说明:待过滤的默认注解,首当其冲的就是Component注解,

    小结:在ClassPathBeanDefinitionScanner里,主要目的就是定义要过滤的注解

    2、scan(basePackages)

       一路点击,来到如下方法

    说明1:方法入参是可变参数,所以可以传入多个

    说明2:这里调用了registerAnnotationConfigProcessors()方法,可能感觉是重复了,其实是因为调用的入口不同所致,但是Spring要保证在处理前注册那几个PostProcessor

    说明:这里简单说下doScan()方法

    a、findCandidateComponents()

        找到候选类,即过滤提前定义好的注解的类,这个方法包含两个步骤

  • 通过递归找到包下面的所有class文件

  • 对于找到的class文件,再筛选符合条件的class文件

    b、postProcessBeanDefinition()

        查看是否有定义autowireCandidatePatterns,通过正则表达式定义一组模式,这些模式将匹配的bean视为自动装配的候选者。在大型项目中特别有用。在xml配置中,使用<context:include-filter>标签

    c、processCommonDefinitionAnnotations()

        处理一些通用注解和属性,比如:Primary和Lazy

    d、AnnotationConfigUtils.applyScopedProxyMode()

        用于处理Scope注解,判断是否要使用作用域代理,默认不使用。作用域代理一般用于如下情况:如果A依赖B,并且B的作用域范围是小于A的作用域范围时,正常情况下,那么B的生命周期会变成与A的生命周期一致。如果不想要改变B的生命周期,就需要使用作用域代理来解决。

        实现方式还是差不多的,JDK代理也好,CGLIB代理也好,使用的类是

ScopedProxyFactoryBean
    说明:看到FactoryBean,我想大家基本就明白了一大部分,有兴趣的话,可以再深入解析
    小结:在scan()方法中,主要目的就是找到待处理的类,同时针对不同的注解作相应的设置

    3、refresh()

        这个方法不能再陌生一点儿了吧,它就是整体的处理流程。下面我们就一些不同点简单说一下

    a、obtainFreshBeanFactory()

        这里调用的是两个方法都是来自于GenericApplicationContext类,但是实际上没有做什么

    注:另一个子类AbstractRefreshableApplicationContext就做了一些事,最主要的就是通过loadBeanDefinitions()方法将xml标签解析成对应的BeanDefinition对象

    b、invokeBeanFactoryPostProcessors()

        一路点进去,来到invokeBeanFactoryPostProcessors()方法

    说明:还记得ConfigurationClassPostProcessor类吗,它是BeanDefinitionRegistryPostProcessor的子类,所以,以前没有走过的分支如今就要走一走了

    说明:进入到processConfigBeanDefinitions()方法,关于排序什么的就不说了,直接到达这里

    说明:虽然注释是解析Configuration注解,但是这里包括Component注解

    说明:因为Configuration是被Component注解的注解

    说明:进入到parse()方法,肯定进入第一个判断分支

    说明1:parse()方法调用XXX()方法,XXX()方法调用doXXX()方法

    说明2:在doProcessConfigurationClass()方法中,主要处理了如下注解:Component、PropertySources、ComponentScan、Import、ImportResource、Bean等

    说明:这里介绍下对Import注解的处理

    说明:这是filter的定义

    说明:会走这个分支,因为AspectJAutoProxyRegistrar是ImportBeanDefinitionRegistrar的实现类。创建对象后,被加入到名为importBeanDefinitionRegistrars的LinkedHashMap中

    说明:从方法出来后,继续向下执行到loadBeanDefinitions()方法,看名字就应该知道它要将注解类转换为BeanDefinition对象了。一路进入到

loadBeanDefinitionsForConfigurationClass()方法
    说明:入参的值是importBeanDefinitionRegistrars,里面存放的是AspectJAutoProxyRegistrar
    说明:这时实际上调用的是AspectJAutoProxyRegistrar#registerBeanDefinitions()方法

    说明1:使用AnnotationAwareAspectJAutoProxyCreator类注册一个名为”org.springframework.aop.config.internalAutoProxyCreator”的BeanDefinition

    说明2:解析EnableAspectJAutoProxy注解,判断是否要给说明1中的BeanDefinition增加属性值

    说明:这是AnnotationAwareAspectJAutoProxyCreator类的继承图

小结:因为ConfigurationClassPostProcessor类是BeanDefinitionRegistryPostProcessor的子类,所以在创建Bean对象前对BeanDefinition进行操作,主要目的就是解析注解,并转换成BeanDefinition对象。

   三 、创建容器对象

    其实创建流程与以前一样,只是使用的实现类不一样,简单说一下,现在大家都知道,AbstractAutoProxyCreator#postProcessAfterInitialization()是创建代理的重要方法。

     其中isInfrastructureClass()方法,以及在AbstractAdvisorAutoProxyCreator#findEligibleAdvisors()方法中调用findCandidateAdvisors()方法时,调用的都是AnnotationAwareAspectJAutoProxyCreator类提供的实现方法

    关于获取对象和执行目标方法和之前说的一样,这里就不再赘述了。

    这次之所以聊全注解的源代码,主要原因在于:

    • 了解Spring是如何处理注解的
    • 为未来打基础
    虽然xml配置是基础,但是注解方式更常用。
        总结:
        1、介绍了全部使用注解实现AOP功能的源代码
        2、感叹Spring优秀的扩展性