spring aop 一本正经的胡说八道笔记

AOP学习

题外话

  1. org.springframework.aop.advisor接口 具有一个getAdvice方法,PointcutAdvisor继承了它增加了一个getPointcut方法;
    所以要构建一个PointcutAdvisor,就需要具备有advice和pointCut
    而一个pointCut具备了getClassFilter和getMethodMatcher方法,匹配类和方法
  2. 而Interceptor extends Advice,MethodInterceptor extends Interceptor
  3. MethodInterceptor 有一个invoke(MethodInvocation invocation)
  4. AspectJMethodBeforeAdvice,AspectJAfterReturningAdvice这两个不是interceptor仅仅是advice
  5. 所以advice有可能是一个interceptor,也有可能不是,如果不是interceptor,又要转成它,就要有适配器AdvisorAdapter能getInterceptor

  • 事务
    .事务registerAutoProxyCreator的是InfrastructureAdvisorAutoProxyCreator,跟切面类AOP同样的继承线路
    所以你看事务中BeanFactoryTransactionAttributeSourceAdvisor是一个PointcutAdvisor,就需要有pointcut和advice,

    • pointcut是用来匹配方法MethodMatcher和类ClassFilter,所以就有了TransactionAttributeSourcePointcut里面有TransactionAttributeSourceClassFilter,TransactionAttributeSource(分注解匹配和名字匹配两种)就用来MethodMatcher的
    • advice可以是一个MethodInterceptor,这边用TransactionInterceptor进行触发invoke进入事务的处理
  • @EnableCaching
    缓存这边也是注入InfrastructureAdvisorAutoProxyCreator.
    然后跟事务很相似,BeanFactoryCacheOperationSourceAdvisor是一个PointcutAdvisor

    • pointcut是CacheOperationSourcePointcut,引入CacheOperationSourceClassFilter,而matches就通过CacheOperationSource实现
    • advice就通过CacheInterceptor实现了
  • @Validated
    参数验证器走的跟事务不是一个继承体系,它走的是AbstractAdvisingBeanPostProcessor;
    这个类有一个advisor变量,凡是继承这个类,都可以提供一个advisor出来,
    然后在IOC的postProcessAfterInitialization阶段对bean进行判断,
    如果当前存在有advisor,那就判断advisor是否匹配beanClass,用的还是AOP那套AopUtils.canApply,解析beanClass的类所有方法进行使用pointcut进行匹配;
    能够匹配上就可以为当前者beanClass创建代理,使用advisor;
    SpringAop到了proxyFactory这一步都是一样的,在这之前,切面类那套是先收集advisors,然后转统一的advisor,因为切面类收集的advisor中的advice并不都是一个interceptor,所以有了转换advisor一层处理,还有advisorAdapter,
    而AbstractAdvisingBeanPostProcessor,则是直接只创建了一个advisor,然后此时大家后续都是对这个走proxyFactory创建

    • MethodValidationPostProcessor在afterPropertiesSet中初始化了DefaultPointcutAdvisor,pointCut用的是AnnotationMatchingPointcut,那classFiter用的AnnotationClassFilter,methodMatcher是全部匹配MethodMatcher.TRUE,处理@Validated注解的
    • advice就是MethodValidationInterceptor了

AOP概括:

初始化时机
springAOP是通过注解EnableAspectJAutoProxy生效的,这个注解会注入一个AnnotationAwareAspectJAutoProxyCreator beanDefination,从继承结构发现它是一个后置处理器且实现了order,那么会早以一般的后置处理器先实例化,在IOC refresh的registerBeanPostProcessors 步骤触发实例化。

在doCreatebean之前会触发InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation后置处理介入是否产生代理,但是不是所有的bean都会进行创建proxy,比如如果在advisedBeans存在的则不会创建proxy,基础的组件isInfrastructureClass(Advice 、PointCut 、Advisor 、AopInfrastructureBean)或者AspectJPointcutAdvisor类型的切面类不创建代理,所以这边会首次进行收集advisor进行判断是否是切面类;那如何收集呢?

这advisor包括原生的advisor和切面类的advisor的解析:
原生的advisor从beanfactory里面直接获取后,进行初始化;
切面类的advisor进行解析切面类,进行获取beanfactory里面所有的beanNames,然后先进行beanname的合法校验过滤,这边可以进行优化,提高效率,然后开始解析bean是否有aspect注解,有的话则开始构建元数据,因为aspect注解里可以配置single,perthis,pertarget
如果是单例会委托给BeanFactoryAspectInstanceFactory去解析切面类,怎么解析呢,
先获取切面类里面非pointcut的方法,然后进行排序(按照5种通知类型排序)
然后检测方法上有哪些通知类型注解,没有则return,有则进行解析封装为advisor,,同时里面会构建相应五种advice
解析完方法还会解析declareparent属性的注解,也是封装advisor,单例会进行缓存这些advisors增强器,
而非单例的解析是不会缓存advisors增强器,每次都是通过PrototypeAspectInstanceFactory解析切面类,不过两者都会缓存解析的factory

缓存了advisors后,在进行初始化之后AbstractAutoProxyCreator#postProcessAfterInitialization会触发代理的创建,
还有AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization(这个是另外的继承体系,MethodValidationPostProcessor,AsyncAnnotationBeanPostProcessor)
如果之前在postProcessBeforeInstantiation有收集过advisor,直接从缓存中获取,判断是否有必要进行创建代理;

需要创建代理的时候,从缓存的advisor中与当前的beanClass进行匹配得出候选的advisor,候选步骤:
advisor匹配分两种,一种引介增强IntroductionAdvisor,一种常规增强PointcutAdvisor;
IntroductionAdvisor的匹配直接通过classFilter进行matches
PointcutAdvisor的匹配则是先通过classFilter匹配,如果通过则继续用pointCut的MethodMatcher进行匹配,这边的matcher分两种,
一种是introductionAwareMethodMatcher,切入点表达式的匹配,
另种则是通过常规注解,比如spring cache,注解的事务匹配,
获取到beanClass所有方法,循环它们,只要匹配到一个方法立即返回;
获取到所有候选的advisor后,如果有AspectJPointcutAdvisor,则会添加一个ExposeInvocationInterceptor;
然后进行所有advisor的排序,排序一种是写注解order,一种注解本身里面有order属性,都是从低到高的排序;同个切面类增强顺序是一致的,然后同个切面类多个相同的增强则是通过字母顺序排的

有了匹配的advisor,开始创建代理,代理类型的选择:
先判断是否是强制cglib(isProxyTargetClass=true),如果没有(isProxyTargetClass=false),则继续判断当前bean的beanDefination里面是否有代理属性preserveTargetClass=true,如果还是么有,则开始判断是否配置了回调接口(InitializingBean,DisposableBean,Closeable,AutoCloseable,Aware),存在则设置proxyTarget=true;
如果都是正常的接口,则不设置ProxyTargetClass,否则设置为true;

封装advisor
先获取通用的commonInterceptors,添加到之前缓存好的advisor里面
然后对这些全部的advisor进行迭代,把这些advisor进行封装为DefaultPointcutAdvisor,因为这里面有包括了本来就是advisor,还有MethodInterceptor类型,MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice,这三种advice需要进行适配,然后转为DefaultPointcutAdvisor;
创建具体代理模式:
如果isProxyTargetClass=false,则创建JdkDynamicAopProxy,否则,判断targetClass如果是接口,则也为JdkDynamicAopProxy,然后获取对应的proxy;
jdk通过Proxy.newProxyInstance代理接口生成proxy;
如果是cglib,则进行创建回调的DynamicAdvisedInterceptor,设置到Enhancer里面

JDK是通过JdkDynamicAopProxy#invoke方法进行触发的,
为当前的method,进行匹配advisor;这边也是分IntroductionAdvisor,PointcutAdvisor进行匹配的,引介是通过getClassFilter,PointcutAdvisor则通过MethodMatcher,匹配到advisor后,如果advisor中的advice是MethodInterceptor类型不用转换,如果是比如AfterReturningAdvice,MethodBeforeAdvice,ThrowsAdvice,需要经过适配器获取对应的getInterceptor,组装成链
然后通过索引下标-1开始迭代这个链

  • 事务概括

时机触发:
事务生效有两种,一种是通过注解enableTransaction开启,一种是通过切点表达式配置
注解的方式开启会注入一个InfrastructureAdvisorAutoProxyCreator(如果已经存在了AnnotationAwareAspectJAutoProxyCreator,则不会注入InfrastructureAdvisorAutoProxyCreator),这里面只是多一个判断isEligibleAdvisorBean类型实现方法,判断role注解,
同时还导入一个ProxyTransactionManagementConfiguration,配置则是注入了BeanFactoryTransactionAttributeSourceAdvisor(这个就是原生的advisor),advisor由advice和pointcut构成的,所以事务中的advice就是TransactionInterceptor充当,而注解方式的pointcut就是TransactionAttributeSourcePointcut来充当,methodMatch通过AnnotationTransactionAttributeSource充当,而attributeSource又是委托给 SpringTransactionAnnotationParser进行解析。
所以配置又会注入了attributesource和interceptor,interceptor依赖tm和attributesource
attributesource就是defination,定义事务的基础属性,隔离级别,时间,事务管理器,传播行为,异常等等

所有切点配置引入方式按照注解方式一样,也是构造interceptor即advice,
然后依赖tm和atrributesource, attributesource就可以配置正则表达式匹配相应需要事务支持的方法
执行过程
获取attributeSource,Tm :
事务入口通过interceptor进入的,然后通过attributesource获取attribute,这样具备了事务的基本属性,隔离级别,时间传播行为等等
然后从beanfactory里面获取tm
创建新事务:
如果tm存在,则先从tm里面获取到ConnectionHolder,它里面有个transactionActive判断当前线程事务是否存在的
如果不存在事务,则根据传播行为是否开启新事务
开始判断传播行为不能是PROPAGATION_MANDATORY,这是要求必须有事务,否则抛出异常
PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED这三个都需要开启新事务
先获取数据库连接,然后关闭自动提交,设置连接的隔离级别,超时时间。就可以激活事务状态transactionActive=true
将链接绑定到当前线程中;
如果是已经存在事务了:
根据隔离级别PROPAGATION_NOT_SUPPORTED,PROPAGATION_REQUIRES_NEW,这两个是先挂起事务
PROPAGATION_NEVER这个是抛出异常,表示当前不能有事务存在
PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. 这两个则是沿用当前事务,但是要保持两个事务隔离级别和只读事务一致
PROPAGATION_NESTED,如果支持保存点,则创建保存点,否则开启新事务

执行业务逻辑invocation.proceedWithInvocation();
如果抛异常,则根据异常类型是否匹配进行回滚,如果没匹配则进行提交,在提交里面还会判断是否需要回滚
最后进行commit;

  • 术语

    • joinpoint 连接点
      目标对象的所属类中,定义的所有方法
    • Pointcut:切入点
      那些被拦截 / 被增强的连接点,从连接点中进行选择某些点即选某些方法作为切入点
    • Advice:通知
      增强的逻辑,对切入点进行增强的逻辑
      通知还有分类型:
      Before 前置通知, After 后置通知 ,AfterReturning 返回通知, AfterThrowing 异常通知
      Around 环绕通知
    • Aspect:切面
      Aspect 切面 = PointCut 切入点 + Advice 通知
    • Weaving:织入
      将 Advice 通知应用到 Target 目标对象,进而生成 Proxy 代理对象的过程
    • Introduction:引介
    • 在不修改原有类的代码的前提下,在运行期为原始类动态添加新的属性 / 方法
  • 关键图

    在这里插入图片描述
    继承图
    在这里插入图片描述

  • AOP底层原理

    • @EnableAspectJAutoProxy 注解有啥作用
      这个是启动AOP功能的注解,从enable开头可以看出,里面肯定有导入类AspectJAutoProxyRegistrar, 这个类是处理BeanDefinitionsd的注入,里面注入了一个AnnotationAwareAspectJAutoProxyCreator 的BeanDefinitions ,然后注入后,接着去判断这个注解里面的属性,然后放入到这个BeanDefinitions的propertyValues里面
      • AnnotationAwareAspectJAutoProxyCreator 类初始化时机
        这个是BeanPostProcessor,看继承图,它还实现排序接口Ordered,那意味它优先普通的后置处理器先初始化,在IOC中的refresh-registerBeanPostProcessors 方法里面先初始化实现PriorityOrdered的BeanPostProcessor,再初始化实现Ordered的BeanPostProcessor,其中通过beanFactory.getBean进行初始化的
    • 增强器的构建
      • AbstractAutoProxyCreator #postProcessBeforeInstantiation (InstantiationAwareBeanPostProcessor)做了啥事情
        • 触发时机
          这个方法是在doGetBean-createBean-InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation - doCreateBean,在对象实例化之前,AOP在这边进行触发
        • 做了哪些事情
          1.标记过的beanclass不能被增强:如果当前bean被增强过,则不会再次增强:通过判断是否存在advisedBeans集合中
          2 哪些类或者接口不能被增强:如果么有被增强过,则判断是否是基础类型Advice,Pointcut,Advisor,AopInfrastructureBean这些接口实现类,这些是基础类型InfrastructureClass,不能被增强;同时如果是切面类且!compiledByAjc,也是不能被增强的
          3.哪些beanName需要被跳过:接着判断是否需要被跳过的bean(shouldSkip), shouldSkip方法(这个方法被AspectJAwareAdvisorAutoProxyCreator类重写了)会处理如下事情
        1. 通过findCandidateAdvisors(这个被AnnotationAwareAspectJAutoProxyCreator 重写了)找到所有候选的增强器,循环他们,如果当前的增强器是AspectJPointcutAdvisor类型且当前beanname与这个增强器的切面名称一样,则跳过不进行增强
        2. 如果上述循环后没找到,则调用父类判断当前beanname是否是ORIGINAL结尾的,是则不进行增强
      1. 如果存在getCustomTargetSource,则会进行创建代理对象,结束IOC的流程
      • 如何找到候选的增强器 findCandidateAdvisors (AnnotationAwareAspectJAutoProxyCreator重写)逻辑是啥
        增强器advisor,就是pointcut+方法体,这些东西就是存在切面类中,所以肯定会去解析所有的切面类,解析过的就不会再解析了
    1. 原生的AdvisorBeans收集:先获取spring原生的advisors, 调用父类的逻辑super.findCandidateAdvisors()
      父类的增强器构建委托给了BeanFactoryAdvisorRetrievalHelperAdapter类,这个原生在事务的AOP里面ProxyTransactionManagementConfiguration配置类有注入一个BeanFactoryTransactionAttributeSourceAdvisor,在这边就会获取到它
      1. 找出beanFactory中实现了Advisor接口的beanName数组,要是发现没有则直接返回;这边缓存了这个cachedAdvisorBeanNames,下次如果有值,则直接使用了,不用重新获取
      2. 如果找到了advisorNames,则依次循环他们,同时先判断是否是合法的beanname(isEligibleBean#AbstractAdvisorAutoProxyCreator,这个可以进行覆写), 然后判断是否是当前正在创建的bean,最后则通过beanFactory.getBean(name, Advisor.class)初始化这个advisor
    2. 切面类的advisor收集:原生的advisor初始化后,则接着收集切面类中的advisor,那肯定要先找出切面类
      1. 委托BeanFactoryAspectJAdvisorsBuilderAdapter进行构建收集,这个类除了使用到beanfactory,还会使用到一个ReflectiveAspectJAdvisorFactory(这里面有5种那个通知类型,你懂得)
      2. 收集所有的beanNames:获取beanfactory中所有的beanname,是所有,然后判断isEligibleBean(这个被BeanFactoryAspectJAdvisorsBuilderAdapter重写了,底层还是调用了AnnotationAwareAspectJAutoProxyCreator类里面的isEligibleAspectBean)是否合格的bean,以目前测试来看,是都符合的,他里面可以设置过滤模式includePatterns(正则表达式),进行结合判断是否是个合格的bean,不符合继续循环
      3. 判断是否有对应的classType:合格后,则通过beanFactory再获取这个bean的类型,这边都么有进行任何初始化,如果么有获取到类型,则continue
      4. 切面类判断:通过判断当前beanType(上面从beanfactory里面获取的,根据beanName)使用了注解Aspect,以及不是被ajc编译过(就是属性不包含ajc$,被AspectJ 编译过的,是含有相应的痕迹,它们是不能被spring aop利用的),那就是切面类了
      5. 构建AspectMetadata:根据beanType和beanName构建出AspectMetadata,这里面做很多事情
        1. 再次判断是否是切面类
        2. 根据注解@Aspect里面的属性value,进行构造PerClauseKind,如果么有值默认是SINGLETON,有值的话,则根据值,构建比如PERTHIS,PERTARGET等等;
        3. 如果是SINGLETON,perClausePointcut为TruePointcut,否则为AspectJExpressionPointcut或者ComposablePointcut
      6. 分单例非单例处理增强器
        1. 单例bean的切面
          构建BeanFactoryAspectInstanceFactory(这个里面又会构建一个aspectMetadata),传入给ReflectiveAspectJAdvisorFactory,通过它去收集advisor,收集后,如果是单例,则放入advisorsCache,否则不缓存,只缓存aspectFactoryCache,就是BeanFactoryAspectInstanceFactory,进行获取增强器getAdvisors,那如何获取呢?
          1. 备选advisor方法构建:根据aspectMetadata进行获取到当前的class,然后通过反射获取类中所有方法,过滤出不含包注解Pointcut方法,然后排序,排序的时候,如果方法上面有通知类型的注解,则按照这个顺序的Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),如果有相同的多个通知类型,继续按照方法名称排序,如果么有通知类型的方法,则也是按照方法名称排序,作为备选的advisorMethod
          2. pointCut构建:构建AspectJExpressionPointcut,先看方法上是否有那6种注解,before,after等等,有则可以构建,没有则continue,
          3. AdvisorImpl封装,通知类型初始化:有了切入点,有了方法体,那就可以构建增强器了InstantiationModelAwarePointcutAdvisorImpl;同时这边还初始化了具体通知类型,就是那5种注解,比如AspectJAroundAdvice,AspectJMethodBeforeAdvice,AspectJAfterAdvice,AspectJAfterReturningAdvice,AspectJAfterThrowingAdvice等等,以及方法参数的绑定
          4. 属性注解@DeclareParents增强器器收集 :方法的增强器构建结束后,会开始处理切面类中属性的增强器收集,构建DeclareParentsAdvisor,这个目前没接触,后面在看看 ? ? 在这个类处理ReflectiveAspectJAdvisorFactory
          5. 缓存advisors:增强器收集后,如果当前bean是单例,则将增强器classAdvisors放入advisorsCache,下次直接从缓存获取而不再重新解析;否则缓存BeanFactoryAspectInstanceFactory进入aspectFactoryCache
        2. 非单例
          非单例构建的是PrototypeAspectInstanceFactory,只是不会缓存收集好的classAdvisors,下次进来要重新解析当前切面类收集增强器,但是不会在重新构建MetadataAwareAspectInstanceFactory,走缓存获取,所以同样会缓存PrototypeAspectInstanceFactory进入aspectFactoryCache
    • Bean如何被AOP代理

      • AbstractAutoProxyCreator #postProcessAfterInitialization 初始化时机

        1. 上面说过AnnotationAwareAspectJAutoProxyCreator 是个后置处理器,在doCreateBean的最后阶段即初始化阶段,createBeanInstance—applyMergedBeanDefinitionPostProcessors—populateBean—initializeBean(这里面的最后一个阶段applyBeanPostProcessorsAfterInitialization)进行了AOP的逻辑处理,调用AbstractAutoProxyCreator#wrapIfNecessary
      • wrapIfNecessary 做了啥

      1. 判断是否要被增强的bean
        如果targetSourcedBeans存在,则不进行处理;
        如果在上面阶段被标记为跳过的bean,不进行处理(放在了advisedBeans Map 里面为false );
        同时这边也会进行基础类型的判断isInfrastructureClass和是否跳过shouldSkip(这里面有advisors的收集,收集只是为了判断advisors的切面类名getAspectName是否与当前的beanName相同,相同则跳过),因为有可能上个阶段不是必须经历的,那在这边要有相同的处理
      2. 为当前的bean进行匹配增强器
        1. 获取增强器:获取收集好的候选增强器candidateAdvisors
        2. 匹配当前class的增强器:为当前的beanClass进行应用合适的增强器findAdvisorsThatCanApply
          1. 引介增强器匹配:循环增强器列表,先匹配引介增强IntroductionAdvisor(前面有收集引介增强的增强器,这边才有的判断),如果是,则调用它的ClassFilter进行matches当前的class
          2. 常规方法增强器匹配:接着判断PointcutAdvisor,之前我们构建的是InstantiationModelAwarePointcutAdvisorImpl,然后pointCut是AspectJExpressionPointcut,
            1. 所以获取pointCut的getClassFilter进行判断是否matches当前的class
            2. 过了1,则继续获取MethodMatcher,进行判断是匹配任何方法的TrueMethodMatcher
            3. 如果不是proxyClass,这边会进行判断className是否包含了$$,如果包含了则获取他的父类,不包含直接返回class,如果当前的class有接口,则获取他的接口类
            4. 然后针对收集到的class进行反射所有的方法,然后调用pointcut的matches进行匹配,至于如何匹配,太复杂了,看不懂,留着???
      3. 添加默认DefaultPointcutAdvisor以及排序advisors:收集完匹配的增强器eligibleAdvisors后, 如果增强器列表中有一个增强器是isAspectJAdvice类型,则就在列表首位置增加一个DefaultPointcutAdvisor,传入一个ExposeInvocationInterceptor(类里面有个threadLocal,用于保存当前的MethodInvocation,便于调用过程其他线程可以获取)进行构造,然后sortAdvisors下增强器列表,一个切面类之间是根据advice通知类型排序,如果多个切面类则是通过@order先确定切面类的顺序;在这说明这个类要被增强,放入到advisedBeans标记下为true
      4. 开始创建代理createProxy
        1. 代理模式判断:ProxyFactory构建,判断是否需要强制使用CGlib,如果之前EnableAspectJAutoProxy里面有设置proxyTargetClass为true,就不用进入判断使用哪种代理模式,所以可以看出是配置优先于接口代理,否则进入判断,如果当前的bean的beanDefination有这个preserveTargetClass属性为true,则代理;否则再进行接口的判断,接口实现需要排除配置接口的判断,如InitializingBean,DisposableBean,Closeable,AutoCloseable,Aware以及内部语言接口名称groovy.lang.GroovyObject等等,这些接口不能算,如果排除了接口还有正常接口,收集它们进入interfaces,同时不设置setProxyTargetClass为True,否则设置它为true,这个如果是true创建代理会直接创建cglibProxy
        2. 构建advisor: 先获取commonInterceptors,可以进行设置,如果有的话,则加入到前面已经收集好的advisor
        3. 封装为真的Advisor,放入ProxyFactory: 通过DefaultAdvisorAdapterRegistry将所有的advisor转换为advisor,因为前面收集好的advsior有很多种类型,如果是真的Advisor类型(比如事务的BeanFactoryTransactionAttributeSourceAdvisor)直接返回继续下一个,MethodInterceptor类型则封装为DefaultPointcutAdvisor,如果是MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice则也封装为DefaultPointcutAdvisor,所有全部都封装为真的advisor类型,放入ProxyFactory
        4. 创建具体代理aopProxy: 通过DefaultAopProxyFactory进行创建代理,config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config),满足这些的前提下(不满足是JDK代理),此时如果targetClass是个接口或者是个代理类则创建JdkDynamicAopProxy,否则使用bjenesisCglibAopProxy
        5. 获取代理getProxy
          1. JdkDynamicAopProxy
            1. 获取完整的代理接口集合:除了上面收集的interfaces,这边判断是否已经收集了SpringProxy,Advised,DecoratingProxy接口,如果没有,则进行添加
            2. 找出接口中有定义了hashcode和equal方法,findDefinedEqualsAndHashCodeMethods,进行标记,等会在执行代理的时候会进行判断是否是调用了这个两个方法
            3. 实例化:Proxy.newProxyInstance(classLoader, proxiedInterfaces, this),传入了InvocationHandler,jdkproxy本身是实现了InvocationHandler接口,最终是cons.newInstance(new Object[]{h}),通过InvocationHandler作为参数
          2. CglibAopProxy
            1. 如果getTargetClass的beanName包含了$$,则需要获取他的superClass,用于enhancer的设置父类
            2. createEnhancer
            3. 设置接口,这步与jdkproxy一样
            4. 组装回调数组callbacks,这个数组就是执行实际方法时会进行拦截的,比如equal,hascode这边有单独的Interceptor,就不会执行DynamicAdvisedInterceptor;设置setCallbackTypes回调类型
            5. 实例化,先enhancer.createClass(),然后实例化,最后设置callbacks
    • Bean被AOP代理后如何执行

      • CglibProxy 不代理private方法VisibilityPredicate#evaluate
        1. cglib的代理是由DynamicAdvisedInterceptor进行进入的,这个是在获取代理的时候设置的callBack放入的
        2. 先判断exposeProxy的设置,如果设置了为true, 则将会将当前代理对象放入aop的上下文即AopContext.setCurrentProxy(proxy)
        3. 获取增强器链getInterceptorsAndDynamicInterceptionAdvice
          1. 先从缓存methodCache获取当前method的缓存,有缓存,直接返回之前构造好的增强器链
          2. 没有缓存,先创建DefaultAdvisorAdapterRegistry,这个适配器有默认的三种MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter,然后获取之前收集好的advisors,循环它们,里面分PointcutAdvisor,IntroductionAdvisor以及其他进行判断
            1. PointcutAdvisor
              1. 通过advisor的getPointcut,获取他的getClassFilter进行匹配当前的actualClass,看下切入点表达式是否匹配
              2. 如果class符合,则获取pointCut的getMethodMatcher进行匹配当前的方法,这边有分IntroductionAwareMethodMatcher引介匹配
              3. 方法匹配上后,获取MethodInterceptor[],通过DefaultAdvisorAdapterRegistry获取当前的advisor的advice,然后判断这个advice是真的MethodInterceptor子类还是具体的通知,如果是具体的通知,则通过适配器获取对应的AdviceInterceptor,比如AfterReturningAdviceInterceptor,MethodBeforeAdviceInterceptor,ThrowsAdviceInterceptor;如果直接是MethodInterceptor,则直接添加
              4. 如果是动态的匹配器MethodMatcher.isRuntime(),则继续封装为InterceptorAndDynamicMethodMatcher
            2. IntroductionAdvisor
              1. 也是先获取IntroductionAdvisor的classFilter匹配当前的actualClass
              2. 然后就开始直接获取advisor的getInterceptors了
            3. 前两种都不是
              1. 直接执行 registry.getInterceptors(advisor);
        4. 如果没有获取到增强器链且方法是public ,则直接反射methodProxy.invoke(target, argsToUse)获取到值
        5. 如果4不满足,则开始创建CglibMethodInvocation,继承了ReflectiveMethodInvocation,这里面会初始化methodProxy,前提是(Modifier.isPublic(method.getModifiers()) &&
          method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
          !AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method);然后调用CglibMethodInvocation的proceed
        6. 执行proceed,调用是super的即ReflectiveMethodInvocation的proceed方法,这个方法里面利用一个下标初始值为-1控制获取增强器链来执行相应的通知方法,如果获取到的interceptorOrInterceptionAdvice是InterceptorAndDynamicMethodMatcher则会进行走它的逻辑,否则就走具体的interceptorOrInterceptionAdvice的invoke方法逻辑
          1. 首先先调用ExposeInvocationInterceptor进行保存当前代理对象上下文
          2. 然后要是有环绕通知,则先调用AspectJAroundAdvice#invoke,这里面先处理参数的绑定argBinding,里面有个argumentBindings,不知道什么阶段设置的??;环绕通知会传入一个MethodInvocationProceedingJoinPoint参数,然后调用在业务方法里面继续调用joinpoint的process方法,这里面会进行ReflectiveMethodInvocation的clone复制一份,然后才调用process,又回去了继续调用链的获取
          3. 接着就到MethodBeforeAdviceInterceptor#invoke,然后先调用AspectJMethodBeforeAdvice#before,后面执行逻辑就相同了处理参数绑定然后通过反射调用业务的before方法;调用后再继续调用MethodInvocation.proceed方法
          4. 接着就到AspectJAfterAdvice#invoke,这个方法是先调用MethodInvocation#proceed方法直接回到了增强器链那边去调用下一个增强器链afterReturning了,然后在finally里面才发起业务逻辑@after注解通知方法的调用,所以@after不管如何都会执行的而且是最后执行
          5. 到了AfterReturningAdviceInterceptor#invoke方法,也先是调用了MethodInvocation#proceed方法,也是直接到增强链那边进行下一个 增强链的判断;然后执行完才会调用业务逻辑@afterReturn注解通知方法的调用
          6. 最后到了AspectJAfterThrowingAdvice#invoke,也是直接调用MethodInvocation#proceed方法,回到增强器链,如果执行过程有异常,判断异常类型是否符合@AfterThrowing方法定义,如果符合则调用@AfterThrowing业务逻辑方法,最终还是会抛出异常;到这边就全部 调用链结束了,
          7. 然后发起真正的业务方法调用,调用结束后,此时倒退到AfterReturningAdviceInterceptor中,执行切面类中@afterReturn注解通知方法的调用;然后再继续回到AspectJAfterAdvice中,执行它的finally方法即@after注解通知方法调用;接着在回到了环绕通知里面,进行环绕通知里面的剩余的业务,最后退回ExposeInvocationInterceptor到方法,回到DynamicAdvisedInterceptor的intercept方法里面
    • jdkProxy

      1. jdkproxy先判断equal,hashcode这个两个方法则不走增强链,如果是method.getDeclaringClass()是DecoratingProxy,Advised这两个类,也不走增强链
      2. 剩下的就跟cglib一样的逻辑了,先判断advised.exposeProxy,然后获取getInterceptorsAndDynamicInterceptionAdvice增强器链,如果为空直接反射调用,不为空构造ReflectiveMethodInvocation进行proceed调用
        在这里插入图片描述

继承图
在这里插入图片描述

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   boolean proxyTargetClass() default false;
   boolean exposeProxy() default false;
}
-------------
AspectJAutoProxyRegistrar 代码:
@Override
   public void registerBeanDefinitions(
   		AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
   	AnnotationAttributes enableAspectJAutoProxy =
   			AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
   	if (enableAspectJAutoProxy != null) {
   		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
   			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
   		}
   		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
   			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
   		}
   	}
   }
  • 事务传播行为

    • REQUIRED 你没有,我开启新事务;你有了,我加入。
    • REQUIRES_NEW 你没有,我开启新事务;你有挂起,我开启新的,结束后释放你的
    • SUPPORTS 你有事务我就加入,没有就没有
    • NOT_SUPPORTED 你有事务,把你挂起
    • MANDATORY 你必须有事务,没事务我不干活,抛出异常
    • NEVER 你必须没有事务,如果你有,则我抛出异常
    • NESTED 没事务,开启新事务,有事务,则判断是否支持保存点,支持则记录保存点,然后子事务出现异常回滚部分即上个保存点(基于同个数据源,如果是分布式,则不适合),不支持保存点会开启新事务
  • 事务核心关键类

    • TransactionDefinition
      事务明细定义,比如事务隔离级别,事务超时时间,读写事务,事务传播行为
      在这里插入图片描述

      1. TransactionAttribute 定义了事务捕捉异常
      2. TransactionTemplate 是个definition,它的构建是需要PlatformTransactionManager
        在这里插入图片描述
    • TransactionStatus 事务状态
      在这里插入图片描述
      在这里插入图片描述

    • PlatformTransactionManager
      在这里插入图片描述
      在这里插入图片描述

  • 事务原理

    • 注解 @EnableTransactionManagement
      proxyTargetClass 看了上面的AOP,这个就很熟悉
      mode有两个值PROXY 代表的确实是运行期增强,但 ASPECTJ 代表的是类加载期增强
      order 事务通知的执行顺序,在advisor会进行设置
      在这里插入图片描述
      1. AutoProxyRegistrar 这个一看就是注入一个BeanDefinition,这个里面主要做了解析注解中的mode和proxyTargetClass属性,然后注入InfrastructureAdvisorAutoProxyCreator,上面的AOP注入的是AnnotationAwareAspectJAutoProxyCreator,如果存在了这个就不会注入事务的InfrastructureAdvisorAutoProxyCreator

      2. InfrastructureAdvisorAutoProxyCreator它是用来筛选基础的adivsor,看下面的isEligibleAdvisorBean方法
        在这里插入图片描述

      3. 导入一个ProxyTransactionManagementConfiguration,这里面注入所有bean都是ROLE_INFRASTRUCTURE

        1. 注入TransactionAttributeSource接口实现类AnnotationTransactionAttributeSource,里面有个通过method和targetClass获取TransactionAttribute即TransactionDefination方法;同时这个实现类,里面会添加三个,其中有个解析类SpringTransactionAnnotationParser会去解析@Transactional
        2. 注入TransactionInterceptor的方法拦截器bean,同时依赖了transactionAttributeSource
        3. 注入BeanFactoryTransactionAttributeSourceAdvisor 增强器依赖transactionAttributeSource和TransactionInterceptor,advisor里面需要有advice和pointcut(pointcut又是需要通过attributeSource去解决的,找到有注解的方法啊);同时这边会设置order,从注解里面的order属性获取值,设置的到advisor里面;而AOP里面的advisor从切面类上的@order注解进行设置,是通过BeanFactoryAspectInstanceFactory里面的getOrder进行处理的
          TransactionInterceptor就是一个advice;
          pointcut在advisor内部声明为TransactionAttributeSourcePointcut;
          切入点的判断是否有写注解Transactional,那肯定是交给了TransactionAttributeSource处理

        在这里插入图片描述

  • 事务Interceptor 执行

    1. TransactionInterceptor#invoke
      ProxyTransactionManagementConfiguration类里面有注入了一个TransactionInterceptor类同时它依赖transactionAttributeSource,既然是interceptor那么在调用业务方法之前就会触发到invoke方法,开始做如下事情:
      1. AttributeSource获取:先获取到TransactionAttributeSource,这个在configuaration时候就注入了,直接获取

      2. TransactionAttribute封装,它是definition:通过TransactionAttributeSource获取当前方法上的事务定义TransactionAttribute 类似advice,
        先判断方法是public;默认是public方法才具有事务语义
        通过annotationParsers解析器去解析当前的method,这边使用SpringTransactionAnnotationParser获取到这个方法上面的注解Transactional,
        然后将注解里面的属性封装TransactionAttribute(RuleBasedTransactionAttribute类)对象,然后就return,
        如果方法上面没有,则继续解析类上是否有,然后有放入缓存,没有也放入缓存null值,方法优先级高于类
        这里面有缓存机制,同时这个方法在wrapIfNecessary方法执行为当前bean找到候选的advisor会触发,因为判断是否是候选,要获取当前类中所有方法,然后判断方法是否匹配,事务就是通过TransactionAttributeSourcePointcut中的methodMathcher匹配,匹配的时候通过TransactionAttributeSource去实现了,所以这边是一开始就缓存了所有类的方法的attributieSource了,执行interceptor只要获取缓存里面即可

      3. txAttr辅助获取tm,因为注解里面有属性可以当下指定tm:获取TransactionManager,里面有很多种获取,其中一种就是从beanFactory.getBean(TransactionManager.class)获取,这就是configuration里面定义的,还有一种通过@Transaction注解里面的value,去beanfactory里面找

      4. 如果是ReactiveTransactionManager 响应式的,这边处理它的逻辑然后直接返回了

      5. 封装tm,txAttr,txStatus,构建TransactionInfo:如果非CallbackPreferringPlatformTransactionManager,开始构建TransactionInfo,这对象包含了tm,txAttr,txStatus,所以这边还少了atrributeStatus
        5.1 TransactionStatus封装
                  5.1.1. 先获取transaction:tm.getTransaction(txAttr)
        tm.doGetTransaction:直接构造DataSourceTransactionObject对象作为Transaction,以dataSource对象作为key,从当前线程中获取一个Map,然后通过key去获取ConnectionHolder,然后设置setConnectionHolder;这边根据当前的tm进行获取,这边要设置是否支持保存点
                  5.1.2. 判断是否已经存在的事务: 当前的DataSourceTransactionObject是否有connetionHolder存在以及它是否被激活transactionActive,则走已经存在的事务逻辑处理

         1. 如果是PROPAGATION_NOT_SUPPORTED,不支持事务,如果存在事务则挂起,怎么挂起呢,
         就是在tm里面把DataSourceTransactionObject的setConnectionHolder为null,
         同时将Datasource去除绑定即在threadlocal里面去掉(如果恢复挂起,就是把这Datasource再绑定回去),
         然后TransactionSynchronizationManager原先设置的事务名称,只读事务属性,事务隔离级别,
         激活事务状态通通封装到对象SuspendedResourcesHolder里面,
         然后将TransactionSynchronizationManager全部设置为初始状态当然要先保存这个source
         2. 如果是PROPAGATION_REQUIRES_NEW,先挂起事务,然后开启新事务,如果出异常则恢复被挂起的事务
         3. PROPAGATION_NESTED, 如果支持保存点,则先构建prepareTransactionStatus,然后创建保存点,
         因为是嵌套事务,所有不是新事务isNewSynchronization(),那么所有属性都是用原先的,
         比如隔离级别,时间等等
        

                  5.1.3 如果是新事务:
                           5.1.3.1. 然后根据事务txAtrr的传播行为PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED这三种为一种类型处理,他们都是没事务则开启一个
                  新事务startTransaction
                  先构造DefaultTransactionStatus,属性赋值是否是个新事务以及newSynchronization,readOnly,刚开始都是新的
                  ----------------------- 接着开始doBegin
                  设置Transaction某些属性-----如果没有connection先获取connection(判断条件!txObject.hasConnectionHolder() ||
        txObject.getConnectionHolder().isSynchronizedWithTransaction()),此时获取到连接就具备了ConnectionHolder;通过DataSource获取连接,这边有个选择数据源切入点
                  然后synchronizedWithTransaction设置为true
                  设置conn的隔离级别和只读事务
                  设置conn的setAutoCommit,如果是true,则设置为false,
                  设置是否执行只读事务的SQL,两个因素决定:Tm里面的enforceReadOnly以及TransactionDefinition的readOnly,
                  然后激活事务transactionActive
                  设置超时时间到connection
                  如果是新连接,则绑定datasource和connectionHolder为map对象到当前线程threadLocal,保存的是一个map,key是datasource对象 ,value是connetionHolder
                  ----------------------- doBegin结束
                  
        接着将setActualTransactionActive,setCurrentTransactionIsolationLevel,setCurrentTransactionReadOnly,setCurrentTransactionName设置到当前线程中,最后initSynchronization----isSynchronizationActive=true
        startTransaction结束,返回DefaultTransactionStatus
                          5.1.3.2. 如果是其他行为,无需开启事务的,则直接构建DefaultTransactionStatus 返回,只是它的transaction是null

                      5.1.4. 最后构造TransactionInfo对象传入tm, txAttr,然后设置status,最后将info设置到当前线程中threadlocal

      6. 然后执行invocation的proceed方法,回到了增强器链中执行下个chain

      7. 异常回滚:如果执行业务方法抛出异常
        通过transactionAttribute判断异常是否在设置范围内,
        在异常设置范围内,通过DefaultTransactionStatus判断是否有保存点,则回滚到保存点,
                      如果是新事务,则通过status获取getTransaction,然后transaction再获取connection进行rollback
                      如果是参与其他事务中,当前if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()),则设置rollbackOnly=true(ResourceHolder里面的
        如果异常不在范围,则继续进行提交事务

      8. 提交事务
        如果TransactionStatus是complete,则无法提交;
        如果defStatus.isLocalRollbackOnly()被标记为回滚,则执行回滚,且unexpectedRollback=FALSE
        如果!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()(这是ResourceHolder里面的),则执行回滚,且unexpectedRollback=TRUE
        否则开始提交(判断status.isNewTransaction()才可以提交意味新事务才行,嵌套和默认传播级别都是原用上一个事务的),tm里面有个prepareForCommit可以进行覆盖自定义逻辑,triggerBeforeCommit,triggerBeforeCompletion,triggerAfterCompletion,triggerAfterCommit,可以TransactionSynchronizationManager.getSynchronizations()进行放入自定义业务逻辑;
        通过DefaultTransactionStatus获取getTransaction,然后再获取connection进行commit

      9. 完成后,开始进行清除上面保存的资源,重置连接,释放连接,最后如果之前有挂起事务,则需要释放

  • 事务回滚异常情况(针对DataSourceTransactionManager)

    • Transaction rolled back because it has been marked as rollback-only
      场景现况:一个PersonDao类中的一个方法调用了另外一个StudentDao里面的方法,然后StudentDao方法抛出了异常被PersonDao方法捕获了。在这里插入图片描述

      开始前的想法:personDao方法能正常的插入数据,StudentDao操作的数据进行回滚
      结果:两个dao都没有插入数据成功,且抛出了未曾想到的异常
      分析

      • 两个方法的事务传播行为都是默认即Propagation.REQUIRED,

      • 当一个 具有事务的方法 如果抛出异常之后且符合transactionAttribute定义的类型,要进行TransactionManager.rollback操作,可以定义unexpectedRollback是否直接抛出异常
        分三种情况操作:
        ①如果有Savepoint进行rollbackToHeldSavepoint。
        ②如果是新事务,则直接进行con.rollback()
        ③如果是参与了上一级的事务中(发生事务嵌套了),
        先判断rollbackOnly(默认false) 或者globalRollbackOnParticipationFailure(默认true) 这两个参数只要有一个true,
        则进行标记connection的rollbackOnly=true,否则不做任何标记处理。
        最后如果failEarlyOnGlobalRollbackOnly(默认false)这个为false,会直接修改unexpectedRollback=false保证不会抛出异常在这里插入图片描述

      • 所以StudentDao这边没设置任何参数且是参与上一级事务,所以标记rollbackOnly=true

      • 当PersonDao进行提交操作的时候,
        在这里插入图片描述
        1.先判断当前事务中的TransactionStatus.rollbackOnly=true,那进行unexpectedRollback=false的TransactionManager.rollback三种情况操作
        2.如果connection为rollbackOnly=true的状态,同时,是同时shouldCommitOnGlobalRollbackOnly=false,那么会进行unexpectedRollback=true的TransactionManager.rollback三种情况操作
        所以PersonDao是新事务,因为它是最外一层同时传播行为是Propagation.REQUIRED;所以它先进行了con.rollback(),然后根据unexpectedRollback直接抛出了异常

    • 解决方案
      1.如果调用第三方法失败就失败,不能影响自己的事务提交,那么从commit方法可以看出,直接修改当前线程中的transactionStatus的rollbackOnly=true,那么就会进入不抛出异常(unexpected=false)的processRollback三种情况进行操作;
      所以在捕获异常后,增加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 就可以改变rollbackOnly

  • Spring事务失效情况

    • 需要进行事务支持的的类没有被spring管理;
      spring 的事务是通过动态代理,所以前提要被spring进行管理,将interceptor进行织入到类中,这样从容器获取bean执行方法的时候才会触发
    • final 方法无法触发事务执行,因为spring是使用动态代理,cglib方式是无法对final方法进行代理
    • 方法内部调用无法触发事务;因为方法内部直接调用,获取的对象不是通过spring进行管理的对象,所以没有interceptor的织入
    • 多线程调用会触发事务,但是不会回滚,所以这边也归入
      spring事务的connection是通过TransactionSynchronizationManager进行操作threadLocal进行绑定的,所以方法内部如果使用多线程操作DB,获取到的connection不是同个,无法进行回滚
      在这里插入图片描述

在这里插入图片描述

  • Spring cache

spring cache的套路与注解的AOP很相似,看下面图

  • EnableCaching注解也是导入两个类,一个AOP,一个配置类
    在这里插入图片描述
    在这里插入图片描述
  • spring caceh核心逻辑
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • @Validated

在上面的AOP依赖的是AbstractAutoProxyCreator,而Validated注解生效的继承的是AbstractAdvisingBeanPostProcessor,这里面是有一个advisor变量,需要为其赋值,然后在postProcessAfterInitialization进行判断当前beanClass是否满足advisor的匹配,如果匹配,则为其创建proxy

  • 主角是这个MethodValidationPostProcessor类,为啥,因为他里面有个注解Validated,

  • 那这个后置处理器在什么时候被注入进来呢,在springWebMvc的自动装配的时候WebMvcAutoConfiguration这个类,会通过@AutoConfigureAfter先导入一个ValidationAutoConfiguration,他里面就会@Bean了一个MethodValidationPostProcessor后置处理器
    在这里插入图片描述

  • MethodValidationPostProcessor 作用

    1. 这个类实现了InitializingBean接口,在初始化的时候,把增强器advisor给初始化了,增强器的是需要切入点pointcut和advice即interceptor, 所以它的pointcut是AnnotationMatchingPointcut,这里面有关键的classFilter(他肯定是判断类上是否有注解),methodMatcher他直接是MethodMatcher.TRUE,所以不用反射类中方法进行逐一判断,advice就是MethodValidationInterceptor
    2. 怎么为bean进行匹配增强器呢: 因为它继承了AbstractAdvisingBeanPostProcessor,它实现了BeanPostProcessor,所以在postProcessAfterInitialization阶段先判断是bean是否是合法bean即通过AopUtils.canApply进行判断advisor是否匹配当前bean。其实就是判断类上是否有Validated注解,然后再根据methodMatcher,因为它的是MethodMatcher.TRUE,所以类上有注解即可
    3. 创建代理:当前的bean能够被advisor匹配后,就开始创建proxyFactory,然后添加这个advisor,进行创建具体的代理模式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • @Async

async是使用AOP的AbstractAdvisingBeanPostProcessor这个继承体系与@Validated一样
通过EnableAsync注解@Bean一个AsyncAnnotationBeanPostProcessor
不过这个postProcessor在setBeanFactory的时候初始化了advisor为AsyncAnnotationAdvisor,
赋值给了AbstractAdvisingBeanPostProcessor类中的advisor变量

  • AsyncAnnotationAdvisor的poinut是AnnotationMatchingPointcut,AnnotationClassFilter作为classFilter
  • advice 就是AnnotationAsyncExecutionInterceptor
    在这里插入图片描述
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值