第一部分 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. 银行转账案例问题分析
- 问题一:service层实现类在使用dao层对象时,直接在TransferService中通过AccountDao accountDao = new AccountDaoImpl获得dao层对象,然而new关键字却将service层和dao层实现类耦合在一起,如果技术架构发生变动,dao层的实现要使用其他技术,切换成本太大,每一个new的地方都需要修改源代码,重新编译,面向接口开发的意义将大打折扣。
- 问题二: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
标签可以使用 key 和 value 属性指定存入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
一直执行到XmlBeanDefinitionReader
的doLoadBeanDefinitions
方法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>
-
细节
-
关于切入点表达式
上述配置实现了对
TransferServiceImpl
的transfer
方法进行增强,执行之前,输出了记录日志的语句。这里使用到了切入点表达式-
概念及作用
切入点表达式,也称为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方法注入这两个属性,效果一样。
- 事务拦截器实现MethodInterceptor接口,追溯一下上面提到的
-
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会触发原有业务逻辑调用(增强事务)