Spring源码(七):Spring AOP 详解

一、几种BeanDefinition

  1. RootBeanDefinition:实例化前把所有的BeanDefinition都封装成RootBeanDefinition。
  2. GenericBeanDefinition:一般是由开发者手动构造的BeanDefinition,xml解析创建的BeanDefinition也是GenericBeanDefinition。
  3. ScannedGenericBeanDenition:通过Compoent-scan扫描得到。new ScannedGenericBeanDenition(metadataReader);
  4. AnnotatedGenericBeanDefinition:@Import进来的类和内部类。new AnnotatedGenericBeanDefinition(metadata);
  5. ConfigurationClassBeanDefinition:@Bean解析得到。new ConfigurationClassBeanDefinition(configclass, metadata)

二、动态代理

        Advisor,即切面,必须要有PointCut和Advice。
        PointCut是用来匹配、拦截的。作用:1. 为了生成代理校验当前的类是否有切面,有切面才会生成代理。2. 用代理对象调用的时候,匹配调用的方法到底需不需要拦截。PointCut中ClassFilter用来拦截类,MethodMatcher用来匹配方法。
        Advice,定义了增强的逻辑。
        被代理对象创建完成后,才会生成代理对象

三、AOP的入口类

        在注解的收集与依赖注入、initializeBean、Bean的销毁这篇文章中可以看到,spring aop的入口在这里。但是这里只能知道是AbstractAutoProxyCreator接口,没有具体的实现类。下面来看看具体的实现类是怎样导入的。
在这里插入图片描述

  1. 通过xml的方式导入。
            xml定义<aop:aspectj-autoproxy/>
    原理:spring-aop的spring.handlers定义了aop的处理类org.springframework.aop.config.AopNamespaceHandler,AopNamespaceHandler注册了aspectj-autoproxy的解析类AspectJAutoProxyBeanDefinitionParser,在parse方法中可以看到AnnotationAwareAspectJAutoProxyCreator被封装成BeanDefinition。AnnotationAwareAspectJAutoProxyCreator即是AOP逻辑的实现类。
    在这里插入图片描述
  2. @EnableAspectJAutoProxy注解
            这个注解import了AspectJAutoProxyRegistrar.class,它实现了ImportBeanDefinitionRegistrar接口,registerBeanDefinitions方法中注册到BeanDefinitionRegistry的BeanDefinition会被Spring实例化。可以看到AnnotationAwareAspectJAutoProxyCreator.class被封装成BeanDefinition,然后注册到BeanDefinitionRegistry中。
    在这里插入图片描述

四、AnnotationAwareAspectJAutoProxyCreator

        从AbstractAutoProxyCreator.postProcessAfterInitialization进入,earlyProxyReferences是已经生成代理类的集合(三级缓存里面提前生成代理),根据这个集合判断哪些类要生成代理。在这里插入图片描述
        来到wrapIfNecessary,首先根据getAdvicesAndAdvisorsForBean拿到要生成代理的类。
在这里插入图片描述
        从AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean进入findEligibleAdvisors。首先找到候选的切面,findCandidateAdvisors有两个实现,先执行子类AnnotationAwareAspectJAutoProxyCreator,调用super.findCandidateAdvisors(),可以看到这里是找到所有实现Advisor接口的类,实例化后加入容器。
在这里插入图片描述
        this.aspectJAdvisorsBuilder.buildAspectJAdvisors()是解析带有@Aspect注解的类。首先获取spring容器中所有bean的名称,然后通过this.advisorFactory.isAspect判断是否有@Aspect注解。
        创建获取有@Aspect注解类的实例工厂,把当作参数传入this.advisorFactory.getAdvisors。
在这里插入图片描述
        getAdvisorMethods是用工具类拿到没有Pointcut注解的方法。添加到容器后返回,循环处理没有@Pointcut注解的方法,即带有@Around、@Before、@After这类注解的方法。
在这里插入图片描述
        在getAdvisor()方法中,每个@Around、@Before、@After这类注解的方法都会和pointCut组合成一个advisor。
在这里插入图片描述
        来到getAdvisor,进入getPointcut。
在这里插入图片描述
        从候选的增强方法里面找到Around.class, Before.class, After.class等注解的方法并包装成AspectJAnnotation对象。创建一个PointCut,把AspectJAnnotation对象中的表达式设置进去,返回PointCut。
在这里插入图片描述
        接着看getAdvisor,上面已经创建了pointcut,接下来看InstantiationModelAwarePointcutAdvisorImpl怎么封装advice对象。进入构造方法,可以看到通过this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);创建advice对象。
在这里插入图片描述
        跟着代码来到ReflectiveAspectJAdvisorFactory.getAdvice。
完成了以下操作:

  1. 获取有candidateAdviceMethod对应的类。
  2. 找到candidateAdviceMethod方法上面的注解,包装成AspectJAnnotation对象。
  3. 根据注解类型创建不同的advice类实例。
    在这里插入图片描述

        在Spring中创建Advice有两种方式,一种是实现Advice接口,另一种是实现MethodInterceptor接口,用的最多的是实现MethodInterceptor接口。MethodInterceptor有invoke方法,增强的逻辑就是在这个里面实现。

        继续看AbstractAdvisorAutoProxyCreator.findEligibleAdvisors,findAdvisorsThatCanApply是判断候选的切面是否作用在当前beanClass上面。首先beanClass的类名和表达式匹配,然后beanClass的方法和表达式匹配,把匹配上了的Advisor返回。

        在wrapIfNecessary中判断如果有切面,则生成该bean的代理。
在这里插入图片描述

五、切面的排序

  1. @Aspect注解引入切面的排序
    在这里插入图片描述
            由@Aspect注解引入的切面在getAdvisorMethods方法中排序,在METHOD_COMPARATOR中可以看到,首先根据Around、Before、After、AfterReturning、AfterThrowing的顺序排序,然后根据方法的名称排序。在这里插入图片描述
  2. 实现Advisor接口类的排序
            extendAdvisors中判断是否有@Aspect注解的类,如果有则在eligibleAdvisors容器最前面加一个默认的切面DefaultPointcutAdvisor。它的Pointcut为Pointcut.TRUE,意思是对所有的方法和类都有效,advice是ExposeInvocationInterceptor。ExposeInvocationInterceptor在调用invoke的时候会把MethodInvocation放到ThreadLocal,这个ThreadLocal是静态的,通过ExposeInvocationInterceptor.currentInvocation();可以拿到MethodInvocation对象,从这个对象中可以拿到入参、调用的方法、调用的代理、调用的对象。
    在这里插入图片描述
            sortAdvisors中对@Order和实现了Ordered接口的切面排序。comparePrecedenceWithinAspect返回的是0,对@Aspect引入的切面没有做任何处理。
    在这里插入图片描述
            comparePrecedenceWithinAspect中得到了2个Advisor getAspectDeclarationOrder的差值,getAspectDeclarationOrder来源于getDeclarationOrder,目的是拿到declarationOrder,declarationOrder在定义的时候就为0。所以这里没有对@Aspect引入的切面排序。
    在这里插入图片描述

        总结:DefaultPointcutAdvisor排在最前面,然后是带有@Order或者实现了Ordered接口的Advisor,最后是@Aspect引入的切面。

六、proxyFactory

        被代理实例会被放到实现了TargetSource接口的对象中。createProxy中new创建了proxyFactory代理工厂,proxyFactory.copyFrom(this);是把AnnotationAwareAspectJAutoProxyCreator中的某些属性copy到proxyFactory中。
        proxyTargetClass为true代表使用Cglib动态代理。proxyFactory.isProxyTargetClass()这部分代码根据proxyTargetClass判断使用JDK动态代理还是Cglib动态代理,如果没有实现接口只能使用Cglib动态代理。
在这里插入图片描述
在这里插入图片描述
        进入buildAdvisors,resolveInterceptorNames中可以设置自定义的MethodInterceptor和Advice。interceptorNames可以通过setInterceptorNames设置实现了MethodInterceptor的类,实例化后通过wrap匹配到DefaultPointcutAdvisor后添加到advisors容器中。这是一个全局的Advisor,每一个Bean实例化过程中都会增加这个Advisor。
在这里插入图片描述
在这里插入图片描述
        buildAdvisors中resolveInterceptorNames下面是把之前排序过了的Advisor创建出来并返回,然后设置到proxyFactory中。接着把targetSource对象加入到proxyFactory,这里是SingletonTargetSource。
        进入proxyFactory.getProxy,来到DefaultAopProxyFactory的createAopProxy方法,根据类是否实现接口和proxyTargetClass属性的值判断返回Jdk动态代理还是Cglib动态代理,父接口是AopProxy。创建AopProxy时用了有参构造,参数是proxyFactory。之后会执行动态代理实例的getProxy方法返回代理实例,比如Jdk动态代理类JdkDynamicAopProxy中是执行Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
在这里插入图片描述

七、代理方法的调用

        由上面的内容可知,AopProxy中保存了proxyFactory,proxyFactory中保存了targetSource,targetSource保存了被代理对象。并且,被代理的bean、proxyFactory、AopProxy是一一对应的。
        调用被代理实例的调用方法时,就会执行JdkDynamicAopProxy或CglibAopProxy的invoke方法。
        以JdkDynamicAopProxy的invoke方法举例,这里有段比较重要的代码,根据exposeProxy的属性判断是否将代理对象设置到ThreadLocal中。假如设置了,可以在同一个类中通过AopContext.currenetProxy();拿到代理实例。
在这里插入图片描述
        下面这行代码中的逻辑是用pointCut的ClassFilter和MethodMatcher分别匹配被代理类的类名和方法名,匹配到的方法在被调用时会被增强。getInterceptorsAndDynamicInterceptionAdvice返回的是MethodInterceptor对象。
        在这个过程中会把过滤器链缓存,方便下次调用方法时直接使用。如果该方法没有执行链,则说明这个方法不需要被拦截,则直接反射调用。

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        点进去可以看到这里还有更细粒度的匹配,即方法参数级别的匹配。
        isRuntime()一般为false。如果是@aspect,不用管这部分代码。在自定义MethodMatcher里将isRuntime()返回值改为true,会在下面调到匹配参数的matches方法。这个时候存在动态的Interceptor,最后是
把Advice和目标方法绑定成InterceptorAndDynamicMethodMatcher。
在这里插入图片描述

        增强逻辑链式调用。如果匹配到的Advice为空,直接反射调用目标方法。如果Advice链不为空,则走到invocation.proceed();
在这里插入图片描述
        从chain的第一个元素开始,判断是否为InterceptorAndDynamicMethodMatcher,如果是,强转后调用匹配参数的matches方法,如果能匹配上,调用增强逻辑,不匹配就递归调用。

在这里插入图片描述
        如果从chain中取出的元素是MethodInterceptor类型,直接调用Advice中的增强逻辑。通过MethodInterceptor.invoke(this),MethodInvocation.proceed()传递,反射调用每一个MethodInterceptor的增强逻辑。特别说明,around的MethodInvocation.proceed()需要在Advice中自己加上ProceedingJoinPoint.proceed()调用。
        根据之前的排序,around的前置增强是最开始被调用的,然后是before,after等等,最后是around的后置增强。

八、提前生成代理

        在createBean中有这样一部分代码。如果返回的bean不为空就不会走下面实例化的代码,直接返回代理实例。
在这里插入图片描述
        进入resolveBeforeInstantiation,applyBeanPostProcessorsBeforeInstantiation,来到AbstractAutoProxyCreator的postProcessBeforeInstantiation。getCustomTargetSource返回一个自定义的TargetSource,如果自定义TargetSource不为空就会返回代理实例。
在这里插入图片描述
        在getCustomTargetSource中,如果customTargetSourceCreators变量不为空,就会返回TargetSource。customTargetSourceCreators变量可以自己用AnnotationAwareAspectJAutoProxyCreator的setCustomTargetSourceCreators设置targetSourceCreator,targetSourceCreator中返回自定义的TargetSource。
在这里插入图片描述
        进入tsc.getTargetSource。getInternalBeanFactoryForBean中重新创建了BeanFactory,把AopInfrastructureBean类型的beanPostProcessor删除了,也就是AOP入口类AnnotationAwareAspectJAutoProxyCreator。也就是说假如目标实例有切面,在调用目标方法时,在AopProxy的invoke中不会返回目标实例的代理对象。
在这里插入图片描述
        接下来,为目标实例创建了GenericBeanDefinition,并将Scope设置为多例。为什么要设置为多例?因为这里没有把目标实例实例化,在要使用目标实例时,如果是单例还要去缓存中找,没有的话再走生成实例的逻辑。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值