Spring 是分层的轻量级开源框架,以IoC和AOP为内核,提供展现层SpringMVC和业务层事务管理等众多企业级应用技术,可以整合众多著名第三方框架和类库,成为使用最多的Java EE企业应用开源框架。
Spring是一个分层清晰并且依赖关系、职责定位明确的轻量级框架,主要包括几大模块:数据处理模块、Web模块、AOP/Aspects模块、Core Container模块和Test模块。
注意:IoC和AOP并不是Spring提出,在Spring之前就已经存在,只不过更偏向于理论化,Spring只是在技术层次把这两个思想做了非常好的实现。
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.1 什么是AOP
AOP全称Aspect oriented Programming(面向切面编程)
AOP是OOP的一个延续,虽然有了OOP的存在,但是有一些情况是OOP处理不了的,因此AOP出场,AOP独辟蹊径提出横向抽取机制,将横切逻辑代码和业务逻辑代码分离。
代码拆分容易,但是如何在不改变原有业务逻辑的情况下,悄无声息的把横切逻辑代码应用到原有的业务逻辑中,达到和原来一样的效果却比较难。
2.2 AOP解决了什么问题
在不改变原有业务逻辑情况下,增强横切逻辑代码,从根本上解耦合,避免横切逻辑代码重复。
2.3 为什么叫做面向切面编程
【切】:指横切逻辑,原有业务逻辑代码不能动的情况下,只能操作横切逻辑代码,所以面向横切逻辑。
【面】:横切逻辑代码往往影响的是很多方法,每一个方法如同一个点,点点成面。
1.1 BeanFactory与ApplicationContext区别
BeanFactory是Spring框架中IoC容器的顶层接口,只是用来定义一些基础功能和基础规范,而ApplicationContext是它的一个子接口,ApplicationContext具备BeanFactory提供的全部功能。
通常称BeanFactory为Spring IoC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,比如国际化支持和访问(xml,java配置类)等。
启动IoC容器的方式
Java环境下启动IoC容器
Web环境下启动IoC容器
从xml启动容器,在web.xml中配置
Archetype Created Web Application
contextConfigLocation
classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
从配置类启动容器
Archetype Created Web Application
contextClass
org.springframework.web.context.support.AnnotationConfigWebAppli
cationContext
contextConfigLocation
com.lagou.edu.SpringConfig
org.springframework.web.context.ContextLoaderListener
1.2 纯xml模式
xml文件头
实例化Bean的三种方式
方式一:使用无参构造函数
默认情况下,会通过反射调用无参构造函数来创建对象。如果类中没有无参构造函数,将创建失败。
方式二:使用静态方法创建
在实际开发中,我们使用的对象有时候并不是直接通过构造函数就可以创建处理,它可能在创建的过程中会做很多额外的操作。此时会提供一个创建对象的方法,恰好这个方法是static修饰的方法,即是这种情况。
方式三:使用实例化方法创建
此种方法和方式二类似,区别是用于获取对象的方法不再是static修饰的,而是类中的普通方法。此种方式比静态方法创建的使用几率高一些。
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自动注入。
注意:@Resource在JDK 11中已经移除,如需使用,需单独引入jar包
javax.annotation
javax.annotation-api
1.3.2
1.4 纯注解模式
改造xml+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从java配置类启动。
对应注解
@Configuration注解,表名当前类是配置类
@ComponentScan注解,替代context:component-scan
@PropertySource,引入外部属性配置文件
@Import引入其他配置类
@Value对变量赋值,可以直接赋值,也可以使用${}读取资源配置文件中的信息。
@Bean将方法返回对象加入Spring IoC容器
2.1 lazy-init延迟加载
Bean的延迟加载(延迟创建)
该bean默认的设置为:
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)
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.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)时序图
通过最开始的关键时机点分析,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);
}
...
}
普通 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.1 什么是循环依赖
循环依赖就是循环引用,也就是两个或两个以上的Bean互相持有对方,最终形成闭环。
注意:这里不是函数的循环调用,而是对象的相互依赖关系。
Spring中循环依赖场景有:
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
ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB,此时ClassB不存在容器中
Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
ClassB调用setClassA方法,Spring从容器中获取ClassA,因为第一步已经提前暴露ClassA到容器中,因此可以获取ClassA实例
ClassA通过Spring容器获取到ClassB,完成了对象初始化操作
这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题
AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、日志代码、事务控制代码、性能监控代码。
1.1 相关术语
名词 | 解释 |
---|---|
Joinpoint(连接点) | 指那些可以用于把增强代码加入到业务主线中的点(方法) |
Pointcut(切点) | 指那些已经把增强代码加入到业务主线进来之后的连接点 |
Advice(通知/增强) | 指切面类中用于提供增强功能的方法,并且不同的方法增强的时机是不一样的 |
Target(目标对象) | 指代理的目标对象 |
Proxy(代理) | 指一个类被AOP织入增强后,产生的代理类(代理对象) |
Weaving(织入) | 指把增强应用到目标对象来创建新的代理对象的过程 |
Aspect(切面) | 指增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面类 |
连接点:方法开始时、结束时、正常运行完毕时、方法异常时等这些特殊的时机点,称之为连接点
切入点:指定AOP想要影响的具体方法
Advice增强:
第一个层次:横切逻辑
第二个层次:方位点(在某一些连接点加入横切逻辑,那么这些连接点就叫做方位点,描述的是具体的特殊时机)
Aspects切面:切面就是对上述概念的综合
Aspects切面 = 切入点(锁定方法)+ 方位点(锁定方法中的特殊时机)+ 横切逻辑
Spring实现AOP思想,使用的是动态代理技术
默认情况下,Spring会根据被代理对象是否实现接口来选择使用JDK还是CGLIB。当被代理对象没有实现任何接口时,Spring会选择CGLIB。当被代理对象实现了接口,Spring会选择JDK官方的代理技术,可以通过配置的方式,让Spring强制使用CGLIB。
在Spring的AOP配置中,支持3类配置方式
第一类:使用XML配置
第二类:使用XML + 注解组合配置
第三类:使用纯注解配置
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>
<?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标签配置
五种通知类型
前置通知
配置方式: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标签
执行时机:最终通知的执行时机是在切入点方法执行完成之后,切入点方法返回之前执行。无论切入点方法执行是否产生异常,它都会在返回之前执行。
细节:最终通知执行时,可以获取到通知方法的参数,同时它可以做一些清理操作。
环绕通知
配置方式:aop:around标签
**特此说明:**环绕通知,有别于前面四种通知类型外的特殊通知。前面四种通知(前置、后置、异常和最终),它们都是指定何时增强的通知类型。而环绕通知,它是Spring框架提供的一种通过编码的方式,控制增强代码何时执行的通知类型。
4.2 XML + 注解模式
XML中开启Spring对AOP注解的支持
示例
@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 {
}
编程式事务:业务代码中添加事务控制代码,叫做编程式事务
声明式事务:通过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 + 注解模式
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
纯注解模式
Spring 的配置类上添加 @EnableTransactionManagement 注解即可
@EnableTransactionManagement//开启spring注解事务的⽀持
public class SpringConfiguration {
}
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.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注解
// 构造方法传入
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);
}
上述组件如何管理起来?
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 annotationParsers;
具体使用的是SpringTransactionAnnotationParser解析器,用来解析@Transactional的事务属性
事务拦截器:TransactionInterceptor实现了MethodInterceptor接口,该通用拦截会在产生代理对象之前和aop增强合并,最终一起影响到代理对象
TransactionInterceptor的invoke方法中invokeWithinTransaction会触发原有业务逻辑调用(增强事务)