SpringIOC源碼 Spring源碼大綱 https://www.processon.com/view/link/5f5075c763768959e2d109df IOC載入流程圖 https://www.processon.com/view/link/5f15341b07912906d9ae8 ...
目錄
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的區別
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方法為準
迴圈依賴
如何解決迴圈依賴/為什麼要有二級緩存和三級緩存
三級緩存結構
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 = 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會發現已經有一個事務,可選擇掛起或加入事務;