目录

  • SpringIOC源码
    • IOC容器加载过程及Bean生命周期
      • BeanFactory和ApplicationContext的区别
      • Spring IOC容器的具体加载过程
      • 简述Bean的生命周期
      • 后置处理器的九次调用
      • BeanDefinition
    • 内置后置PostProcess处理器
      • BeanFactoryPostProcessor的调用过程/配置类的解析过程
      • 配置类@Configuration加与不加的区别
      • 重复beanName覆盖原则
    • 循环依赖
      • 如何解决循环依赖/为什么要有二级缓存和三级缓存
      • Spring三级缓存解决setter方式的循环依赖原理
      • BeanCurrentlyInCreationException
    • 监听器Listener
      • Spring事件监听器的原理
      • Spring是怎样避免读取到不完整的Bean
    • 推断构造方法底层原理
    • SpringAOP底层原理
  • Spring事务
    • Spring事务的7种传播行为
      • 1、PROPAGATION_REQUIRED
      • 2、PROPAGATION_SUPPORTS
      • 3、PROPAGATION_MANDATORY
      • 4、PROPAGATION_REQUIRES_NEW
      • 5、PROPAGATION_NOT_SUPPORTED
      • 6、PROPAGATION_NEVER
      • 7、PROPAGATION_NESTED
      • 8、总结
    • 用法
    • Spring事务不生效
    • Transaction rolled back because it has been marked as rollback-only
    • 切面类内的事务
    • 自定义AOP与声明式事务执行顺序问题

SpringIOC源码

Spring源码大纲 https://www.processon.com/view/link/5f5075c763768959e2d109df

IOC加载流程图 https://www.processon.com/view/link/5f15341b07912906d9ae8642

Spring循环依赖图 https://www.processon.com/view/link/5f1fb2cf1e08533a628a7b4c

Spring Xmind 小结

IOC容器加载过程及Bean生命周期BeanFactory和ApplicationContext的区别

Spring Framework 中文文档

BeanFactory和ApplicationContext的区别就是工厂和4S店的区别

BeanFactory是Bean的工厂,spring的顶层核心接口,没有BeanFactory就没有Bean的存在,工厂只负责按照要求生产Bean,Bean的定义信息,要生产成什么样由下家(ApplicationContext)说了算。

ApplicationContext面向的是用户,所以需要更好的服务用户,不仅要提供Bean和调用工厂去生产Bean还要提供一系列人性化的服务如国际化、加载Bean定义、监听器等等,怎么生成Bean的事交给工厂去做。

但是ApplicationContext也依赖工厂,没有工厂他没有办法提供Bean,没有办法更好的服务用户,所以它需要继承工厂;ApplicationContext 继承自 BeanFactory,但是它不应该被理解为 BeanFactory 的实现类,而是说其内部持有一个实例化的 BeanFactory(DefaultListableBeanFactory)。以后所有的 BeanFactory 相关的操作其实是给这个实例来处理的。DefaultListableBeanFactory也有注册bean定义的能力。

BeanDefinition是bean在spring中的描述,有了BeanDefinition我们就可以创建Bean。

BeanDefinition接口: 顶级基础接口,用来描述Bean,里面存放Bean元数据,比如Bean类名、scope、属性、构造函数参数列表、依赖的bean、是否是单例类、是否是懒加载等一些列信息。

Spring IOC容器的具体加载过程

// spring的配置方式一般有三种:注解配置/xml配置/JavaConfig配置 // 创建spring 容器: ClassPathXmlApplicationContext构造器 / AnnotationConfigApplicationContext构造器 public AnnotationConfigApplicationContext(Class... componentClasses) {   this();                      // 1.准备工作   register(componentClasses);  // 2.注册配置类   refresh();                   // 3.IOC容器刷新}// 1. 这是一个有参的构造方法,可以接收多个配置类,不过一般情况下,只会传入一个配置类。 // 2. 这个配置类有两种情况,一种是传统意义上的带上@Configuration注解的配置类,还有一种是没有带上@Configuration,但是带有@Component,@Import,@ImportResouce,@Service, @ComponentScan等注解的配置类,在Spring内部把前者称为Full配置类,把后者称之为Lite配置类。
  • 1.准备工作,过程中主要实例化的对象

GenericApplicationContext#beanFactory = new DefaultListableBeanFactory()

父类构造函数为spring上下文,实例化了beanFactory:DefaultListableBeanFactory
DefaultListableBeanFactory 是最底层,实现功能最全的BeanFactory

AnnotationConfigApplicationContext#reader = new AnnotatedBeanDefinitionReader(this);

初始化注解模式下bean定义扫描器, 注册了一些创世纪后置处理器,比如:
解析我们配置类的后置处理器ConfigurationClassPostProcessor(它是BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor的实现,用来处理配置类解析@Configuration、@ComponentScan、@Import等);
AutowiredAnnotationBeanPostProcessor(BeanPostProcessor的实现,解析@Autowired);
AnnotationAwareOrderComparator(Order注解相关)

AnnotationConfigApplicationContext#scanner = new ClassPathBeanDefinitionScanner(this);

初始化classPath类型bean定义扫描器,可以用来扫描指定包下所有类,并将符合过滤条件的类(设置this.includeFilters =AnnotationTypeFilter(Component.class))封装成 beanDefinition 注册到容器,适用于没有指定配置类时手动调用scan,不是默认的扫描包对象,可忽略

BeanDefinitionReader 读取
BeanDefinitionScanner 扫描
BeanDefinitionRegistry 注册

拓展点:

BeanFactoryPostProcessor 修改BeanDefinition
BeanDefinitionRegistryPostProcessor 注册BeanDefinition eg:集成Mybatis

  • 2.注册配置类

将配置类注册到beanDefinitionMap中,此时beanDefinitionMap中只有配置类、创世纪后置处理器

  • 3.IOC容器刷新过程-源码debug

AbstractApplicationContext#refresh()

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

Spring解析xml配置文件,将要创建的所有bean配置信息保存起来;javaconfig只刷新该beanFactory包括 beanDefinitionMap和beanDefinitionNames等

invokeBeanFactoryPostProcessors(beanFactory)

执行BeanFactoryPostProcessor 调用BeanFactory的后置处理器,真正的扫描包对象scanner,扫描class,解析成beanDefinition并注册到beanDefinitionMap

registerBeanPostProcessors(beanFactory)

注册Bean后置处理器

finishBeanFactoryInitialization(beanFactory)

实例化所有剩余的(非延迟初始化)单例

beanFactory.preInstantiateSingletons()

获取容器中所有bean定义的名称;
合并BeanDefinition生成RootBeanDefinition;
判断beanDefinition是不是抽象的&&不是单例的&&不是懒加载的;
判断是FactoryBean则创建,SmartFactoryBean可调用工厂方法getobject返回内部对象,不是FactoryBean调用getBean();getBean(&beanName)返回的是FactoryBean,beanDefinitionMap中只有FactoryBean,但单例池最终会生成2个bean

// 核心!!!getBean方法

AbstractBeanFactory#getBean调用#doGetBean  DefaultSingletonBeanRegistry#getSingleton(beanName) 为空,往下调用  DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory) 钩子函数调用AbstractAutowireCapableBeanFactory#createBean调用#doCreateBean,再依次调用   AbstractAutowireCapableBeanFactory#resolveBeanClass 加载类 先加载当前BeanDefinition所对应的class  AbstractAutowireCapableBeanFactory#createBeanInstance 实例化(addSingletonFactory放入缓存)  AbstractAutowireCapableBeanFactory#populateBean 属性注入,填充属性/注入依赖  AbstractAutowireCapableBeanFactory#initializeBean 初始化,执行aware接口中的方法,完成AOP代理,最后把最终生成的代理对象放入单例池,下次getBean时就直接从单例池拿即可 // AbstractAutowireCapableBeanFactory#createBeanInstance,bean的实例化过程:// 使用合适的实例化策略来创建新的实例:工厂方法、构造函数自动注入、简单初始化 1.首先判断BeanDefinition中是否设置了Supplier,如果设置了则调用Supplier的get()得到对象。2.如果没有设置Supplier,检查BeanDefinition中是否设置了factoryMethod,然后调用工厂方法得到对象。@Bean所注解的方法就是factoryMethod,配置类为factoryBean3.推断构造方法:根据class推断构造方法,根据推断出来的构造方法,反射得到一个对象// DefaultSingletonBeanRegistry#getSingleton(beanName)一级缓存二级缓存beanName不存在且标记为正在创建,加锁,取出三级缓存的Bean工厂调用getObject方法拿到单例对象或代理对象,将单例对象添加到二级缓存中,移除三级缓存单例工厂中对应的singletonFactory// AbstractAutowireCapableBeanFactory#addSingletonFactory放入三级缓存InstantiationAwareBeanPostProcessor#postProcessAfterInitialization是后置处理器的扩展点,允许在对象返回之前修改甚至替换bean;如果存在AOP,返回的不是原始的Bean实例,而是实现AOP方法的代理类;只用二级缓存会将AOP中创建代理对象的时机提前,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理;循环依赖发生时提前代理,没有循环依赖代理方式不变,依然是初始化以后代理;有ab对象,getBean(a)在加载b的流程中如果发生了循环依赖,就是说b又依赖了a,我们就要对a执行AOP,提前获取增强以后的a对象,这样b对象依赖的a对象就是增强以后的a了。见https://segmentfault.com/a/1190000023712597

简述Bean的生命周期


1.利用该类的构造方法来实例化得到一个对象(但是如何一个类中有多个构造方法,Spring则会进行选择,这个叫做推断构造方法)

2.得到一个对象后,Spring会判断该对象中是否存在被@Autowired注解了的属性,把这些属性找出来并由Spring进行赋值(依赖注入)

3.依赖注入后,Spring会判断该对象是否实现了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数(Aware回调)

4.Aware回调后,Spring会判断该对象中是否存在某个方法被@PostConstruct注解了,如果存在,Spring会调用当前对象的此方法(初始化前)

5.紧接着,Spring会判断该对象是否实现了InitializingBean接口,如果实现了,就表示当前对象必须实现该接口中的afterPropertiesSet()方法,那Spring就会调用当前对象中的afterPropertiesSet()方法(初始化)

6.最后,Spring会判断当前对象需不需要进行AOP,如果不需要那么Bean就创建完了,如果需要进行AOP,则会进行动态代理并生成一个代理对象做为Bean(初始化后)

后置处理器的九次调用

时机方法入口实现的接口 instanceofeg
实例化前#resolveBeforeInstantiationInstantiation AwareBeanPostProcessorAnnotationAwareAspectJAutoProxyCreator解析aop切面信息进行缓存
实例化-推断构造器#createBeanInstanceSmartInstantiation AwareBeanPostProcessor通过bean的后置处理器进行选举出合适的构造函数对象
实例化后#applyMergedBean DefinitionPostProcessorsMergedBeanDefinition PostProcessor@AutoWired的注解的预解析 可以修改BeanDefinition
实例化后#getEarlyBeanReferenceSmartInstantiation AwareBeanPostProcessor解决循环依赖
填充属性前#populateBeanInstantiationAware BeanPostProcessor用户可以自定义属性注入
填充属性前#populateBeanInstantiationAware BeanPostProcessor可以修改填充属性的值 处理@AutoWired
初始化#initializeBeanBeanPostProcessor例如@PostConstruct
初始化#initializeBeanBeanPostProcessoraop和事务都会在这里生成代理对象
销毁bean容器InitDestroyAnnotationBeanPostProcessor

BeanDefinition

BeanDefinition是Spring顶层核心接口封装了生产Bean的一切原料,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如:

  • class,表示Bean类型
  • scope,表示Bean作用域,单例或原型等
  • lazyInit:表示Bean是否是懒加载
  • initMethodName:表示Bean初始化时要执行的方法
  • destroyMethodName:表示Bean销毁时要执行的方法

内置后置PostProcess处理器BeanFactoryPostProcessor的调用过程/配置类的解析过程

https://www.processon.com/view/link/5f18298a7d9c0835d38a57c0

调用bean工厂的后置处理器 1)BeanDefinitionRegistryPostProcessor(先被执行)              它是能注册BeanDefinition                        的子接口   所有的bean定义信息将要被加载到容器中,Bean实例还没有被初始化2)BeanFactoryPostProcessor(后执行)                           它是修改BeanDefinition 但不能注册BeanDefinition  的父接口所有的Bean定义信息已经加载到容器中,但是Bean实例还没有被初始化修改BeanDefinition 即通过设置bean对象的类型 setBeanClassName 偷天换日1.去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称// 判断是否实现了PriorityOrdered接口的,getBean,调用他的后置处理方法2.去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称// 判断是否实现了Ordered接口的,getBean,调用他的后置处理方法3.去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称// 剩下的没有被处理过的,getBean,调用他的后置处理方法4.去容器中获取BeanDefinitionRegistryPostProcessor,同时实现了BeanFactoryPostProcessor的bean的处理器名称// getBean 调用他的后置处理方法123后置处理方法   #postProcessBeanDefinitionRegistry4后置处理方法     #postProcessBeanFactory// ConfigurationAnnotationProcessor 会走第一步、第四步5.获取容器中所有的 BeanFactoryPostProcessor6.先调用BeanFactoryPostProcessor实现了 PriorityOrdered接口的7.再调用BeanFactoryPostProcessor实现了 Ordered的8.调用没有实现任何方法接口的 后置处理方法 BeanFactoryPostProcessor#postProcessBeanFactory
  • BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 做了什么?
  1. 循环bean定义名称names找到配置类,判断其是完全的配置类还是一个非正式的配置类;
  2. 创建一个配置类解析器对象真正地解析配置类,parser.parse(),把我们扫描出来的类添加到beanDefinition的集合即beanDefinitionMap(@ComponentScan);
  3. 新建一个ConfigurationClassBeanDefinitionReader,把我们解析出来的配置类configClasses(解析出来的配置类)注册到容器中(@Import、@Bean、@ImportResources、ImportBeanDefinition注解)
  • BeanDefinitionRegistryPostProcessor#postProcessBeanFactory 做了什么?

enhanceConfigurationClasses 配置类增强

  • 提前生成配置类单例bean引发的问题

自定义beanFactory后置处理器会在第一步被ConfigurationClassPostProcessor扫描添加到 beanFactory 的BeanDefinitionMap,在第三步时被getBean,再调用自定义beanFactory后置处理器的后置处理方法;如果是在配置类里@Bean注册的自定义beanFactory后置处理器,会getBean(配置类),提前生成配置类,没有通过配置类增强、配置类增强失败。

解决:static关键字修饰@Bean方法返回为BeanPostProcessor、BeanFactoryPostProcessor等类型的方法

// 方式1:    @Configurationclass AppConfig {AppConfig() { System.out.println("AppConfig init...");}    @BeanBeanDefinitionRegistryPostProcessor postProcessor() {return new MyBeanDefinitionRegistryPostProcessor();}}class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {MyBeanDefinitionRegistryPostProcessor() {System.out.println("MyBeanDefinitionRegistryPostProcessor init...");}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}}// 控制台输出AppConfig init...MyBeanDefinitionRegistryPostProcessor init...// 警告org.springframework.context.annotation.ConfigurationClassPostProcessor enhanceConfigurationClassesCannot enhance @Configuration bean definition 'appConfig' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
// 方式2:    @Configurationclass AppConfig {AppConfig() { System.out.println("AppConfig init...");}}@Componentclass MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {MyBeanDefinitionRegistryPostProcessor() {System.out.println("MyBeanDefinitionRegistryPostProcessor init...");}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}}// 控制台输出MyBeanDefinitionRegistryPostProcessor init...AppConfig init...

配置类@Configuration加与不加的区别

配置类加@Configuration的话,@Bean里方法名获取对象,对象只实例化一次

BeanDefinitionRegistryPostProcessor中第4步调用postProcessBeanFactory方法时给配置类创建cglib动态代理,指定配置时不加Configuration也行,但加了@Configuration会根据方法名从单例池拿getBean,这样就有bean和bean之间的引用,而不是重复加载bean
@Configuration为Full配置类,经过enhance增强,所有的@Bean方法都被BeanMethodInterceptor拦截

重复beanName覆盖原则

loadBeanDefinitionsForBeanMethod1、配置类的名字相同,则报错(同名@Component)2、同一个配置类中的@Bean名字相同,则返回true,意思是以先加载的@Bean方法为准3、不同的配置类中的@Bean名字相同,则返回false,意思是可以被覆盖,已后被加载的@Bean方法为准

循环依赖如何解决循环依赖/为什么要有二级缓存和三级缓存

https://note.youdao.com/ynoteshare/index.html?id=01ec86d7955e2c9cd45c1c0e22f07535&type=note&_time=1635692387674

三级缓存结构

Map singletonObjects // 一级缓存

Map earlySingletonObjects // 二级缓存

Map singletonFactories // 三级缓存

一级缓存的作用:存放可用的成品bean;

二级缓存的作用:为了将成熟Bean和纯净Bean分离(未注入属性),避免多线程下读取到不完整的Bean;存放半成品bean,半成品bean即已经调用完构造但是还没有注入属性和初始化;

三级缓存的作用:用来生产半成品的bean,与getbean方法解耦,能解决aop增强下的循环依赖;存放函数接口/钩子函数,函数接口实现创建动态代理调用BeanPostProcessor,即其要加强的aop处理(为了避免重复创建,调用会返回动态代理对象或者原实例,再存储在二级缓存);

真正的解决循环依赖是靠二级缓存,不用三级缓存也可以解决循环依赖,但这样就造成了在实例化后就立马完成代理,违背了最后一步完成代理的原则;

在创建bean的时候,在哪里通过什么方式创建了动态代理:通过BeanPostProcessor创建动态代理,在初始化之后或在出现循环依赖时实例化之后(实例化 -> 属性注入 -> 初始化)

发生循环依赖会用到二级缓存,普通依赖过程只用到一三级缓存

Spring三级缓存解决setter方式的循环依赖原理



为什么Spring不能解决构造器的循环依赖?

从流程图应该不难看出来,在Bean调用构造器实例化之前,一二三级缓存并没有Bean的任何相关信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。

为什么多例Bean不能解决循环依赖?

我们的bean是单例的,而且是字段注入(setter注入)的,单例意味着只需要创建一次对象,后面就可以从缓存中取出来,字段注入,意味着我们无需调用构造方法进行注入。

  • 如果是原型bean,那么就意味着每次都要去创建对象,无法利用缓存;
  • 如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存。

如何进行拓展?

bean可以通过实现SmartInstantiationAwareBeanPostProcessor接口getEarlyBeanReference方法进行拓展

BeanCurrentlyInCreationException

spring的aop代理(包括@Aysnc,@Transactional),一般都是在属性赋值时中调用#postProcessAfterInitialization方法创建的代理对象,这个代理过程是不涉及到循环引用的情况下执行;在循环引用下会提前创建代理对象#getEarlyBeanReference(ab循环依赖,a通过ObjectFactory提前曝光自己,b通过getObject获取到这个提前曝光的a对象填充属性,该earlySingletonReference放进二级缓存,只有循环依赖下才会放入二级缓存),

如果循环引用下提前创建了代理对象,经过initializeBean初始化又产生代理对象(exposedObject与earlySingletonReference两者不等抛BeanCurrentlyInCreationException异常,@Aysnc会发生,@Transactional不会发生);解决方式:加上@lazy

spring循环依赖在 构造器注入下会抛异常BeanCurrentlyInCreationException 可以用基于属性注入

监听器Listener

  • Spring事件体系包括三个组件:事件,事件监听器,事件广播器。基于观察者模式。

事件(ApplicationEvent)负责对应相应监听器,事件源发生某事件是特定事件监听器被触发的原因。事件分为 Spring内置事件自定义事件(继承ApplicationEvent)

事件监听器(ApplicationListener)对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应逻辑。 分为 基于接口(继承ApplicationListener)、基于注解 (@EventListener)

事件广播器(ApplicationEventMulticaster)对应于观察者模式中的被观察者/主题, 负责通知观察者对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。

发布者调用applicationContext.publishEvent(msg),将事件发送给了EventMultiCaster,而后由 EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。

Spring事件监听器的原理

IOC容器刷新接口refresh方法

initApplicationEventMulticaster 创建事件多播器(默认的事件广播器SimpleApplicationEventMulticaster)

registerListeners 把我们的事件监听器名字注册到多播器上,通过多播器进行播发早期事件

finishRefresh 容器刷新,发布刷新事件(ContextRefreshedEvent)

Spring提供的事件机制默认是同步的(SimpleApplicationEventMulticaster#multicastEvent),如果想用异步的可以自己实现ApplicationEventMulticaster接口,并在Spring容器中注册id为applicationEventMulticaster的Bean

Spring是怎样避免读取到不完整的Bean

防止多线程下Spring读取到不完整Bean加了两把锁

一把锁放在getSingleton()方法三级缓存,第二个线程阻塞直到第一个线程把二三级缓存删除完;

一把锁放在getSingleton(,)方法,先从单例池再拿一遍单例对象(double check防重复创建单例bean)

怎么样可以在所有Bean创建完后做扩展代码?

ContextRefreshedEvent/SmartInitializingSingleton

推断构造方法底层原理

Spring的判断逻辑如下:

1.如果一个类只存在一个构造方法,不管该构造方法是无参构造方法,还是有参构造方法,Spring都会用这个构造方法

2.如果一个类存在多个构造方法

a.这些构造方法中,存在一个无参的构造方法,那么Spring就会用这个无参的构造方法

b.这些构造方法中,不存在一个无参的构造方法,那么Spring就会报错

c.如果某个构造方法上加了@Autowired注解,Spring就会用这个加了@Autowired注解构造方法了

SpringAOP底层原理

https://www.processon.com/view/link/5faa4ccce0b34d7a1aa2a9a5

Bean的生命周期 : UserService.class -> 无参构造方法(推断构造方法)-> 普通对象 -> 依赖注入(属性赋值) -> 初始化前 -> 初始化 -> 初始化后 -> 代理对象(UserServiceProxy) -> Bean

如何判断当前Bean对象需不需要进行AOP:

1.找出所有的切面Bean

2.遍历切面中的每个方法,看是否写了@Before、@After等注解

3.如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配

4.如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP

利用cglib进行AOP的大致流程:

1.生成代理类UserServiceProxy,代理类继承UserService

2.代理类中重写了父类的方法,比如UserService中的test()方法

3.代理对象持有普通对象的引用,UserServiceProxy.target = 普通对象

4.调用代理类的test方法 -> 先执行切面逻辑@Before,再执行target.test方法

Spring常见代理创建方式:

1.FactoryBean方式创建单个动态代理:

  • proxyInterfaces指定需增强的接口;
  • target指定需增强的实现类
  • interceptorNames 指定Advice(MethodBeforeAdvice, AfterReturningAdvice)、Interceptor(MethodInterceptor)、Advisor(结合Advice或Interceptor)都行;
  • Advice、Interceptor拦截器的粒度只控制到了类级别,类中所有的方法都进行拦截;Advisor拦截器的粒度达到方法级别,通知者切点分为正则匹配/方法名;需要获取这个代理类

2.autoProxy方式: 根据advisor批量创建自动代理(当Spring发现一个bean需要被切面织入的时候,Spring会自动生成这个bean的一个代理来拦截方法的执行,确保定义的切面能被执行);不需要获取这个代理类

  • BeanPostProcessor手动指定Advice方式,BeanNameAutoProxyCreator指定Advisor,可以使用正则来匹配要创建代理的那些Bean的名字
  • BeanPostProcessor自动扫描Advisor方式,DefaultAdvisorAutoProxyCreator

开启aop:

1.配置类,加入@EnableAspectJAutoProxy注解

2.切面类,加入@Aspect注解,定义一个Pointcut方法(切点:指定哪些类需要被代理),最后定义一系列的增强方法(Advice通知:代理逻辑)

切面类的解析: AspectJAutoProxyRegistrar实现ImportBeanDefinitionRegistrar,在解析配置类到容器时,如果开启@EnableAspectJAutoProxy注解下,通过registerBeanDefinitions方法为我们容器导入beanDefinition;该beanDefinition 是 AnnotationAwareAspectJAutoProxyCreator,继承自AbstractAutoProxyCreator,#findCandidateAdvisors在bean实例化前解析aop切面信息,生成对应的Advisor对象进行缓存

AbstractAdvisorAutoProxyCreator非常强大以及重要,只要Spring容器中存在这个类型的Bean,就相当于开启了AOP,AbstractAdvisorAutoProxyCreator实际上就是一个BeanPostProcessor,所以在创建某个Bean时,就会进入到它对应的生命周期方法中,比如:在某个Bean初始化之后,会调用wrapIfNecessary()方法进行AOP,底层逻辑是AbstractAdvisorAutoProxyCreator #getAdvicesAndAdvisorsForBean 会找到所有的通知器Advisor,然后判断当前这个Bean是否存在某个Advisor与之匹配(根据Pointcut)AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply ,如果匹配就表示当前这个Bean有对应的切面逻辑,需要进行AOP,需要产生一个代理对象

// ProxyFactory产生代理对象{    UserService target = new UserService();    ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAdvisor(new PointcutAdvisor() {        @Override        public Pointcut getPointcut() {            return new StaticMethodMatcherPointcut() {                @Override                public boolean matches(Method method, Class targetClass) {                    return method.getName().equals("test");                }            };        }        @Override        public Advice getAdvice() {            return new MethodInterceptor() {                @Override                public Object invoke(MethodInvocation invocation) throws Throwable {                     System.out.println("before...");                     Object result = invocation.proceed();                     System.out.println("after...");                     return result;                 }            };        }        @Override        public boolean isPerInstance() {            return false;        }    });    UserInterface userService = (UserInterface) proxyFactory.getProxy();userService.test();  }

代理对象执行过程: CglibAopProxy.DynamicAdvisedInterceptor#intercept

1.在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor

2.代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行匹配筛选

3.把和方法所匹配的Advisor适配成MethodInterceptor

4.把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前Method对象、方法参数封装为MethodInvocation对象

5.调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的对应方法

6.按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入invoke()方法

7.直到执行完最后一个MethodInterceptor了,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法

Spring事务

当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。

事务注解@EnableTransactionManagement 为我们的容器导入了添加了两个Bean:

  1. AutoProxyRegistrar 导入的 InfrastructureAdvisorAutoProxyCreator :开启自动代理
  2. ProxyTransactionManagementConfiguration 导入的 BeanFactoryTransactionAttributeSourceAdvisor(Advisor)、AnnotationTransactionAttributeSource(pointcut)、TransactionInterceptor(advice)

事务是基于AOP完成的,判断bean生命周期是否开启aop:找到所有的通知器对象Advisor,判断这个bean是否与Advisor匹配:通过pointcut对象(解析@Transactional注解,匹配逻辑为判断该Bean的类上是否存在@Transactional注解,或者类中的某个方法上是否存在@Transactional注解)、Advice对象(TransactionalInterceptor 代理的逻辑)

该代理对象在执行某个方法时,会再次判断当前执行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配则执行该Advisor中的TransactionInterceptor的invoke()方法,执行基本流程为:

Spring事务的代理对象执行某个方法时的步骤:

1.判断当前执行的方法是否存在@Transactional注解

2.如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接

3.修改数据库连接的autocommit为false,数据库连接放入threadlocal

4.执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql

5.执行完了之后如果没有出现异常,则提交,否则回滚

Spring事务的7种传播行为

https://blog.csdn.net/weixin_39625809/article/details/80707695

事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

1、PROPAGATION_REQUIRED

如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

@Transactional(propagation = Propagation.REQUIRED)public void methodA(){}@Transactional(propagation = Propagation.REQUIRED)public void methodB(){}单独调用A、B方法都会开启一个新的事务A调用B方法、B调用A方法都会加入到同一个事务

2、PROPAGATION_SUPPORTS

如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。

@Transactional(propagation = Propagation.REQUIRED)public void methodA(){}@Transactional(propagation = Propagation.SUPPORTS)public void methodB(){}单独调用B方法不会开启事务A调用B方法,B会加入这个事务

3、PROPAGATION_MANDATORY

如果存在一个事务,支持当前事务。如果没有事务,则抛出异常。

@Transactional(propagation = Propagation.REQUIRED)public void methodA(){}@Transactional(propagation = Propagation.MANDATORY)public void methodB(){}单独调用B方法会抛IllegalTransactionStateException异常A调用B方法,B会加入这个事务

4、PROPAGATION_REQUIRES_NEW

如果存在一个事务,先将这个存在的事务挂起,再开启一个新的事务。如果没有事务则开启一个新的事务。

@Transactional(propagation = Propagation.REQUIRED)public void methodA(){}@Transactional(propagation = Propagation.REQUIRES_NEW)public void methodB(){}单独调用B方法开启事务A方法(外层事务)调用B方法(内层事务),B会开启一个新的事务外层事务回滚,内层事务仍然提交

5、PROPAGATION_NOT_SUPPORTED

总是非事务地执行,并挂起任何存在的事务。

6、PROPAGATION_NEVER

总是非事务地执行,如果存在一个活动事务,则抛出异常。

7、PROPAGATION_NESTED

如果存在一个事务,依赖该事务,作为该事务的子事务。如果没有事务则开启一个新的事务。

@Transactional(propagation = Propagation.REQUIRED)public void methodA(){}@Transactional(propagation = Propagation.NESTED)public void methodB(){}单独调用B方法开启事务A方法(外层事务)调用B方法(内层事务),nested属于子事务,依赖于外层,有单独的保存节点外层事务的回滚可以引起内层事务的回滚,内层事务异常的回滚可导致外层事务的回滚(如果没有吞掉异常)也可不导致外层事务的回滚(吞掉异常),外层事务自行决定是commit还是rollback

8、总结

case1:如果A捕获B的异常,并且未向上抛异常

case2:如果A未捕获B的异常,则默认将B的异常向上抛

REQUIRES_NEW和NESTED:内层事务抛异常一定会回滚,外层事务可以通过控制吞不吞内层事务抛的异常来决定是否回滚

异常状态REQUIREDREQUIRES_NEWNESTED
methodA抛异常 methodB正常均回滚A回滚,B正常提交均回滚
methodA正常 methodB抛异常case1:均回滚抛异常 case2:均回滚case1:A正常提交B回滚 case2:均回滚case1:A正常提交B回滚 case2:均回滚
methodA抛异常 methodB抛异常均回滚均回滚均回滚
methodA正常 methodB正常均提交均提交均提交

用法

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP的本质决定的。如果你在 protected、private或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

@Transactional不做任何配置,默认是对抛出的unchecked异常、Error回滚,checked异常不会回滚,为了让所有异常都会让事务启动可以将 @Transactional配置为 @Transactional(rollbackFor = Exception.class)

Spring事务不生效

  1. 框架不支持:入口的方法必须是public、事务是否在同一个线程里、数据库引擎设置不对数据库不支持事务
  2. 错误使用:只对出现运行期异常(java.lang.RuntimeException及其子类)/Error进行回滚、rollbackFor属性设置错误、异常被catch、错误地传播机制
  3. 代理失效:被final、static关键字修饰的类或方法、将注解标注在接口方法上将无法用CGLIB代理、是否通过代理对象只有代理对象调用方法才能被拦截
@Transactional(propagation = Propagation.REQUIRED)public void methodA(){}public void methodB(){}A方法(事务)调用B方法(没有事务),B方法的异常也会导致AB方法事务的回滚B方法(没有事务)调用A方法(事务),事务失效

Transaction rolled back because it has been marked as rollback-only

Spring的@Transactional 可以注解到方法上或者类上从而开启事务,而正确调用类事务方法是通过容器调用,即@autowird 被注入到其他类中使用,因为此时调用方法会被spring容器的 TransactionInterceptor 拦截器拦截

@Transactional(propagation = Propagation.REQUIRED)public void methodA(){}@Transactional(propagation = Propagation.REQUIRED)public void methodB(){}Propagation.REQUIRED实例,默认事务实例不管是否捕获异常,全部一起回滚A方法(外层事务)调用B方法(内层事务),B方法发现异常了会标记整个事务为roll-back但如果外层方法捕获异常正常退出后执行commit事务,此时发现已经标记异常会出错抛UnexpectedRollbackException部分失败。全局回滚属性为true

解决办法:在catch块中添加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 手动回滚
或者内层事务使用propagation = Propagation.NESTED,从而保证内层异常不会影响外层提交

切面类内的事务

含有@Aspect的类在生命周期的第一个bean后置处理器会被标记不处理,在最后一个bean后置处理器它就不会被代理,故aop切面类本身使用不了声明式事务@Transactional;可以在切面类新写子方法新建事务,或用publisher.publishEvent发布异步事件新建事务

// 判断当前事务是否是新事务TransactionAspectSupport.currentTransactionStatus().isNewTransaction()// @Order默认为@Order(value = Ordered.LOWEST_PRECEDENCE)优先度最低;可以自定义修改切面类的优先级别@Order(value = Ordered.HIGHEST_PRECEDENCE + 1)

自定义AOP与声明式事务执行顺序问题

@SysLog: 自定义AOP,产生系统日志
@Transactional:声明式事务

//某个service方法@SysLog("/testService")@Transactional(propagation = Propagation.REQUIRED)public Result testService(Info info) {}//自定义aop@Aspect@Componentpublic class SysLogAspect{@Around("@annotation(sysLog)")  public Object around(ProceedingJoinPoint point, SysLog sysLog) {            obj = point.proceed();            innerService.do();        }}@Transactional(propagation = Propagation.REQUIRED)public Result innerService(Info info) {}

在controller同时标记@SysLog和@Transactional时候,由于事务注解默认 @EnableTransactionManagement(order=Ordered.LOWEST_PRECEDENCE) 优先级最高,
故先执行事务aop再执行自定义注解aop(先进后出的⚪)

此时自定义注解代码SysLogAspect#around与外层方法testService是同一个事务,around子事务方法#innerService会发现已经有一个事务,可选择挂起或加入事务;