源码框架(源码框架脑图)

  本篇文章为你整理了源码框架(源码框架脑图)的详细内容,包含有源码框架多少钱 源码框架脑图 源码框架下载 开源框架源码 源码框架,希望能帮助你了解 源码框架。

  目录SpringIOC源码IOC容器加载过程及Bean生命周期BeanFactory和ApplicationContext的区别Spring IOC容器的具体加载过程简述Bean的生命周期后置处理器的九次调用内置后置PostProcess处理器BeanFactoryPostProcessor的调用过程/配置类的解析过程配置类@Configuration加与不加的区别重复beanName覆盖原则循环依赖如何解决循环依赖/为什么要有二级缓存和三级缓存Spring三级缓存解决setter方式的循环依赖原理BeanCurrentlyInCreationException监听器ListenerSpring事件监听器的原理Spring是怎样避免读取到不完整的Bean推断构造方法底层原理SpringAOP底层原理Spring事务Spring事务的7种传播行为1、PROPAGATION_REQUIRED2、PROPAGATION_SUPPORTS3、PROPAGATION_MANDATORY4、PROPAGATION_REQUIRES_NEW5、PROPAGATION_NOT_SUPPORTED6、PROPAGATION_NEVER7、PROPAGATION_NESTED8、总结用法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生命周期

  文中 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 高级容器有多种方式:

  ClassPathXmlApplicationContext 基于classpath下的xml配置文件创建容器(少见)

  FileSystemXmlApplicationContext 从磁盘路径查找 XML 配置文件创建容器(少见)

  AnnotationConfigApplicationContext 基于纯注解开发、JavaConfig 配置文件

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

  NONE 非web环境下的启动容器,AnnotationConfigApplicationContext,比如 Spring Cloud 环境下容器通过 BootstrapApplicationListener 添加的id=bootstrap的Spring Boot子容器是这种类型的容器

  SERVLET 基于 servlet 的 web 容器,XmlWebApplicationContext、AnnotationConfigWebApplicationContext或者AnnotationConfigServletWebServerApplicationContext,适用于普通的Spring Boot Web项目

  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 中为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(初始化后)

  后置处理器的九次调用

  
Instantiation AwareBeanPostProcessor

  AnnotationAwareAspectJAutoProxyCreator解析aop切面信息进行缓存

  
SmartInstantiation AwareBeanPostProcessor

  通过bean的后置处理器进行选举出合适的构造函数对象

  
InstantiationAware BeanPostProcessor

  可以修改填充属性的值 处理@AutoWired

  
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 做了什么?

  
循环bean定义名称names找到配置类,判断其是完全的配置类还是一个非正式的配置类;

  创建一个配置类解析器对象真正地解析配置类,parser.parse(),把我们扫描出来的类添加到beanDefinition的集合即beanDefinitionMap(@ComponentScan);

  新建一个ConfigurationClassBeanDefinitionReader,把我们解析出来的配置类configClasses(解析出来的配置类)注册到容器中(@Import、@Bean、@ImportResources、ImportBeanDefinition注解)

  
自定义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方法为准

  

 

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

  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

  
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方法

  
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

  
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:

  AutoProxyRegistrar 导入的 InfrastructureAdvisorAutoProxyCreator :开启自动代理

  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

  

 

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

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

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

  
case1:均回滚抛异常 case2:均回滚

  case1:A正常提交B回滚 case2:均回滚

  case1:A正常提交B回滚 case2:均回滚

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

  虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP的本质决定的。如果你在 protected、private或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
 

  默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

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

  Spring事务不生效

  框架不支持:入口的方法必须是public、事务是否在同一个线程里、数据库引擎设置不对数据库不支持事务

  错误使用:只对出现运行期异常(java.lang.RuntimeException及其子类)/Error进行回滚、rollbackFor属性设置错误、异常被catch、错误地传播机制

  代理失效:被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) 优先级最。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: