源码框架-Spring-思维导图

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 小结
Spring Xmind 小结

IOC容器加载过程及Bean生命周期

文中 spring-boot-dependencies 的 version 基于 2.3.6.RELEASE
spring-cloud-dependencies的 version 基于 Hoxton.SR3

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元数据,比如class表示Bean类型、scope表示bean作用域/是否为单例、属性、构造函数参数列表、依赖的bean、lazyInit表示是否是懒加载、bean初始销毁会调用的方法等信息。

Spring IOC容器的具体加载过程

spring的配置方式一般有三种:
1.注解配置
2.xml配置
3.JavaConfig配置

通过main函数创建 spring 高级容器有多种方式:

  1. ClassPathXmlApplicationContext 基于classpath下的xml配置文件创建容器(少见)
  2. FileSystemXmlApplicationContext 从磁盘路径查找 XML 配置文件创建容器(少见)
  3. AnnotationConfigApplicationContext 基于纯注解开发、JavaConfig 配置文件

Spring Boot 根据WebApplicationType来推断选择哪个容器创建,WebApplicationType有以下3种类型:

  1. NONE 非web环境下的启动容器,AnnotationConfigApplicationContext,比如 Spring Cloud 环境下容器通过 BootstrapApplicationListener 添加的id=bootstrap的Spring Boot子容器是这种类型的容器
  2. SERVLET 基于 servlet 的 web 容器,XmlWebApplicationContext、AnnotationConfigWebApplicationContext或者AnnotationConfigServletWebServerApplicationContext,适用于普通的Spring Boot Web项目
  3. REACTIVE 使用响应式的 web 容器, AnnotationConfigReactiveWebServerApplicationContext,在 Spring-Cloud-Gateway 中,基于REACTIVE 环境的启动容器是这种类型

AnnotationConfigApplicationContext 支持从给定的包路径和配置类启动容器,下文以它的构造函数来展开IOC容器的加载过程。

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.准备工作过程中,实例化了一些对象:bean工厂、reader、scanner(这3个是AnnotationConfigApplicationContext的成员变量)

GenericApplicationContext#beanFactory = new DefaultListableBeanFactory()

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

AnnotationConfigApplicationContext#reader = new AnnotatedBeanDefinitionReader(this);

初始化注解模式下bean定义扫描器, 而且注册了很多的创世纪后置处理器,比如:
解析我们配置类的后置处理器ConfigurationClassPostProcessor(它实现了BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor接口,用来处理配置类解析@Configuration、@ComponentScan、@Import等);
AutowiredAnnotationBeanPostProcessor(BeanPostProcessor的实现,解析@Autowired);
AnnotationAwareOrderComparator(Order注解相关)等等。这些后置处理器诞生时间最早,有了它们之后才能有我们添加的普通bean,故名创世纪后置处理器。

AnnotationConfigApplicationContext#scanner = new ClassPathBeanDefinitionScanner(this);

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

下面举例BeanDefinitionReader (Bean定义读取器)、BeanDefinitionScanner (Bean定义扫描器)的简单用法

package com.example.demo;

import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

public class Test {
    /** 
     * @see AnnotatedBeanDefinitionReader
     *   传入 BeanDefinitionRegistry 具有注册Bean定义到注册表的能力
     * 	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
     * 		this(registry, getOrCreateEnvironment(registry));
     *   }
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context);
        //将Teacher.class解析为BeanDefinition
        reader.register(Teacher.class);
        System.out.println(context.getBean("teacher"));
        System.out.println(context.getBean("student"));
        //打印结果
        //com.example.demo.Teacher@2177849e
        //com.example.demo.Student@40cb8df7
    }
}
class Teacher {}
@Component
class Student {}
@ComponentScan("com.example.demo")
class AppConfig {
}

BeanDefinitionReader可以直接把某个类转换为BeanDefinition,并且会解析该类上的注解,它能解析的注解是:@Conditional,@Scope、@Lazy、@Primary、@DependsOn、 @Role、@Description。类上不需要@Component类似注解

package com.example.demo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.stereotype.Component;

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.refresh();
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
        scanner.scan("com.example.demo");
        System.out.println(context.getBean("teacher"));
        //打印结果
        //com.example.demo.Teacher@7b02881e
    }
}
@Component
class Teacher {}

ClassPathBeanDefinitionScanner也能将类解析为BeanDefinition,但它需要通过扫描某个包路径,对包路径下的类进行解析,类上有类似@Component 注解的类,才能解析为一个BeanDefinition

  • 2.注册配置类

将配置类也添加到beanDefinitionMap中,在此之前beanDefinitionMap中只有上一步添加的几个创世纪后置处理器,在此之后beanDefinitionMap中就多了配置类这个beanDefinition。添加到beanDefinitionMap的逻辑看这个方法: DefaultListableBeanFactory#registerBeanDefinition

beanDefinitionMap是我们创建的bean工厂-DefaultListableBeanFactory的成员变量

  • 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,配置类对应为factoryBean。
3.推断构造方法:根据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(初始化后)

后置处理器的九次调用

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

内置后置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后置处理方法   #postProcessBeanDefinitionRegistry
4后置处理方法     #postProcessBeanFactory
// ConfigurationAnnotationProcessor 会走第一步、第四步

5.获取容器中所有的 BeanFactoryPostProcessor
6.先调用BeanFactoryPostProcessor实现了 PriorityOrdered接口的
7.再调用BeanFactoryPostProcessor实现了 Ordered的
8.调用没有实现任何方法接口的 后置处理方法 BeanFactoryPostProcessor#postProcessBeanFactory

拓展点:

其他框架基于spring的后置处理器这一拓展点,整合了自己的技术,这点我们也可以学习借鉴。

BeanFactoryPostProcessor 修改BeanDefinition
BeanDefinitionRegistryPostProcessor 注册BeanDefinition  eg:集成Mybatis,根据mapper接口创建代理对象(源码看MapperScannerConfigurer);dubbo类似。

  • BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 做了什么?
  1. 循环bean定义名称names找到配置类,判断其是完全的配置类还是一个非正式的配置类;
  2. 创建一个配置类解析器对象真正地解析配置类,parser.parse(),把我们扫描出来的类添加到beanDefinition的集合即beanDefinitionMap(@ComponentScan);
  3. 新建一个ConfigurationClassBeanDefinitionReader,把我们解析出来的配置类configClasses(解析出来的配置类)注册到容器中(@Import、@Bean、@ImportResources、ImportBeanDefinition注解)
  • BeanDefinitionRegistryPostProcessor#postProcessBeanFactory 做了什么?

enhanceConfigurationClasses 配置类增强

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

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

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

// 方式1:    
package com.example.demo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

public class Test {
    public static void main(String[] args) {
        new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

@Configuration
@ComponentScan("com.example.demo")
class AppConfig {
    AppConfig() { System.out.println("AppConfig init...");}
    @Bean
    BeanDefinitionRegistryPostProcessor postProcessor() {return new MyBeanDefinitionRegistryPostProcessor();}
}
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    MyBeanDefinitionRegistryPostProcessor() {System.out.println("MyBeanDefinitionRegistryPostProcessor init...");}
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

// 控制台输出
AppConfig init...
MyBeanDefinitionRegistryPostProcessor init...

// 警告
org.springframework.context.annotation.ConfigurationClassPostProcessor enhanceConfigurationClasses
Cannot 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:    加上static修改postProcessor方法
// 控制台输出日志与方式一不同
MyBeanDefinitionRegistryPostProcessor init...
AppConfig init...

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

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

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

重复beanName覆盖原则

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

Snipaste_2022-03-15_22-20-24.png

循环依赖

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

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

三级缓存结构

Map<String,Object> singletonObjects                          // 一级缓存

Map<String,Object> earlySingletonObjects                 // 二级缓存

Map<String,ObjectFactory> 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:内层事务抛异常一定会回滚,外层事务可以通过控制吞不吞内层事务抛的异常来决定是否回滚

异常状态 REQUIRED REQUIRES_NEW NESTED
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)
Snipaste_2022-03-23_14-30-26.png

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
@Component
public 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会发现已经有一个事务,可选择挂起或加入事务;

posted @ 2022-10-20 16:31  Luke!  阅读(475)  评论(0编辑  收藏  举报