目錄
- SpringIOC原始碼
- IOC容器加載程序及Bean生命周期
- BeanFactory和ApplicationContext的區別
- Spring IOC容器的具體加載程序
- 簡述Bean的生命周期
- 后置處理器的九次呼叫
- BeanDefinition
- 內置后置PostProcess處理器
- BeanFactoryPostProcessor的呼叫程序/配置類的決議程序
- 配置類@Configuration加與不加的區別
- 重復beanName覆寫原則
- 回圈依賴
- 如何解決回圈依賴/為什么要有二級快取和三級快取
- Spring三級快取解決setter方式的回圈依賴原理
- BeanCurrentlyInCreationException
- 監聽器Listener
- Spring事件監聽器的原理
- Spring是怎樣避免讀取到不完整的Bean
- 推斷構造方法底層原理
- SpringAOP底層原理
- IOC容器加載程序及Bean生命周期
- Spring事務
- Spring事務的7種傳播行為
- 1、PROPAGATION_REQUIRED
- 2、PROPAGATION_SUPPORTS
- 3、PROPAGATION_MANDATORY
- 4、PROPAGATION_REQUIRES_NEW
- 5、PROPAGATION_NOT_SUPPORTED
- 6、PROPAGATION_NEVER
- 7、PROPAGATION_NESTED
- 8、總結
- 用法
- Spring事務不生效
- Transaction rolled back because it has been marked as rollback-only
- 切面類內的事務
- 自定義AOP與宣告式事務執行順序問題
- Spring事務的7種傳播行為
SpringIOC原始碼
Spring原始碼大綱 https://www.processon.com/view/link/5f5075c763768959e2d109df
IOC加載流程圖 https://www.processon.com/view/link/5f15341b07912906d9ae8642
Spring回圈依賴圖 https://www.processon.com/view/link/5f1fb2cf1e08533a628a7b4c
Spring Xmind 小結

IOC容器加載程序及Bean生命周期
BeanFactory和ApplicationContext的區別
Spring Framework 中文檔案
BeanFactory和ApplicationContext的區別就是工廠和4S店的區別
BeanFactory是Bean的工廠,spring的頂層核心介面,沒有BeanFactory就沒有Bean的存在,工廠只負責按照要求生產Bean,Bean的定義資訊,要生產成什么樣由下家(ApplicationContext)說了算,
ApplicationContext面向的是用戶,所以需要更好的服務用戶,不僅要提供Bean和呼叫工廠去生產Bean還要提供一系列人性化的服務如國際化、加載Bean定義、監聽器等等,怎么生成Bean的事交給工廠去做,
但是ApplicationContext也依賴工廠,沒有工廠他沒有辦法提供Bean,沒有辦法更好的服務用戶,所以它需要繼承工廠;ApplicationContext 繼承自 BeanFactory,但是它不應該被理解為 BeanFactory 的實作類,而是說其內部持有一個實體化的 BeanFactory(DefaultListableBeanFactory),以后所有的 BeanFactory 相關的操作其實是給這個實體來處理的,DefaultListableBeanFactory也有注冊bean定義的能力,
BeanDefinition是bean在spring中的描述,有了BeanDefinition我們就可以創建Bean,
BeanDefinition介面: 頂級基礎介面,用來描述Bean,里面存放Bean元資料,比如Bean類名、scope、屬性、建構式引數串列、依賴的bean、是否是單例類、是否是懶加載等一些列資訊,

Spring IOC容器的具體加載程序
// spring的配置方式一般有三種:注解配置/xml配置/JavaConfig配置
// 創建spring 容器: ClassPathXmlApplicationContext構造器 / AnnotationConfigApplicationContext構造器
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this(); // 1.準備作業
register(componentClasses); // 2.注冊配置類
refresh(); // 3.IOC容器重繪
}
// 1. 這是一個有參的構造方法,可以接收多個配置類,不過一般情況下,只會傳入一個配置類,
// 2. 這個配置類有兩種情況,一種是傳統意義上的帶上@Configuration注解的配置類,還有一種是沒有帶上@Configuration,但是帶有@Component,@Import,@ImportResouce,@Service, @ComponentScan等注解的配置類,在Spring內部把前者稱為Full配置類,把后者稱之為Lite配置類,
- 1.準備作業,程序中主要實體化的物件
GenericApplicationContext#beanFactory = new DefaultListableBeanFactory()
父類建構式為spring背景關系,實體化了beanFactory:DefaultListableBeanFactory
DefaultListableBeanFactory 是最底層,實作功能最全的BeanFactory
AnnotationConfigApplicationContext#reader = new AnnotatedBeanDefinitionReader(this);
初始化注解模式下bean定義掃描器, 注冊了一些創世紀后置處理器,比如:
決議我們配置類的后置處理器ConfigurationClassPostProcessor(它是BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor的實作,用來處理配置類決議@Configuration、@ComponentScan、@Import等);
AutowiredAnnotationBeanPostProcessor(BeanPostProcessor的實作,決議@Autowired);
AnnotationAwareOrderComparator(Order注解相關)
AnnotationConfigApplicationContext#scanner = new ClassPathBeanDefinitionScanner(this);
初始化classPath型別bean定義掃描器,可以用來掃描指定包下所有類,并將符合過濾條件的類(設定this.includeFilters =AnnotationTypeFilter(Component.class))封裝成 beanDefinition 注冊到容器,適用于沒有指定配置類時手動呼叫scan,不是默認的掃描包物件,可忽略
BeanDefinitionReader 讀取
BeanDefinitionScanner 掃描
BeanDefinitionRegistry 注冊
拓展點:
BeanFactoryPostProcessor 修改BeanDefinition
BeanDefinitionRegistryPostProcessor 注冊BeanDefinition eg:集成Mybatis
- 2.注冊配置類
將配置類注冊到beanDefinitionMap中,此時beanDefinitionMap中只有配置類、創世紀后置處理器
- 3.IOC容器重繪程序-原始碼debug
AbstractApplicationContext#refresh()
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()
Spring決議xml組態檔,將要創建的所有bean配置資訊保存起來;javaconfig只重繪該beanFactory包括 beanDefinitionMap和beanDefinitionNames等
invokeBeanFactoryPostProcessors(beanFactory)
執行BeanFactoryPostProcessor 呼叫BeanFactory的后置處理器,真正的掃描包物件scanner,掃描class,決議成beanDefinition并注冊到beanDefinitionMap
registerBeanPostProcessors(beanFactory)
注冊Bean后置處理器
finishBeanFactoryInitialization(beanFactory)
實體化所有剩余的(非延遲初始化)單例
beanFactory.preInstantiateSingletons()
獲取容器中所有bean定義的名稱;
合并BeanDefinition生成RootBeanDefinition;
判斷beanDefinition是不是抽象的&&不是單例的&&不是懶加載的;
判斷是FactoryBean則創建,SmartFactoryBean可呼叫工廠方法getobject回傳內部物件,不是FactoryBean呼叫getBean();getBean(&beanName)回傳的是FactoryBean,beanDefinitionMap中只有FactoryBean,但單例池最侄訓生成2個bean
// 核心!!!getBean方法
AbstractBeanFactory#getBean呼叫#doGetBean
DefaultSingletonBeanRegistry#getSingleton(beanName) 為空,往下呼叫
DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory) 鉤子函式呼叫
AbstractAutowireCapableBeanFactory#createBean呼叫#doCreateBean,再依次呼叫
AbstractAutowireCapableBeanFactory#resolveBeanClass 加載類 先加載當前BeanDefinition所對應的class
AbstractAutowireCapableBeanFactory#createBeanInstance 實體化(addSingletonFactory放入快取)
AbstractAutowireCapableBeanFactory#populateBean 屬性注入,填充屬性/注入依賴
AbstractAutowireCapableBeanFactory#initializeBean 初始化,執行aware介面中的方法,完成AOP代理,最后把最終生成的代理物件放入單例池,下次getBean時就直接從單例池拿即可
// AbstractAutowireCapableBeanFactory#createBeanInstance,bean的實體化程序:
// 使用合適的實體化策略來創建新的實體:工廠方法、建構式自動注入、簡單初始化
1.首先判斷BeanDefinition中是否設定了Supplier,如果設定了則呼叫Supplier的get()得到物件,
2.如果沒有設定Supplier,檢查BeanDefinition中是否設定了factoryMethod,然后呼叫工廠方法得到物件,@Bean所注解的方法就是factoryMethod,配置類為factoryBean
3.推斷構造方法:根據class推斷構造方法,根據推斷出來的構造方法,反射得到一個物件
// DefaultSingletonBeanRegistry#getSingleton(beanName)
一級快取二級快取beanName不存在且標記為正在創建,加鎖,取出三級快取的Bean工廠呼叫getObject方法拿到單例物件或代理物件,將單例物件添加到二級快取中,移除三級快取單例工廠中對應的singletonFactory
// AbstractAutowireCapableBeanFactory#addSingletonFactory放入三級快取
InstantiationAwareBeanPostProcessor#postProcessAfterInitialization是后置處理器的擴展點,允許在物件回傳之前修改甚至替換bean;
如果存在AOP,回傳的不是原始的Bean實體,而是實作AOP方法的代理類;
只用二級快取會將AOP中創建代理物件的時機提前,設計之初就是讓Bean在生命周期的最后一步完成代理而不是在實體化后就立馬完成代理;
回圈依賴發生時提前代理,沒有回圈依賴代理方式不變,依然是初始化以后代理;
有ab物件,getBean(a)在加載b的流程中如果發生了回圈依賴,就是說b又依賴了a,我們就要對a執行AOP,
提前獲取增強以后的a物件,這樣b物件依賴的a物件就是增強以后的a了,
見https://segmentfault.com/a/1190000023712597
簡述Bean的生命周期



1.利用該類的構造方法來實體化得到一個物件(但是如何一個類中有多個構造方法,Spring則會進行選擇,這個叫做推斷構造方法)
2.得到一個物件后,Spring會判斷該物件中是否存在被@Autowired注解了的屬性,把這些屬性找出來并由Spring進行賦值(依賴注入)
3.依賴注入后,Spring會判斷該物件是否實作了BeanNameAware介面、BeanClassLoaderAware介面、BeanFactoryAware介面,如果實作了,就表示當前物件必須實作該介面中所定義的setBeanName()、setBeanClassLoader()、setBeanFactory()方法,那Spring就會呼叫這些方法并傳入相應的引數(Aware回呼)
4.Aware回呼后,Spring會判斷該物件中是否存在某個方法被@PostConstruct注解了,如果存在,Spring會呼叫當前物件的此方法(初始化前)
5.緊接著,Spring會判斷該物件是否實作了InitializingBean介面,如果實作了,就表示當前物件必須實作該介面中的afterPropertiesSet()方法,那Spring就會呼叫當前物件中的afterPropertiesSet()方法(初始化)
6.最后,Spring會判斷當前物件需不需要進行AOP,如果不需要那么Bean就創建完了,如果需要進行AOP,則會進行動態代理并生成一個代理物件做為Bean(初始化后)
后置處理器的九次呼叫
| 時機 | 方法入口 | 實作的介面 instanceof | eg |
|---|---|---|---|
| 實體化前 | #resolveBeforeInstantiation | Instantiation AwareBeanPostProcessor | AnnotationAwareAspectJAutoProxyCreator決議aop切面資訊進行快取 |
| 實體化-推斷構造器 | #createBeanInstance | SmartInstantiation AwareBeanPostProcessor | 通過bean的后置處理器進行選舉出合適的建構式物件 |
| 實體化后 | #applyMergedBean DefinitionPostProcessors | MergedBeanDefinition PostProcessor | @AutoWired的注解的預決議 可以修改BeanDefinition |
| 實體化后 | #getEarlyBeanReference | SmartInstantiation AwareBeanPostProcessor | 解決回圈依賴 |
| 填充屬性前 | #populateBean | InstantiationAware BeanPostProcessor | 用戶可以自定義屬性注入 |
| 填充屬性前 | #populateBean | InstantiationAware BeanPostProcessor | 可以修改填充屬性的值 處理@AutoWired |
| 初始化 | #initializeBean | BeanPostProcessor | 例如@PostConstruct |
| 初始化 | #initializeBean | BeanPostProcessor | aop和事務都會在這里生成代理物件 |
| 銷毀bean容器 | InitDestroyAnnotationBeanPostProcessor |
BeanDefinition
BeanDefinition是Spring頂層核心介面封裝了生產Bean的一切原料,BeanDefinition中存在很多屬性用來描述一個Bean的特點,比如:
- class,表示Bean型別
- scope,表示Bean作用域,單例或原型等
- lazyInit:表示Bean是否是懶加載
- initMethodName:表示Bean初始化時要執行的方法
- destroyMethodName:表示Bean銷毀時要執行的方法
內置后置PostProcess處理器
BeanFactoryPostProcessor的呼叫程序/配置類的決議程序
https://www.processon.com/view/link/5f18298a7d9c0835d38a57c0
呼叫bean工廠的后置處理器
1)BeanDefinitionRegistryPostProcessor(先被執行) 它是能注冊BeanDefinition 的子介面
所有的bean定義資訊將要被加載到容器中,Bean實體還沒有被初始化
2)BeanFactoryPostProcessor(后執行) 它是修改BeanDefinition 但不能注冊BeanDefinition 的父介面
所有的Bean定義資訊已經加載到容器中,但是Bean實體還沒有被初始化
修改BeanDefinition 即通過設定bean物件的型別 setBeanClassName 偷天換日
1.去容器中獲取BeanDefinitionRegistryPostProcessor的bean的處理器名稱
// 判斷是否實作了PriorityOrdered介面的,getBean,呼叫他的后置處理方法
2.去容器中獲取BeanDefinitionRegistryPostProcessor的bean的處理器名稱
// 判斷是否實作了Ordered介面的,getBean,呼叫他的后置處理方法
3.去容器中獲取BeanDefinitionRegistryPostProcessor的bean的處理器名稱
// 剩下的沒有被處理過的,getBean,呼叫他的后置處理方法
4.去容器中獲取BeanDefinitionRegistryPostProcessor,同時實作了BeanFactoryPostProcessor的bean的處理器名稱
// getBean 呼叫他的后置處理方法
123后置處理方法 #postProcessBeanDefinitionRegistry
4后置處理方法 #postProcessBeanFactory
// ConfigurationAnnotationProcessor 會走第一步、第四步
5.獲取容器中所有的 BeanFactoryPostProcessor
6.先呼叫BeanFactoryPostProcessor實作了 PriorityOrdered介面的
7.再呼叫BeanFactoryPostProcessor實作了 Ordered的
8.呼叫沒有實作任何方法介面的 后置處理方法 BeanFactoryPostProcessor#postProcessBeanFactory
- BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 做了什么?
- 回圈bean定義名稱names找到配置類,判斷其是完全的配置類還是一個非正式的配置類;
- 創建一個配置類決議器物件真正地決議配置類,parser.parse(),把我們掃描出來的類添加到beanDefinition的集合即beanDefinitionMap(@ComponentScan);
- 新建一個ConfigurationClassBeanDefinitionReader,把我們決議出來的配置類configClasses(決議出來的配置類)注冊到容器中(@Import、@Bean、@ImportResources、ImportBeanDefinition注解)
- BeanDefinitionRegistryPostProcessor#postProcessBeanFactory 做了什么?
enhanceConfigurationClasses 配置類增強
- 提前生成配置類單例bean引發的問題
自定義beanFactory后置處理器會在第一步被ConfigurationClassPostProcessor掃描添加到 beanFactory 的BeanDefinitionMap,在第三步時被getBean,再呼叫自定義beanFactory后置處理器的后置處理方法;如果是在配置類里@Bean注冊的自定義beanFactory后置處理器,會getBean(配置類),提前生成配置類,沒有通過配置類增強、配置類增強失敗,
解決:static關鍵字修飾@Bean方法回傳為BeanPostProcessor、BeanFactoryPostProcessor等型別的方法
// 方式1:
@Configuration
class AppConfig {
AppConfig() { System.out.println("AppConfig init...");}
@Bean
BeanDefinitionRegistryPostProcessor postProcessor() {
return new MyBeanDefinitionRegistryPostProcessor();
}
}
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
MyBeanDefinitionRegistryPostProcessor() {System.out.println("MyBeanDefinitionRegistryPostProcessor init...");}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
// 控制臺輸出
AppConfig init...
MyBeanDefinitionRegistryPostProcessor init...
// 警告
org.springframework.context.annotation.ConfigurationClassPostProcessor enhanceConfigurationClasses
Cannot enhance @Configuration bean definition 'appConfig' since
its singleton instance has been created too early.
The typical cause is a non-static @Bean method
with a BeanDefinitionRegistryPostProcessor return type:
Consider declaring such methods as 'static'.
// 方式2:
@Configuration
class AppConfig {
AppConfig() { System.out.println("AppConfig init...");}
}
@Component
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
MyBeanDefinitionRegistryPostProcessor() {System.out.println("MyBeanDefinitionRegistryPostProcessor init...");}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
// 控制臺輸出
MyBeanDefinitionRegistryPostProcessor init...
AppConfig init...
配置類@Configuration加與不加的區別
配置類加@Configuration的話,@Bean里方法名獲取物件,物件只實體化一次
在BeanDefinitionRegistryPostProcessor中第4步呼叫postProcessBeanFactory方法時給配置類創建cglib動態代理,指定配置時不加Configuration也行,但加了@Configuration會根據方法名從單例池拿getBean,這樣就有bean和bean之間的參考,而不是重復加載bean
@Configuration為Full配置類,經過enhance增強,所有的@Bean方法都被BeanMethodInterceptor攔截
重復beanName覆寫原則
loadBeanDefinitionsForBeanMethod
1、配置類的名字相同,則報錯(同名@Component)
2、同一個配置類中的@Bean名字相同,則回傳true,意思是以先加載的@Bean方法為準
3、不同的配置類中的@Bean名字相同,則回傳false,意思是可以被覆寫,已后被加載的@Bean方法為準

回圈依賴
如何解決回圈依賴/為什么要有二級快取和三級快取
https://note.youdao.com/ynoteshare/index.html?id=01ec86d7955e2c9cd45c1c0e22f07535&type=note&_time=1635692387674
三級快取結構
Map<String,Object> singletonObjects // 一級快取
Map<String,Object> earlySingletonObjects // 二級快取
Map<String,ObjectFactory> singletonFactories // 三級快取
一級快取的作用:存放可用的成品bean;
二級快取的作用:為了將成熟Bean和純凈Bean分離(未注入屬性),避免多執行緒下讀取到不完整的Bean;存放半成品bean,半成品bean即已經呼叫完構造但是還沒有注入屬性和初始化;
三級快取的作用:用來生產半成品的bean,與getbean方法解耦,能解決aop增強下的回圈依賴;存放函式介面/鉤子函式,函式介面實作創建動態代理呼叫BeanPostProcessor,即其要加強的aop處理(為了避免重復創建,呼叫會回傳動態代理物件或者原實體,再存盤在二級快取);
真正的解決回圈依賴是靠二級快取,不用三級快取也可以解決回圈依賴,但這樣就造成了在實體化后就立馬完成代理,違背了最后一步完成代理的原則;
在創建bean的時候,在哪里通過什么方式創建了動態代理:通過BeanPostProcessor創建動態代理,在初始化之后或在出現回圈依賴時實體化之后(實體化 -> 屬性注入 -> 初始化)
發生回圈依賴會用到二級快取,普通依賴程序只用到一三級快取
Spring三級快取解決setter方式的回圈依賴原理




為什么Spring不能解決構造器的回圈依賴?
從流程圖應該不難看出來,在Bean呼叫構造器實體化之前,一二三級快取并沒有Bean的任何相關資訊,在實體化之后才放入三級快取中,因此當getBean的時候快取并沒有命中,這樣就拋出了回圈依賴的例外了,
為什么多例Bean不能解決回圈依賴?
我們的bean是單例的,而且是欄位注入(setter注入)的,單例意味著只需要創建一次物件,后面就可以從快取中取出來,欄位注入,意味著我們無需呼叫構造方法進行注入,
- 如果是原型bean,那么就意味著每次都要去創建物件,無法利用快取;
- 如果是構造方法注入,那么就意味著需要呼叫構造方法注入,也無法利用快取,
如何進行拓展?
bean可以通過實作SmartInstantiationAwareBeanPostProcessor介面getEarlyBeanReference方法進行拓展
BeanCurrentlyInCreationException
spring的aop代理(包括@Aysnc,@Transactional),一般都是在屬性賦值時中呼叫#postProcessAfterInitialization方法創建的代理物件,這個代理程序是不涉及到回圈參考的情況下執行;在回圈參考下會提前創建代理物件#getEarlyBeanReference(ab回圈依賴,a通過ObjectFactory提前曝光自己,b通過getObject獲取到這個提前曝光的a物件填充屬性,該earlySingletonReference放進二級快取,只有回圈依賴下才會放入二級快取),
如果回圈參考下提前創建了代理物件,經過initializeBean初始化又產生代理物件(exposedObject與earlySingletonReference兩者不等拋BeanCurrentlyInCreationException例外,@Aysnc會發生,@Transactional不會發生);解決方式:加上@lazy
spring回圈依賴在 構造器注入下會拋例外BeanCurrentlyInCreationException 可以用基于屬性注入
監聽器Listener
- Spring事件體系包括三個組件:事件,事件監聽器,事件廣播器,基于觀察者模式,
事件(ApplicationEvent)負責對應相應監聽器,事件源發生某事件是特定事件監聽器被觸發的原因,事件分為 Spring內置事件 、自定義事件(繼承ApplicationEvent)
事件監聽器(ApplicationListener)對應于觀察者模式中的觀察者,監聽器監聽特定事件,并在內部定義了事件發生后的回應邏輯, 分為 基于介面(繼承ApplicationListener)、基于注解 (@EventListener)
事件廣播器(ApplicationEventMulticaster)對應于觀察者模式中的被觀察者/主題, 負責通知觀察者對外提供發布事件和增刪事件監聽器的介面,維護事件和事件監聽器之間的映射關系,并在事件發生時負責通知相關監聽器,
發布者呼叫applicationContext.publishEvent(msg),將事件發送給了EventMultiCaster,而后由 EventMultiCaster注冊著所有的Listener,然后根據事件型別決定轉發給那個Listener,
Spring事件監聽器的原理
IOC容器重繪介面refresh方法
initApplicationEventMulticaster 創建事件多播器(默認的事件廣播器SimpleApplicationEventMulticaster)
registerListeners 把我們的事件監聽器名字注冊到多播器上,通過多播器進行播發早期事件
finishRefresh 容器重繪,發布重繪事件(ContextRefreshedEvent)
Spring提供的事件機制默認是同步的(SimpleApplicationEventMulticaster#multicastEvent),如果想用異步的可以自己實作ApplicationEventMulticaster介面,并在Spring容器中注冊id為applicationEventMulticaster的Bean
Spring是怎樣避免讀取到不完整的Bean
防止多執行緒下Spring讀取到不完整Bean加了兩把鎖
一把鎖放在getSingleton()方法三級快取,第二個執行緒阻塞直到第一個執行緒把二三級快取洗掉完;
一把鎖放在getSingleton(,)方法,先從單例池再拿一遍單例物件(double check防重復創建單例bean)
怎么樣可以在所有Bean創建完后做擴展代碼?
ContextRefreshedEvent/SmartInitializingSingleton
推斷構造方法底層原理
Spring的判斷邏輯如下:
1.如果一個類只存在一個構造方法,不管該構造方法是無參構造方法,還是有參構造方法,Spring都會用這個構造方法
2.如果一個類存在多個構造方法
a.這些構造方法中,存在一個無參的構造方法,那么Spring就會用這個無參的構造方法
b.這些構造方法中,不存在一個無參的構造方法,那么Spring就會報錯
c.如果某個構造方法上加了@Autowired注解,Spring就會用這個加了@Autowired注解構造方法了
SpringAOP底層原理
https://www.processon.com/view/link/5faa4ccce0b34d7a1aa2a9a5
Bean的生命周期 : UserService.class -> 無參構造方法(推斷構造方法)-> 普通物件 -> 依賴注入(屬性賦值) -> 初始化前 -> 初始化 -> 初始化后 -> 代理物件(UserServiceProxy) -> Bean
如何判斷當前Bean物件需不需要進行AOP:
1.找出所有的切面Bean
2.遍歷切面中的每個方法,看是否寫了@Before、@After等注解
3.如果寫了,則判斷所對應的Pointcut是否和當前Bean物件的類是否匹配
4.如果匹配則表示當前Bean物件有匹配的的Pointcut,表示需要進行AOP
利用cglib進行AOP的大致流程:
1.生成代理類UserServiceProxy,代理類繼承UserService
2.代理類中重寫了父類的方法,比如UserService中的test()方法
3.代理物件持有普通物件的參考,UserServiceProxy.target = 普通物件
4.呼叫代理類的test方法 -> 先執行切面邏輯@Before,再執行target.test方法
Spring常見代理創建方式:
1.FactoryBean方式創建單個動態代理:
- proxyInterfaces指定需增強的介面;
- target指定需增強的實作類;
- interceptorNames 指定Advice(MethodBeforeAdvice, AfterReturningAdvice)、Interceptor(MethodInterceptor)、Advisor(結合Advice或Interceptor)都行;
- Advice、Interceptor攔截器的粒度只控制到了類級別,類中所有的方法都進行攔截;Advisor攔截器的粒度達到方法級別,通知者切點分為正則匹配/方法名;需要獲取這個代理類
2.autoProxy方式: 根據advisor批量創建自動代理(當Spring發現一個bean需要被切面織入的時候,Spring會自動生成這個bean的一個代理來攔截方法的執行,確保定義的切面能被執行);不需要獲取這個代理類
- BeanPostProcessor手動指定Advice方式,BeanNameAutoProxyCreator指定Advisor,可以使用正則來匹配要創建代理的那些Bean的名字
- BeanPostProcessor自動掃描Advisor方式,DefaultAdvisorAutoProxyCreator
開啟aop:
1.配置類,加入@EnableAspectJAutoProxy注解
2.切面類,加入@Aspect注解,定義一個Pointcut方法(切點:指定哪些類需要被代理),最后定義一系列的增強方法(Advice通知:代理邏輯)
切面類的決議: AspectJAutoProxyRegistrar實作ImportBeanDefinitionRegistrar,在決議配置類到容器時,如果開啟@EnableAspectJAutoProxy注解下,通過registerBeanDefinitions方法為我們容器匯入beanDefinition;該beanDefinition 是 AnnotationAwareAspectJAutoProxyCreator,繼承自AbstractAutoProxyCreator,#findCandidateAdvisors在bean實體化前決議aop切面資訊,生成對應的Advisor物件進行快取
AbstractAdvisorAutoProxyCreator非常強大以及重要,只要Spring容器中存在這個型別的Bean,就相當于開啟了AOP,AbstractAdvisorAutoProxyCreator實際上就是一個BeanPostProcessor,所以在創建某個Bean時,就會進入到它對應的生命周期方法中,比如:在某個Bean初始化之后,會呼叫wrapIfNecessary()方法進行AOP,底層邏輯是AbstractAdvisorAutoProxyCreator #getAdvicesAndAdvisorsForBean 會找到所有的通知器Advisor,然后判斷當前這個Bean是否存在某個Advisor與之匹配(根據Pointcut)AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply ,如果匹配就表示當前這個Bean有對應的切面邏輯,需要進行AOP,需要產生一個代理物件,
// ProxyFactory產生代理物件
{
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(new PointcutAdvisor() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("test");
}
};
}
@Override
public Advice getAdvice() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
@Override
public boolean isPerInstance() {
return false;
}
});
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();
}
代理物件執行程序: CglibAopProxy.DynamicAdvisedInterceptor#intercept
1.在使用ProxyFactory創建代理物件之前,需要往ProxyFactory先添加Advisor
2.代理物件在執行某個方法時,會把ProxyFactory中的Advisor拿出來和當前正在執行的方法進行匹配篩選
3.把和方法所匹配的Advisor適配成MethodInterceptor
4.把和當前方法匹配的MethodInterceptor鏈,以及被代理物件、代理物件、代理類、當前Method物件、方法引數封裝為MethodInvocation物件
5.呼叫MethodInvocation的proceed()方法,開始執行各個MethodInterceptor以及被代理物件的對應方法
6.按順序呼叫每個MethodInterceptor的invoke()方法,并且會把MethodInvocation物件傳入invoke()方法
7.直到執行完最后一個MethodInterceptor了,就會呼叫invokeJoinpoint()方法,從而執行被代理物件的當前方法
Spring事務
當我們在某個方法上加了@Transactional注解后,就表示該方法在呼叫時會開啟Spring事務,而這個方法所在的類所對應的Bean物件會是該類的代理物件,
事務注解@EnableTransactionManagement 為我們的容器匯入了添加了兩個Bean:
- AutoProxyRegistrar 匯入的 InfrastructureAdvisorAutoProxyCreator :開啟自動代理
- ProxyTransactionManagementConfiguration 匯入的 BeanFactoryTransactionAttributeSourceAdvisor(Advisor)、AnnotationTransactionAttributeSource(pointcut)、TransactionInterceptor(advice)
事務是基于AOP完成的,判斷bean生命周期是否開啟aop:找到所有的通知器物件Advisor,判斷這個bean是否與Advisor匹配:通過pointcut物件(決議@Transactional注解,匹配邏輯為判斷該Bean的類上是否存在@Transactional注解,或者類中的某個方法上是否存在@Transactional注解)、Advice物件(TransactionalInterceptor 代理的邏輯)
該代理物件在執行某個方法時,會再次判斷當前執行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配則執行該Advisor中的TransactionInterceptor的invoke()方法,執行基本流程為:
Spring事務的代理物件執行某個方法時的步驟:
1.判斷當前執行的方法是否存在@Transactional注解
2.如果存在,則利用事務管理器(TransactionMananger)新建一個資料庫連接
3.修改資料庫連接的autocommit為false,資料庫連接放入threadlocal
4.執行target.test(),執行程式員所寫的業務邏輯代碼,也就是執行sql
5.執行完了之后如果沒有出現例外,則提交,否則回滾
Spring事務的7種傳播行為
https://blog.csdn.net/weixin_39625809/article/details/80707695
事務傳播行為(propagation behavior)指的就是當一個事務方法被另一個事務方法呼叫時,這個事務方法應該如何進行,例如:methodA事務方法呼叫methodB事務方法時,methodB是繼續在呼叫者methodA的事務中運行呢,還是為自己開啟一個新事務運行,這就是由methodB的事務傳播行為決定的,
1、PROPAGATION_REQUIRED
如果存在一個事務,則支持當前事務,如果沒有事務則開啟一個新的事務,
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB(){}
單獨呼叫A、B方法都會開啟一個新的事務
A呼叫B方法、B呼叫A方法都會加入到同一個事務
2、PROPAGATION_SUPPORTS
如果存在一個事務,支持當前事務,如果沒有事務,則非事務的執行,
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB(){}
單獨呼叫B方法不會開啟事務
A呼叫B方法,B會加入這個事務
3、PROPAGATION_MANDATORY
如果存在一個事務,支持當前事務,如果沒有事務,則拋出例外,
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){}
@Transactional(propagation = Propagation.MANDATORY)
public void methodB(){}
單獨呼叫B方法會拋IllegalTransactionStateException例外
A呼叫B方法,B會加入這個事務
4、PROPAGATION_REQUIRES_NEW
如果存在一個事務,先將這個存在的事務掛起,再開啟一個新的事務,如果沒有事務則開啟一個新的事務,
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB(){}
單獨呼叫B方法開啟事務
A方法(外層事務)呼叫B方法(內層事務),B會開啟一個新的事務
外層事務回滾,內層事務仍然提交
5、PROPAGATION_NOT_SUPPORTED
總是非事務地執行,并掛起任何存在的事務,
6、PROPAGATION_NEVER
總是非事務地執行,如果存在一個活動事務,則拋出例外,
7、PROPAGATION_NESTED
如果存在一個事務,依賴該事務,作為該事務的子事務,如果沒有事務則開啟一個新的事務,
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){}
@Transactional(propagation = Propagation.NESTED)
public void methodB(){}
單獨呼叫B方法開啟事務
A方法(外層事務)呼叫B方法(內層事務),nested屬于子事務,依賴于外層,有單獨的保存節點
外層事務的回滾可以引起內層事務的回滾,內層事務例外的回滾可導致外層事務的回滾(如果沒有吞掉例外)
也可不導致外層事務的回滾(吞掉例外),外層事務自行決定是commit還是rollback
8、總結
case1:如果A捕獲B的例外,并且未向上拋例外
case2:如果A未捕獲B的例外,則默認將B的例外向上拋
REQUIRES_NEW和NESTED:內層事務拋例外一定會回滾,外層事務可以通過控制吞不吞內層事務拋的例外來決定是否回滾
| 例外狀態 | REQUIRED | REQUIRES_NEW | NESTED |
|---|---|---|---|
| methodA拋例外 methodB正常 | 均回滾 | A回滾,B正常提交 | 均回滾 |
| methodA正常 methodB拋例外 | case1:均回滾拋例外 case2:均回滾 | case1:A正常提交B回滾 case2:均回滾 | case1:A正常提交B回滾 case2:均回滾 |
| methodA拋例外 methodB拋例外 | 均回滾 | 均回滾 | 均回滾 |
| methodA正常 methodB正常 | 均提交 | 均提交 | 均提交 |
用法
@Transactional 可以作用于介面、介面方法、類以及類方法上,當作用于類上時,該類的所有 public方法將都具有該型別的事務屬性,同時,我們也可以在方法級別使用該標注來覆寫類級別的定義,
雖然 @Transactional 注解可以作用于介面、介面方法、類以及類方法上,但是 Spring 建議不要在介面或者介面方法上使用該注解,因為這只有在使用基于介面的代理時它才會生效,另外, @Transactional 注解應該只被應用到 public 方法上,這是由 Spring AOP的本質決定的,如果你在 protected、private或者默認可見性的方法上使用 @Transactional 注解,這將被忽略,也不會拋出任何例外,
默認情況下,只有來自外部的方法呼叫才會被AOP代理捕獲,也就是,類內部方法呼叫本類內部的其他方法并不會引起事務行為,即使被呼叫方法使用@Transactional注解進行修飾,
@Transactional不做任何配置,默認是對拋出的unchecked例外、Error回滾,checked例外不會回滾,為了讓所有例外都會讓事務啟動可以將 @Transactional配置為 @Transactional(rollbackFor = Exception.class)

Spring事務不生效
- 框架不支持:入口的方法必須是public、事務是否在同一個執行緒里、資料庫引擎設定不對資料庫不支持事務
- 錯誤使用:只對出現運行期例外(java.lang.RuntimeException及其子類)/Error進行回滾、rollbackFor屬性設定錯誤、例外被catch、錯誤地傳播機制
- 代理失效:被final、static關鍵字修飾的類或方法、將注解標注在介面方法上將無法用CGLIB代理、是否通過代理物件只有代理物件呼叫方法才能被攔截
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){}
public void methodB(){}
A方法(事務)呼叫B方法(沒有事務),B方法的例外也會導致AB方法事務的回滾
B方法(沒有事務)呼叫A方法(事務),事務失效
Transaction rolled back because it has been marked as rollback-only
Spring的@Transactional 可以注解到方法上或者類上從而開啟事務,而正確呼叫類事務方法是通過容器呼叫,即@autowird 被注入到其他類中使用,因為此時呼叫方法會被spring容器的 TransactionInterceptor 攔截器攔截
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB(){}
Propagation.REQUIRED實體,默認事務實體不管是否捕獲例外,全部一起回滾
A方法(外層事務)呼叫B方法(內層事務),B方法發現例外了會標記整個事務為roll-back
但如果外層方法捕獲例外正常退出后執行commit事務,此時發現已經標記例外會出錯拋UnexpectedRollbackException
部分失敗,全域回滾屬性為true
解決辦法:在catch塊中添加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 手動回滾
或者內層事務使用propagation = Propagation.NESTED,從而保證內層例外不會影響外層提交
切面類內的事務
含有@Aspect的類在生命周期的第一個bean后置處理器會被標記不處理,在最后一個bean后置處理器它就不會被代理,故aop切面類本身使用不了宣告式事務@Transactional;可以在切面類新寫子方法新建事務,或用publisher.publishEvent發布異步事件新建事務
// 判斷當前事務是否是新事務
TransactionAspectSupport.currentTransactionStatus().isNewTransaction()
// @Order
默認為@Order(value = https://www.cnblogs.com/sudokill/p/Ordered.LOWEST_PRECEDENCE)優先度最低;可以自定義修改切面類的優先級別@Order(value = Ordered.HIGHEST_PRECEDENCE + 1)
自定義AOP與宣告式事務執行順序問題
@SysLog: 自定義AOP,產生系統日志
@Transactional:宣告式事務
//某個service方法
@SysLog("/testService")
@Transactional(propagation = Propagation.REQUIRED)
public Result testService(Info info) {}
//自定義aop
@Aspect
@Component
public class SysLogAspect{
@Around("@annotation(sysLog)")
public Object around(ProceedingJoinPoint point, SysLog sysLog) {
obj = point.proceed();
innerService.do();
}
}
@Transactional(propagation = Propagation.REQUIRED)
public Result innerService(Info info) {}
在controller同時標記@SysLog和@Transactional時候,由于事務注解默認 @EnableTransactionManagement(order=Ordered.LOWEST_PRECEDENCE) 優先級最高,
故先執行事務aop再執行自定義注解aop(先進后出的?)
此時自定義注解代碼SysLogAspect#around與外層方法testService是同一個事務,around子事務方法#innerService會發現已經有一個事務,可選擇掛起或加入事務;
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/518701.html
標籤:Java
上一篇:Resilience4J通過yml設定circuitBreaker
下一篇:LRU演算法簡單實作
