Spring源码解读笔记

第一部分 Spring概述

1. Spring简介

Spring 是分层的轻量级开源框架,以IoC和AOP为内核,提供展现层SpringMVC和业务层事务管理等众多企业级应用技术,可以整合众多著名第三方框架和类库,成为使用最多的Java EE企业应用开源框架。

2. Spring优势
  • 解耦合,简化开发:提供IoC容器,将对象间的依赖关系交由Spring控制,避免硬编码造成程序耦合,用户可以更专注于上层的应用。
  • AOP编程支持:提供AOP功能,方便进行面向切面编程。
  • 声明式事务支持:可以通过声明式事务灵活进行事务管理,提高开发效率和质量。
  • 方便集成各种开源框架:提供了对各种优秀框架(Struts、Hibernate、Quartz等)的支持。
  • 降低JavaEE API的使用难度:对JavaEE API(JDBC、JavaMail、远程调用等)进行封装,使用难度降低。
3. Spring的核心结构

Spring是一个分层清晰并且依赖关系、职责定位明确的轻量级框架,主要包括几大模块:数据处理模块、Web模块、AOP/Aspects模块、Core Container模块和Test模块。

在这里插入图片描述

  • Spring核心容器(Core container):Spring框架最核心的部分,管理着Spring应用中bean的创建、配置和管理,提供DI功能。
  • 面向切面编程(AOP/Aspects):Spring对面向切面编程提供支持,可以帮助应用对象解耦合。
  • 数据访问与集成(Data Access):Spring的JDBC和DAO模块封装了大量模版代码,可以使得数据库代码变得简洁,让开发者更专注于业务,还可避免数据库资源释放失败而引起的问题。AOP为数据访问提供了事务管理服务,同时还对ORM进行集成。
  • Web模块:提供了SpringMVC框架给Web应用,还提供了多种构建和其他应用交互的远程调用方案。
  • Test模块:集成了Junit,使得开发者能够更方便的进行测试。

第二部分 核心思想

注意:IoC和AOP并不是Spring提出,在Spring之前就已经存在,只不过更偏向于理论化,Spring只是在技术层次把这两个思想做了非常好的实现。

1. IoC

1.1 什么是IoC?

IoC全称IoC Inversion of Control(控制反转),它是一个技术思想,不是一个技术实现。

描述的事情:Java开发领域对象的创建、管理的问题

传统开发方式:比如类A依赖于类B,通常会在类A中new一个B的对象

IoC思想下开发方式:不用自己new对象,而是由IoC容器去实例化对象并管理它,开发者需要哪个对象,直接从IoC容器中取即可

为什么叫做控制反转?

控制:指对象的创建(实例化、管理)的权利

反转:控制权交给IoC容器

在这里插入图片描述

1.2 IoC解决了什么问题

IoC解决了对象之间的耦合问题

在这里插入图片描述

1.3 IoC和DI的区别

DI全称Dependancy Injection(依赖注入)

IoC和DI描述的是同一件事,只不过角度不一样

在这里插入图片描述

2. AOP

2.1 什么是AOP

AOP全称Aspect oriented Programming(面向切面编程)

AOP是OOP的一个延续,虽然有了OOP的存在,但是有一些情况是OOP处理不了的,因此AOP出场,AOP独辟蹊径提出横向抽取机制,将横切逻辑代码和业务逻辑代码分离。

在这里插入图片描述

代码拆分容易,但是如何在不改变原有业务逻辑的情况下,悄无声息的把横切逻辑代码应用到原有的业务逻辑中,达到和原来一样的效果却比较难。

2.2 AOP解决了什么问题

在不改变原有业务逻辑情况下,增强横切逻辑代码,从根本上解耦合,避免横切逻辑代码重复。

2.3 为什么叫做面向切面编程

【切】:指横切逻辑,原有业务逻辑代码不能动的情况下,只能操作横切逻辑代码,所以面向横切逻辑。

【面】:横切逻辑代码往往影响的是很多方法,每一个方法如同一个点,点点成面。

第三部分 手写实现IoC和AOP

1. 银行转账案例问题分析

在这里插入图片描述

  1. 问题一:service层实现类在使用dao层对象时,直接在TransferService中通过AccountDao accountDao = new AccountDaoImpl获得dao层对象,然而new关键字却将service层和dao层实现类耦合在一起,如果技术架构发生变动,dao层的实现要使用其他技术,切换成本太大,每一个new的地方都需要修改源代码,重新编译,面向接口开发的意义将大打折扣。
  2. 问题二:service层代码没有进行事务控制,如果转账过程中出现异常,将导致数据错乱,后果很严重。
2. 问题解决思路
  • 针对问题一解决方案:把类的全限定名配置在xml中,通过工厂模式,加载配置文件并进行解析,通过反射技术,生产对象。
    • 更进一步,声明一个变量并提供set方法,在反射的时候将所需要的对象注入进去,完成依赖关系维护。
  • 针对问题二解决方案:(1)dao层让两次update操作使用同一个connection对象连接,可以给当前线程绑定一个connection,和当前线程有关系的数据库操作都是用这个connection;(2)把事务控制添加在service层。通过动态代理的方式,获取到service接口的代理类,在进行原有逻辑调用前后添加开启事务,事务提交,异常时事务回滚等;(3)servlet层通过代理工厂获取service的委托对象。
3.手写IoC框架

注解实现Spring IOC与事务控制:https://blog.csdn.net/qq_35983669/article/details/107287697

第四部分 Spring IoC的应用

1.Spring IoC基础

在这里插入图片描述

1.1 BeanFactory与ApplicationContext区别

BeanFactory是Spring框架中IoC容器的顶层接口,只是用来定义一些基础功能和基础规范,而ApplicationContext是它的一个子接口,ApplicationContext具备BeanFactory提供的全部功能。

通常称BeanFactory为Spring IoC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,比如国际化支持和访问(xml,java配置类)等。

在这里插入图片描述

启动IoC容器的方式

  • Java环境下启动IoC容器

    • ClassPathXmlApplicationContext:类的根路径下加载配置文件(推荐使用)
    • FileSystemXmlApplicationContext:磁盘路径下加载配置文件
    • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
  • Web环境下启动IoC容器

    • 从xml启动容器,在web.xml中配置

      <!DOCTYPE web-app PUBLIC
      	"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
      	"http://java.sun.com/dtd/web-app_2_3.dtd" >
      <web-app>
      	<display-name>Archetype Created Web Application</display-name>
      	<!--配置Spring ioc容器的配置⽂件-->
      	<context-param>
      		<param-name>contextConfigLocation</param-name>
      		<param-value>classpath:applicationContext.xml</param-value>
      	</context-param>
      	<!--使⽤监听器启动Spring的IOC容器-->
      	<listener>
      		<listener-
      class>org.springframework.web.context.ContextLoaderListener</listener-
      class>
      	</listener>
      </web-app>
      
    • 从配置类启动容器

      <!DOCTYPE web-app PUBLIC
      	"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
      	"http://java.sun.com/dtd/web-app_2_3.dtd" >
      <web-app>
      	<display-name>Archetype Created Web Application</display-name>
      	<!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
      	<context-param>
      		<param-name>contextClass</param-name>
      		<param-
      value>org.springframework.web.context.support.AnnotationConfigWebAppli
      cationContext</param-value>
      		</context-param>
      
      	<!--配置启动类的全限定类名-->
      	<context-param>
      		<param-name>contextConfigLocation</param-name>
      		<param-value>com.lagou.edu.SpringConfig</param-value>
      	</context-param>
      	<!--使⽤监听器启动Spring的IOC容器-->
      	<listener>
      		<listener-
      class>org.springframework.web.context.ContextLoaderListener</listener-
      class>
      	</listener>
      </web-app>
      

1.2 纯xml模式

  • xml文件头

    <?xml version="1.0" encoding="UTF-8"?>
    	<beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    	https://www.springframework.org/schema/beans/spring-beans.xsd">
    
  • 实例化Bean的三种方式

    • 方式一:使用无参构造函数

      默认情况下,会通过反射调用无参构造函数来创建对象。如果类中没有无参构造函数,将创建失败。

      <!--配置service对象-->
      <bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
      </bean>
      
    • 方式二:使用静态方法创建

      在实际开发中,我们使用的对象有时候并不是直接通过构造函数就可以创建处理,它可能在创建的过程中会做很多额外的操作。此时会提供一个创建对象的方法,恰好这个方法是static修饰的方法,即是这种情况。

      <!--使⽤静态⽅法创建对象的配置⽅式-->
      <bean id="userService" class="com.lagou.factory.BeanFactory"
      factory-method="getTransferService"></bean>
      

      方式三:使用实例化方法创建

      此种方法和方式二类似,区别是用于获取对象的方法不再是static修饰的,而是类中的普通方法。此种方式比静态方法创建的使用几率高一些。

      <!--使⽤实例⽅法创建对象的配置⽅式-->
      <bean id="beanFactory"
      class="com.lagou.factory.instancemethod.BeanFactory"></bean>
      <bean id="transferService" factory-bean="beanFactory" factory-
      method="getTransferService"></bean>
      
  • Bean的生命周期

    • 作用范围的改变:在Spring框架管理Bean对象的创建时,Bean对象默认是单例,但是它支持配置的方式改变作用范围。

    • 不同作用范围的生命周期

      单例模式:singleton

      对象出生:创建容器是,对象就被创建

      对象活着:容器在,对象就一直存在

      对象死亡:销毁容器,对象就被销毁

      一句话总结:单例模式的bean对象生命周期与容器相同

      多例模式:prototype

      对象出生:使用对象时,创建新的对象实例

      对象活着:对象在使用,就一直存在

      对象死亡:对象长时间不用时,被java的垃圾回收器回收

      一句话总结:多例模式的bean对象,spring只负责创建,不负责销毁

  • Bean的标签属性

    id属性: bean的唯一标识,一个标签内部,标识必须唯一。

    class属性: bean对象的全限定类名。

    name属性: bean的名称,多个名称用空格分隔。

    factory-bean属性: 用于指定创建当前bean对象的工厂bean的唯一标识。指定此属性后class失效

    factory-method属性: 用于指定创建当前bean对象的工厂方法,配合factory-bean属性使用,class属性失效。

    scope属性: bean的作用范围,默认singleton。需使用多例模式时,可配置为prototype。

    init-method属性: bean对象的初始化方法,会在bean对象装配后调用。必须是无参方法。

    destory-method属性: bean对象的销毁方法,会在bean对象销毁前执行。scope为singleton时起作用。

  • DI依赖注入的xml配置

    • 依赖注入分类

      • 按照注入的方式分类

        构造函数注入: 利用带参构造函数实现对类成员的数据赋值。

        set方法注入: 通过类成员的set方法实现数据的注入。(使用最多)

      • 按照注入的数据类型分类

        基本数据类型和String

        注入的数据类型是基本数据类型或者是字符串类型的数据。

        其它Bean类型

        注入的数据类型是对象类型,这个对象时要求出现在IoC容器中的。

        复杂类型(集合类型)

        注入的数据类型是Array,List,Set,Map,Properties中的一种类型。

    • 依赖注入的配置实现之函数注入

      利用构造函数实现对类成员的赋值。

      使用要求:类中提供的构造函数参数个数必须和配置的参数个数一直,且数据类型匹配。

      注意:当无参构造时,则必须提供构造函数参数的注入,否则会报错。

      使用构造函数注入时,涉及的标签时constructor-arg,标签有如下属性:

      name: 用于给构造函数中指定名称的参数赋值。

      index: 用于给构造函数中指定索引位置的参数赋值。

      value: 用于指定基本数据类型或者String类型的数据。

      ref: 用于指定其它Bean类型的数据。

    • 依赖注入的配置实现之set方法注入

      利用字段的set方法实现赋值的注入方式。实际开发中使用最多的注入方式。

      使用set方法注入时,需要使用property标签,标签有如下属性:

      name: 指定注入时调用的set方法名称。(注:不包含set)

      value: 指定注入的数据,支持基本数据类型和String类型。

      ref: 指定注入的数据。支持其它Bean类型。写的是其它Bean的唯一标识。

      • 复杂数据类型注入

        指的是集合数据类型,分为两类,List和Map

        set方法注入:

        List结构的集合数据注入时,array,list,set这三个标签通用,注值的value标签内部可直接写值,也可以使用bean标签配置一个对象,或者用ref标签引用一个已经配过的bean的唯一标识。

        Map结构的集合数据注入时,map标签使用entry子标签实现注入,entry标签可以使用 keyvalue 属性指定存入map中的数据。使用 value-ref 属性指定已经配置好的bean的引用。

1.3 xml与注解相结合模式

注意:

1)实际企业开发中,纯xml模式已经很少使用

2)引入注解功能,不需要引入额外的jar

3)xml+注解结合模式,xml文件依然存在,所以IoC容器的启动依然从加载xml开始

4)第三方jar中的bean定义在xml,自己开发的bean定义使用注解

  • xml中标签与注解的对应(IoC)

    @Component,注解加在类上,针对分层代码开发提供@Controller、@Service、

    @Repository分别用于控制层、服务层、dao层的bean的定义。用法一样,只是为了清晰区分。

  • DI依赖注入的注解实现方式

    @Autowired(推荐使用):采取的策略为按照类型注入。

    @Resource:默认安装ByName自动注入。

    • 如果同时指定了name和type,则从Spring 上下文中找到唯一匹配的bean进行装配,找不到抛出异常。
    • 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到抛出异常。
    • 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
    • 如果既没有指定name,又没有指定type,则自动安装byName方式进行装配;

    注意:@Resource在JDK 11中已经移除,如需使用,需单独引入jar包

    <dependency>
    	<groupId>javax.annotation</groupId>
    	<artifactId>javax.annotation-api</artifactId>
    	<version>1.3.2</version>
    </dependency>
    

1.4 纯注解模式

改造xml+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从java配置类启动。

对应注解

@Configuration注解,表名当前类是配置类

@ComponentScan注解,替代context:component-scan

@PropertySource,引入外部属性配置文件

@Import引入其他配置类

@Value对变量赋值,可以直接赋值,也可以使用${}读取资源配置文件中的信息。

@Bean将方法返回对象加入Spring IoC容器

2. Spring IoC高级特性

2.1 lazy-init延迟加载

Bean的延迟加载(延迟创建)

<bean id="testBean" class="cn.lagou.LazyBean" />
该bean默认的设置为:
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="false" />

lazy-init=“false”,立即加载,表示在spring启动时,立刻进行实例化。

如果不想让Bean在ApplicationContext实现初始化时被提前实例化,可将lazy-init设置为true。

设置lazy-init为true的bean将不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean索取bean时实例化的。

应用场景

(1)开启延迟加载一定程度提高容器启动和运转性能。

(2)对于不常使用的Bean设置延迟加载,这样偶尔使用的时候再加载,不必要从一开始该Bean就占用资源

2.2 FactoryBean 和 BeanFactory

BeanFactory接口是容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,具体使用它下面的子接口类型(如ApplicationContext)

Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生成某一个类型的Bean实例,也就是说我们可以借助它自定义Bean的创建过程。

Bean创建的三种方式中的静态方法和实例化方法和FactoryBean作用类似,FactoryBean使用较多。

2.3 后置处理器

Spring提供了两种后置处理Bean的扩展接口,分别为BeanPostProcessor和BeanFactoryPostProcessor

工厂初始化(BeanFactory)—>Bean对象

在BeanFactory初始化之后可以使用BeanFactoryPostProcessor进行后置处理做一些事情

在Bean对象实例化(Bean的生命周期还没有完成)之后可以使用BeanPostProcessor进行后置处理做一些事情

注意:对象不一定是Spring Bean,Spring Bean一定是对象。

  • BeanPostProcessor

    BeanPostProcessor是针对Bean级别的处理,可以针对具体的Bean,该接口提供两个方法,分别在Bean的初始化方法前后执行,初始化方法类似于init-method指定的方法。

    public interface BeanPostProcessor {
    
    	@Nullable
    	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		return bean;
    	}
    
    	@Nullable
    	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		return bean;
    	}
    
    }
    

    定义一个类实现BeanPostProcessor,默认对整个Spring容器中所有Bean进行处理,如对具体某个Bean处理,可通过方法参数判断,两个参数分别为Object和String,参数一:bean实例,参数二:bean的name或id

    注意:处理是发生在Spring容器的实例化和依赖注入之后。

  • BeanFactoryPostProcessor

    BeanFactory级别的处理,针对整个Bean的工厂进行处理,典型应用:PropertyPlaceholderConfigurer,此接口只提供了一个方法,方法参数为:ConfigurableListableBeanFactory,该参数定义了一些方法,如getBeanDefinition,可根据此方法,找到定义bean的BeanDefinition对象(可进行修改)

    BeanDefinition对象: xml中定义的bean标签,Spring解析bean标签成为一个JavaBean(就是BeanDefinition)

第五部分 Spring IoC源码深度剖析

  • 好处:提高培养代码架构思维、深入理解框架
  • 原则
    • 定焦原则:主线
    • 宏观原则:关注源码结构和业务流程,淡化具体代码细节
  • 读源码的方法和技巧
    • 断点(调用栈)
    • 反调(Find Usages)
    • 经验(Spring框架中doXXX,做具体处理的地方)
1. Spring IoC容器初始化主题流程

1.1 Spring IoC的容器体系

IoC容器是Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案。提供了很多容器,其中BeanFactory是顶级容器,不能被实例化。

BeanFactory顶级接口方法栈如下:

在这里插入图片描述

1.2 Bean生命周期关键时机点

Spring的生命周期

构造器执行、初始化方法执行、Bean后置处理器的before/after方法:AbstractApplicationContext#refresh#finishBeanFactoryInitialization

Bean工厂后置处理器初始化、方法执行:

AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors

Bean后置处理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors

关键点触发代码
构造器refresh#finishBeanFactoryInitialization
BeanFactoryPostProcessor初始化refresh#invokeBeanFactoryPostProcessors
BeanFactoryPostProcessor方法调用refresh#invokeBeanFactoryPostProcessors
BeanPostProcessor初始化refresh#registerBeanPostProcessors
BeanPostProcessor方法调用refresh#finishBeanFactoryInitialization

1.3 Spring IoC容器初始化主体流程

Spring IoC容器初始化的关键环节就在AbstractApplicationContext#refresh() 方法中。

@Override
public void refresh() throws BeansException, IllegalStateException {
	// 对象锁加锁
	synchronized (this.startupShutdownMonitor) {
		// 1.刷新前的预处理
		prepareRefresh();

		// 2.获取BeanFactory;默认实现是DefaultListableBeanFactory,加载BeanDefition 并注册到 BeanDefitionRegistry
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// 3.BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
		prepareBeanFactory(beanFactory);

		try {
			// 4.BeanFactory准备工作完成后进行的后置处理工作
			postProcessBeanFactory(beanFactory);

			// 5.实例化实现了BeanFactoryPostProcessor接口的Bean,并调用接口方法
			invokeBeanFactoryPostProcessors(beanFactory);

			// 6.注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
			registerBeanPostProcessors(beanFactory);

			// 7.初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
			initMessageSource();

			// 8.初始化事件派发器
			initApplicationEventMulticaster();

			// 9.子类重写这个方法,在容器刷新的时候可以自定义逻辑;
			onRefresh();

			// 10.注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean
			registerListeners();

			/*
				11.初始化所有剩下的非懒加载的单例bean
				初始化创建非懒加载方式的单例Bean实例(未设置属性)
				填充属性
				初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
				调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
			 */
			finishBeanFactoryInitialization(beanFactory);

			/*
				12.完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
			 */
			finishRefresh();
		}

		...
	}
}	
2. BeanFactory创建流程

2.1 获取BeanFactory子流程

在这里插入图片描述

2.28 BeanDefinition加载解析及注册子流程

(1)该子流程涉及以下几个关键步骤

  • Resource定位: 对BeanDefinition的资源定位过程。(找到定义Java Bean信息的xml文件,封装成Resource对象)

  • BeanDefinition载入: 将用户定义好的Java Bean表示为IoC容器内部的数据结构(BeanDefinition)

  • 注册BeanDefinition到IoC容器

(2)过程分析

  • 子流程入口在AbstractRefreshableApplicationContext#refreshBeanFactory方法中

loadBeanDefinitions方法

/*
 * 对 bean factory 执行刷新操作,关闭以前的 bean factory(如果有)
 * 并且为 context 初始化一个新的 bean factory
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
	...
	try {
		// 实例化 DefaultListableBeanFactory
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		...
		// 加载应用中的BeanDefinitions
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			// 赋值当前bean facotry
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		...
	}
}
  • 依次调用多个类的loadBeanDefinitions方法—>AbstractXmlApplicationContext—>AbstractBeanDefinitionReader—>XmlBeanDefinitionReader一直执行到XmlBeanDefinitionReaderdoLoadBeanDefinitions方法

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    	...
    	try {
    		InputStream inputStream = encodedResource.getResource().getInputStream();
    		try {
    			// 把xml文件流封装为InputSource对象
    			InputSource inputSource = new InputSource(inputStream);
    			if (encodedResource.getEncoding() != null) {
    				inputSource.setEncoding(encodedResource.getEncoding());
    			}
    			// do!执行加载逻辑
    			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    		}
    		finally {
    			...
    		}
    	}
    	catch (IOException ex) {
    		...
    	}
    	finally {
    		...
    	}
    }
    
  • 重点观察XmlBeanDefinitionReader类的registerBeanDefinitions方法,此处关注createRederContext方法和DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,可以看到,Spring首先完成了NamespaceHandlerResolver的初始化。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    	...
    	// 注册BeanDefinition
    	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    	...
    }
    
    public XmlReaderContext createReaderContext(Resource resource) {
    	return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
    			this.sourceExtractor, this, getNamespaceHandlerResolver());
    }
    

    进入registerBeanDefinitions方法中追踪,调用了DefaultBeanDefinitionDocumentReader#registerBeanDefinitions方法,进入doRegisterBeanDefinitions方法,调用了parseBeanDefinitions方法

    protected void doRegisterBeanDefinitions(Element root) {
    	...
    	parseBeanDefinitions(root, this.delegate);
    	...
    }
    

    进入parseBeanDefinitions方法,可以看到又调用了parseDefaultElement,这个方法是负责解析默认标签元素的。

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    	...
    				
    	// 解析默认标签元素
    	parseDefaultElement(ele, delegate);
    				
    	// 解析自定义标签元素
    	delegate.parseCustomElement(ele);
    		
    	...
    }
    

    进入parseDefaultElement可以看到又调用了processBeanDefinition,此方法负责解析bean元素为BeanDefinition,通过BeanDefinitionReaderUtils.registerBeanDefinition完成注册。

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    	// 解析bean元素为BeanDefinition,但是此时使用 BeanDefinitionHolder 又包装成了 BeanDefinitionHolder 对象
    	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    	if (bdHolder != null) {
    		...
    		try {
    			// 完成BeanDefinition的注册
    			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    		}
    		catch (BeanDefinitionStoreException ex) {
    			...
    		}
    		...
    	}
    }
    

    至此,注册流程结束,所谓的注册就是把封装的xml中定义的Bean信息封装为BeanDefinition对象之后放入一个map中。

    (3)时序图

在这里插入图片描述

3. Bean创建过程
  • 通过最开始的关键时机点分析,Bean的创建子流程入口在AbstractApplicationContext#refresh()⽅法的finishBeanFactoryInitialization(beanFactory)

    /*
    	Instantiate all remaining (non-lazy-init) singletons.
    	初始化所有剩下的非懒加载的单例bean
    	初始化创建非懒加载方式的单例Bean实例(未设置属性)
    	填充属性
    	初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
    	调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
     */
    finishBeanFactoryInitialization(beanFactory);
    
  • 进⼊finishBeanFactoryInitialization

    /**
     * 结束 bean factory 的初始化工作
     * 实例化所有单例bean
     */
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    	...
    	beanFactory.setTempClassLoader(null);
    
    	// Allow for caching all bean definition metadata, not expecting further changes.
    	beanFactory.freezeConfiguration();
    
    	// Instantiate all remaining (non-lazy-init) singletons.
    	// 实例化所有立即加载的单例bean
    	beanFactory.preInstantiateSingletons();
    }
    
  • 继续进⼊DefaultListableBeanFactory类的preInstantiateSingletons⽅法,可以看到工厂Bean或者普通Bean,最终都是通过getBean的方法获取实例。

    // 触发所有非延迟加载单例bean的初始化,主要步骤为getBean
    for (String beanName : beanNames) {
    	// 合并父BeanDefinition对象
    	// map.get(beanName)
    	RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    	if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
    		if (isFactoryBean(beanName)) {
    			Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
    			// 如果是FactoryBean则加&
    			if (bean instanceof FactoryBean) {
    				final FactoryBean<?> factory = (FactoryBean<?>) bean;
    				boolean isEagerInit;
    				if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
    					isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
    									((SmartFactoryBean<?>) factory)::isEagerInit,
    							getAccessControlContext());
    				}
    				else {
    					isEagerInit = (factory instanceof SmartFactoryBean &&
    							((SmartFactoryBean<?>) factory).isEagerInit());
    				}
    				if (isEagerInit) {
    					getBean(beanName);
    				}
    			}
    		}
    		else {
    			// 实例化当前bean
    			getBean(beanName);
    		}
    	}
    }
    
  • 继续跟踪下去,进⼊到了AbstractBeanFactory类的doGetBean⽅法,找到核心部分

    // 创建单例bean
    if (mbd.isSingleton()) {
    	sharedInstance = getSingleton(beanName, () -> {
    		try {
    			// 创建 bean
    			return createBean(beanName, mbd, args);
    		}
    		catch (BeansException ex) {
    			destroySingleton(beanName);
    			throw ex;
    		}
    	});
    	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    
  • 接着进入到AbstractAutowireCapableBeanFactory类的doCreateBean方法

    try {
    	// 进入,真真正正创建bean
    	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    	return beanInstance;
    }
    
  • 进入doCreateBean方法,重点关注两块重点区域

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
    			throws BeanCreationException {
    
    	...
    	if (instanceWrapper == null) {
    		// 创建 Bean 实例,仅仅调用构造方法,但是尚未设置属性
    		instanceWrapper = createBeanInstance(beanName, mbd, args);
    	}
    	...
    
    	// 初始化bean实例
    	Object exposedObject = bean;
    	try {
    		// Bean属性填充
    		populateBean(beanName, mbd, instanceWrapper);
    		// 调用初始化方法,应用BeanPostProcessor后置处理器
    		exposedObject = initializeBean(beanName, exposedObject, mbd);
    	}
    	...
    }
    
4. lazy-init 延迟加载机制原理
  • lazy-init 延迟加载机制分析

普通 Bean 的初始化是在容器启动初始化阶段执⾏的,⽽被lazy-init=true修饰的 bean 则是在从容器⾥
第⼀次进⾏context.getBean() 时进⾏触发。Spring 启动的时候会把所有bean信息(包括XML和注解)解
析转化成Spring能够识别的BeanDefinition并存到Hashmap⾥供下⾯的初始化时⽤,然后对每个
BeanDefinition 进⾏处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进⾏初始化并依赖注⼊。

public void preInstantiateSingletons() throws BeansException {
	// 所有bean的名字
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

	// Trigger initialization of all non-lazy singleton beans...
	// 触发所有非延迟加载单例bean的初始化,主要步骤为getBean
	for (String beanName : beanNames) {
		// 合并父BeanDefinition对象
		// map.get(beanName)
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				// 如果是FactoryBean则加&
				if (bean instanceof FactoryBean) {
					final FactoryBean<?> factory = (FactoryBean<?>) bean;
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
										((SmartFactoryBean<?>) factory)::isEagerInit,
								getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			}
			else {
				/*
				如果是普通bean则进⾏初始化并依赖注⼊,此 getBean(beanName)接下来触发的逻辑
				和
				懒加载时 context.getBean("beanName") 所触发的逻辑是一样的
				*/
				getBean(beanName);
			}
		}
	}

	...
}
  • 总结:

    • 对于被修饰为lazy-init的bean,Spring容器初始化阶段不会进行init并且依赖注入,当第一次调用getBean的时候才进行初始化并依赖注入

    • 对于非懒加载的bean,getBean的时候会从缓存里取,因为容器初始化阶段bean已经初始化完成并且放入缓存中

5. Spring IoC循环依赖问题

5.1 什么是循环依赖

循环依赖就是循环引用,也就是两个或两个以上的Bean互相持有对方,最终形成闭环。

注意:这里不是函数的循环调用,而是对象的相互依赖关系。

Spring中循环依赖场景有:

  • 构造器的循环依赖(构造器注入)
  • Field属性的循环依赖(set注入)

5.2 循环依赖处理机制

  • 单例bean构造器参数循环依赖(无法解决)

  • propotype原型bean循环依赖(无法解决)

    总结:Spring不支持原型bean的循环依赖

  • 单例bean通过setXXX或者使用**@Autowired**进行循环依赖

    Spring的循环依赖的理论依据基于java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前

    Spring通过setXXX或者使用@Autowired方法解决循环依赖其实是通过提前暴露一个ObectFactory对象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法之前,就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中

    • Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器

      //将初始化后的对象提前已ObjectFactory对象注⼊到容器中
      addSingletonFactory(beanName, new ObjectFactory<Object>() {
      	@Override
      	public Object getObject() throws BeansException {
      		return getEarlyBeanReference(beanName, mbd, bean);
      	}
      });
      
    • ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB,此时ClassB不存在容器中

    • Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中

    • ClassB调用setClassA方法,Spring从容器中获取ClassA,因为第一步已经提前暴露ClassA到容器中,因此可以获取ClassA实例

    • ClassA通过Spring容器获取到ClassB,完成了对象初始化操作

    • 这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题

  • 时序图
    在这里插入图片描述

第六部分 Spring AOP应用

AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、日志代码、事务控制代码、性能监控代码。

1. AOP相关术语

1.1 相关术语

名词解释
Joinpoint(连接点)指那些可以用于把增强代码加入到业务主线中的点(方法)
Pointcut(切点)指那些已经把增强代码加入到业务主线进来之后的连接点
Advice(通知/增强)指切面类中用于提供增强功能的方法,并且不同的方法增强的时机是不一样的
Target(目标对象)指代理的目标对象
Proxy(代理)指一个类被AOP织入增强后,产生的代理类(代理对象)
Weaving(织入)指把增强应用到目标对象来创建新的代理对象的过程
Aspect(切面)指增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面类

连接点:方法开始时、结束时、正常运行完毕时、方法异常时等这些特殊的时机点,称之为连接点

切入点:指定AOP想要影响的具体方法

Advice增强:

第一个层次:横切逻辑

第二个层次:方位点(在某一些连接点加入横切逻辑,那么这些连接点就叫做方位点,描述的是具体的特殊时机)

Aspects切面:切面就是对上述概念的综合

Aspects切面 = 切入点(锁定方法)+ 方位点(锁定方法中的特殊时机)+ 横切逻辑

2. Spring中AOP的代理选择

Spring实现AOP思想,使用的是动态代理技术

默认情况下,Spring会根据被代理对象是否实现接口来选择使用JDK还是CGLIB。当被代理对象没有实现任何接口时,Spring会选择CGLIB。当被代理对象实现了接口,Spring会选择JDK官方的代理技术,可以通过配置的方式,让Spring强制使用CGLIB。

3. Spring中AOP的配置方式

在Spring的AOP配置中,支持3类配置方式

第一类:使用XML配置

第二类:使用XML + 注解组合配置

第三类:使用纯注解配置

4. Spring中AOP实现

4.1 XML模式

需要引入aop的jar包

  • 坐标
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aop</artifactId>
	<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.4</version>
</dependency>
  • AOP核心配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
">

    <bean id="logUtils" class="com.lagou.edu.utils.LogUtils"></bean>
    <aop:config>
        <aop:aspect id="logAspect" ref="logUtils">
            <aop:pointcut id="pt1"
                          expression="execution(void com.lagou.edu.service.impl.TransferServiceImpl.transfer(java.lang.String, java.lang.String, java.math.BigDecimal))"/>
            <aop:before method="before" pointcut-ref="pt1"/>
            <aop:after method="after" pointcut-ref="pt1"/>
            <aop:around method="around" pointcut-ref="pt1"/>
            <aop:after-throwing method="expection" pointcut-ref="pt1"/>
        </aop:aspect>
    </aop:config>
</beans>
  • 细节

    • 关于切入点表达式

      上述配置实现了对TransferServiceImpltransfer方法进行增强,执行之前,输出了记录日志的语句。这里使用到了切入点表达式

      • 概念及作用

        切入点表达式,也称为AspectJ切入点表达式,指的是遵循特定语法结构的字符串,其作用是用于对符合语法格式的连接点进行增强。

      • 关于AspectJ

        AspectJ是一个机遇Java语言的AOP框架,Spring框架从2.0版本之后集成了AspectJ框架中切入点表达式的部分,开始支持AspectJ切入点表达式

      • 切入点表达式使用示例

        全限定方法名 访问修饰符 返回值 包名.类名.方法名(参数列表)
        全匹配方式: public void com.lagou.edu.service.impl.TransferServiceImpl.transfer(java.lang.String, java.lang.String, java.math.BigDecimal)
        简化后:* *..*.*(*) 参数列表可以使用*,表示任意参数类型,但是必须有参数
        全匹配方式:* *..*.*(..) 参数列表可以使用..,表示有误参数均可,参数可以是任意类型
        
    • 改变代理方式的配置

      • 使用aop:config配置标签

        <aop:config proxy-target-class="true">
        
      • 使用aop:aspectj-autoproxy标签配置

        <!--此标签是基于XML和注解组合配置AOP时的必备标签,表示Spring开启注解配置AOP
        的⽀持-->
        <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-
        autoproxy>
        
    • 五种通知类型

      • 前置通知

        配置方式:aop:before标签

        <aop:before method="printLog" pointcut-ref="pointcut1">
        </aop:before>
        

        执行时机:前置通知永远都会在切入点方法(业务核心方法)执行前执行

        细节:前置通知可以获取切入点方法的参数,对其进行增强

      • 正常执行时通知

        配置方式:aop:after-returning标签

        <aop:after-returning method="afterReturningPrintLog" pointcut-
        ref="pt1"></aop:after-returning>
        
      • 异常通知

        配置方式:aop:after-throwing标签

        <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"
        ></aop:after-throwing>
        

        执行时机:异常通知的执行时机是在切入点方法执行产生异常之后,异常通知执行。如果切入点方法执行时没有产生异常,则异常通知不会执行

        细节:异常通知不仅可以获取切入点方法执行的参数,也可以获取切入点方法执行产生的异常信息

      • 最终通知

        配置方式:aop:after标签

        <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
        

        执行时机:最终通知的执行时机是在切入点方法执行完成之后,切入点方法返回之前执行。无论切入点方法执行是否产生异常,它都会在返回之前执行。

        细节:最终通知执行时,可以获取到通知方法的参数,同时它可以做一些清理操作。

      • 环绕通知

        配置方式:aop:around标签

        <aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
        

        **特此说明:**环绕通知,有别于前面四种通知类型外的特殊通知。前面四种通知(前置、后置、异常和最终),它们都是指定何时增强的通知类型。而环绕通知,它是Spring框架提供的一种通过编码的方式,控制增强代码何时执行的通知类型。

4.2 XML + 注解模式

  • XML中开启Spring对AOP注解的支持

    <!--开启spring对注解aop的⽀持-->
    <aop:aspectj-autoproxy/>
    
  • 示例

    @Component
    @Aspect
    public class LogUtils {
    
        @Pointcut("execution(* com.lagou.service.impl.*.*(..))")
    	public void pointcut(){}
    
        @Before("pointcut()")
    	public void beforePrintLog(JoinPoint jp){
    		Object[] args = jp.getArgs();
    		System.out.println("前置通知:beforePrintLog,参数是:"+
    		Arrays.toString(args));
    	}
    
        @AfterReturning(value = "pointcut()",returning = "rtValue")
    	public void afterReturningPrintLog(Object rtValue){
    		System.out.println("后置通知:afterReturningPrintLog,返回值
    		是:"+rtValue);
    	}
    
        @AfterThrowing(value = "pointcut()",throwing = "e")
    	public void afterThrowingPrintLog(Throwable e){
    		System.out.println("异常通知:afterThrowingPrintLog,异常是:"+e);
    	}
    
        @After("pointcut()")
    	public void afterPrintLog(){
    		System.out.println("最终通知:afterPrintLog");
    	}                       
                               
        @Around("pointcut()")
        public Object aroundPrintLog(ProceedingJoinPoint proceedingJoinPoint) {
            //定义返回值
    		Object rtValue = null;
    		try{
    			//前置通知
    			System.out.println("前置通知");
    			//1.获取参数
    			Object[] args = pjp.getArgs();
                //2.执⾏切⼊点⽅法
    			rtValue = pjp.proceed(args);
    			//后置通知
    			System.out.println("后置通知");
        	}catch (Throwable t){
    			//异常通知
    			System.out.println("异常通知");
    			t.printStackTrace();
    		}finally {
    			//最终通知
    			System.out.println("最终通知");
    		}
    		return rtValue;
        }
    }
    

4.3 注解模式

在使⽤注解驱动开发aop时,我们要明确的就是,是注解替换掉配置⽂件中的下⾯这⾏配置:

<!--开启spring对注解aop的⽀持-->
<aop:aspectj-autoproxy/>

配置类使用注解进行替换上诉配置

@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy //开启spring对注解AOP的⽀持
public class SpringConfiguration {
}
5. Spring声明式事务的支持

编程式事务:业务代码中添加事务控制代码,叫做编程式事务

声明式事务:通过xml或注解的方式达到控制事务的目的,叫做声明式事务

5.1 事务回顾

5.1.1 事务的概念

事务:指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不执行。从而确保数据的准确与安全。

5.1.2 事务的四大特性

原子性: 指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生

一致性: 事务必须使数据库从一个一致性状态变换到另一个一致性状态

例如:A给B转账,转账前,A有1000,B有1000,转账后A+B必须是2000

隔离性: 指多个用户并发访问数据库时,数据库为每一个用户开启的事务,每个事务不能被其他事务的操作数据所干扰,多个并发事务之间相互隔离

持久性: 指一个事务一旦被提交,它对数据库的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

5.1.3 事务的隔离级别

不考虑隔离级别,会出行以下情况,也即为隔离级别在解决事务并发问题

脏读:一个线程中的事务读到了另一个线程中未提交的数据

不可重复读:一个线程中的事务读到了另一个线程中已经提交的update的数据

虚读:一个线程中的事务读到了另一个线程中已提交的insert或者delete的数据

数据库工定义了四种隔离级别:

隔离级别说明等级
Serializable(串⾏化)可避免脏读、不可重复读、虚读情况的发⽣。(串⾏化)最⾼
Repeatable read(可重复读)可避免脏读、不可重复读情况的发⽣。(幻读有可能发⽣)第⼆
该机制下会对要update的⾏进⾏加锁
Read committed(读已提交)可避免脏读情况发⽣。不可重复读和幻读⼀定会发⽣。第三
Read uncommitted(读未提交)最低级别,以上情况均⽆法保证。(读未提交)最低

注意:级别依次升高,效率依次降低

5.1.4 事务的传播行为

事务往往在service层进行控制,如果出行service方法A调用了另一个service层的方法B,A和B自身都已经被添加了事务控制,那么A调用B的时候,就需要进行事务的一些协商,这就叫做事务的传播行为。

PROPAGATION_REQUIRED如果当前没有事务,就新建⼀个事务,如果已经存在⼀个事务中,加入到这个事务中。这是最常⻅的选择。
PROPAGATION_SUPPORTS⽀持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使⽤当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执⾏。如果当前没有事务,则
执⾏与PROPAGATION_REQUIRED类似的操作。

5.2 Spring声明式事务配置

  • 纯xml模式

    导入jar包

    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-context</artifactId>
    	<version>5.1.12.RELEASE</version>
    </dependency>
    
    <dependency>
    	<groupId>org.aspectj</groupId>
    	<artifactId>aspectjweaver</artifactId>
    	<version>1.8.13</version>
    </dependency>
    
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-tx</artifactId>
    	<version>5.1.12.RELEASE</version>
    </dependency>
    
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-jdbc</artifactId>
    	<version>5.1.12.RELEASE</version>
    </dependency>
    

    xml配置

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    	<tx:attributes>
    		<tx:method name="*" read-only="false"/>
    		<tx:method name="select*" read-only="true"/>
    	</tx:attributes>
    </tx:advice>
    <aop:config>
    	<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..))"></aop:advisor>
    </aop:config>
    
  • xml + 注解模式

    • xml配置
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    • 接口、类或方法上添加@Transactional注解
    @Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
    
  • 纯注解模式

    Spring 的配置类上添加 @EnableTransactionManagement 注解即可

    @EnableTransactionManagement//开启spring注解事务的⽀持
    public class SpringConfiguration {
    }
    

第七部分 AOP源码深度剖析

1. 代理对象创建

1.1 代理对象创建流程

入口:AbstractAutowireCapableBeanFactory#initializeBean

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	...
	if (mbd == null || !mbd.isSynthetic()) {
		// 整个Bean初始化完成,执⾏后置处理器⽅法
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}

	return wrappedBean;
}

进入applyBeanPostProcessorsAfterInitialization方法

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

	Object result = existingBean;
	// 循环执行后置处理器
	for (BeanPostProcessor processor : getBeanPostProcessors()) {
		Object current = processor.postProcessAfterInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}

创建代理对象的后置处理器AbstractAutoProxyCreator#postProcessAfterInitialization

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		// 检查下该类是否已经暴露过了(可能已经创建了,比如A依赖B时,创建A时候,就会先去创建B。
			// 当真正需要创建B时,就没必要再代理⼀次已经代理过的对象),避免重复创建
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

进入wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	...

	// Create proxy if we have advice.
	// 查找出和当前bean匹配的advisor
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		// 创建代理对象
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

进入createProxy方法

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
	}

	// 创建代理的工作交给ProxyFactory
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	// 判断是否要设置proxyTargetClass为true
	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}

	// 把增强和通用拦截器对象合并,都适配成Advisor
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors);
	// 设置参数
	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);

	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}
	// 准备工作做完就开始创建代理
	return proxyFactory.getProxy(getProxyClassLoader());
}

接着跟进ProxyFactory

public Object getProxy(@Nullable ClassLoader classLoader) {
	// 用ProxyFactory创建AopProxy, 然后用AopProxy创建Proxy, 所以这里重要的是看获取的AopProxy
	
	// 对象是什么,
	// 然后进去看怎么创建动态代理, 提供了两种:jdk proxy, cglib
	return createAopProxy().getProxy(classLoader);
}

流程就是用AopProxyFactory创建AopProxy,再用AopProxy创建代理对象,这里的AopProxyFactory默认是DefaultAopProxyFactory,看它的createAopProxy⽅法

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		...
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

这里决定创建代理对象时用JDK还是用CGLIB,最简单的从使用方面来说:设置proxyTargetClass=true强制使用CGLIB代理,什么参数都不设置并且对象类实现接口则默认用JDK代理,如果没有实现接口则也必须使用CGLIB

  • 时序图

在这里插入图片描述

2. Spring声明式事务控制

2.1 @EnableTransactionManagement

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	// true:使用CGLIB代理,false:使用JDK代理
	boolean proxyTargetClass() default false;

	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;

}

@EnableTransactionManagement 注解使⽤ @Import 标签引⼊了
TransactionManagementConfigurationSelector类,这个类⼜向容器中导⼊了两个重要的组件

protected String[] selectImports(AdviceMode adviceMode) {
	switch (adviceMode) {
		case PROXY:
			return new String[] {AutoProxyRegistrar.class.getName(),
					ProxyTransactionManagementConfiguration.class.getName()};
		case ASPECTJ:
			return new String[] {determineTransactionAspectClass()};
		default:
			return null;
	}
}

2.2 加载事务控制组件

  • AutoProxyRegistrar

    AutoProxyRegistrar类的registerBeanDefinitions方法中又注册了一个组件

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    		boolean candidateFound = false;
    		Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
    		for (String annType : annTypes) {
    			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
    			if (candidate == null) {
    				continue;
    			}
    			Object mode = candidate.get("mode");
    			Object proxyTargetClass = candidate.get("proxyTargetClass");
    			if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
    					Boolean.class == proxyTargetClass.getClass()) {
    				candidateFound = true;
    				if (mode == AdviceMode.PROXY) {
    					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
    					if ((Boolean) proxyTargetClass) {
    						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    						return;
    					}
    				}
    			}
    		}
    		...
    	}
    

    进入AopConfigUtils.registerAutoProxyCreatorIfNecessary方法

    public static BeanDefinition registerAutoProxyCreatorIfNecessary(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
    
    	return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
    }
    

    最终发现,注册了⼀个叫做 InfrastructureAdvisorAutoProxyCreator 的 Bean,而这个类是

AbstractAutoProxyCreator 的⼦类,实现了SmartInstantiationAwareBeanPostProcessor接⼝,说明这是一个后置处理器,而且跟spring AOP 开启@EnableAspectJAutoProxy时注册的 AnnotationAwareAspectJProxyCreator实现的是同⼀个接⼝,所以说,声明式事务是 springAOP 思想的⼀种应用。

  • ProxyTransactionManagementConfiguration组件

    @Configuration
    public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    
    	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
    		// 事务增强器
    		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    		// 向事务增强器中注入 属性解析器 transactionAttributeSource
    		advisor.setTransactionAttributeSource(transactionAttributeSource());
    		// 向事务增强器中注入 事务拦截器 transactionInterceptor
    		advisor.setAdvice(transactionInterceptor());
    		if (this.enableTx != null) {
    			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    		}
    		return advisor;
    	}
    
    	@Bean
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	// 属性解析器 transactionAttributeSource
    	public TransactionAttributeSource transactionAttributeSource() {
    		return new AnnotationTransactionAttributeSource();
    	}
    
    	@Bean
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	// 事务拦截器 transactionInterceptor
    	public TransactionInterceptor transactionInterceptor() {
    		TransactionInterceptor interceptor = new TransactionInterceptor();
    		interceptor.setTransactionAttributeSource(transactionAttributeSource());
    		if (this.txManager != null) {
    			interceptor.setTransactionManager(this.txManager);
    		}
    		return interceptor;
    	}
    
    }
    

    ProxyTransactionManagementConfiguration是一个容器配置类,注册了一个组件transactionAdvisor,称为事务增强器,然后再这个事务增强器中又注入了两个属性:

    transactionAttributeSource属性解析器,和transactionInterceptor事务拦截器

    • 属性解析器AnnotationTransactionAttributeSource部分源码
    public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
    		implements Serializable {
    
    	private static final boolean jta12Present;
    
    	private static final boolean ejb3Present;
    
    	static {
    		ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
    		jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
    		ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
    	}
    
    	private final boolean publicMethodsOnly;
    
    	// 注解解析器集合
    	private final Set<TransactionAnnotationParser> annotationParsers;
    
    	...
    }
    

    属性解析器有一个成员变量是annotationParsers,可以添加多种注解解析器,重点关注Spring的注解解析器,部分源码如下:

    protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    	RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    
    	Propagation propagation = attributes.getEnum("propagation");
    	rbta.setPropagationBehavior(propagation.value());
    	Isolation isolation = attributes.getEnum("isolation");
    	rbta.setIsolationLevel(isolation.value());
    	rbta.setTimeout(attributes.getNumber("timeout").intValue());
    	rbta.setReadOnly(attributes.getBoolean("readOnly"));
    	rbta.setQualifier(attributes.getString("value"));
    
    	List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
    	for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
    		rollbackRules.add(new RollbackRuleAttribute(rbRule));
    	}
    	for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
    		rollbackRules.add(new RollbackRuleAttribute(rbRule));
    	}
    	for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
    		rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    	}
    	for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
    		rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    	}
    	rbta.setRollbackRules(rollbackRules);
    
    	return rbta;
    }
    

    属性解析器的作用就是用来解析@Transaction注解

    • TransactionInterceptor 事务拦截器,部分源码如下
    // 构造方法传入
    public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
    	setTransactionManager(ptm);
    	setTransactionAttributeSource(tas);
    }
    
    
    @Override
    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {
    
    	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    
    	// 添加事务支持
    	return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
    }
    
  • 上述组件如何管理起来?

    • 事务拦截器实现MethodInterceptor接口,追溯一下上面提到的InfrastructureAdvisorAutoProxyCreator后置处理器,它会在代理对象执行目标方法时获取其拦截器链,而拦截器链就是TransactionInterceptor,这就把两个组件联系起来;
    • 构造方法传入PlatformTransactionManager(事务管理器)、TransactionAttributeSource(属性解析器),但是追溯一下上面的ProxyTransactionManagementConfiguration的源码,在注册事务拦截器的时候并没有调用这个带参构造函数,而是调用的无参构造方法,然后再调用set方法注入这两个属性,效果一样。
  • invokeWithinTransaction方法,部分源码如下

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
		final InvocationCallback invocation) throws Throwable {

	// If the transaction attribute is null, the method is non-transactional.
	// 获取属性解析器,即在ProxyTransactionManagementConfiguration容器配置类中注册事务拦截器时注入的
	TransactionAttributeSource tas = getTransactionAttributeSource();
	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
	// 获取事务管理器
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

		Object retVal;
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// target invocation exception
			// 如果目标方法抛异常,会执行completeTransactionAfterThrowing(获取事务管理器,执行回滚操作)
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			cleanupTransactionInfo(txInfo);
		}
		// 如果目标方法正常运行,则会执行commitTransactionAfterReturning(获取事务管理器,执行提交事务操作)
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}

	...
}
  • 过程:
@EnableTransactionManagement 注解
1)通过@Import引入TransactionManagementConfigurationSelector类
			它的selectImports方法导入了另外两个类:AutoProxyRegistrar和ProxyTransactionManagementConfiguration
2)AutoProxyRegistrar类分析
			方法registerBeanDefinitions中,引入其他类,通过AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)引⼊
InfrastructureAdvisorAutoProxyCreator,它继承了AbstractAutoProxyCreator,是⼀个
后置处理器类
3)ProxyTransactionManagementConfiguration是一个添加了@Configuration注解的配置类
		注册事务增强器(注入属性解析器、事务拦截器)
			属性解析器:AnnotationTransactionAttributeSource,内部持有解析器集合Set<TransactionAnnotationParser> annotationParsers;
			具体使用的是SpringTransactionAnnotationParser解析器,用来解析@Transactional的事务属性
			事务拦截器:TransactionInterceptor实现了MethodInterceptor接口,该通用拦截会在产生代理对象之前和aop增强合并,最终一起影响到代理对象
			TransactionInterceptor的invoke方法中invokeWithinTransaction会触发原有业务逻辑调用(增强事务)
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值