本文對Spring相關知識點作了概括整理,包括 Spring 優點、其框架結構、核心思想,並對IoC思想及AOP思想進行手動實現,加強對Spring 核心思想的理解。以後對Spring IoC、AOP 的實現方式和特性進行介紹,並對照源碼理解其實現思路。java
方便解耦,簡化開發web
[注:IoC(下降組件耦合性)、DI(下降業務對象替換的複雜性)]spring
AOP編程的思想數據庫
[注:通用任務集中管理,使得代碼可更好的複用]編程
聲明式事務的支持bootstrap
[注:@Transaction]緩存
輕量級框架,代碼侵入性低,可自由選擇框架部分組件服務器
方便集成各類優秀框架數據結構
IoC 是一個技術思想,而Spring對其進行了實現併發
控制反轉:對象建立(實例化、管理)的權利交給外部環境(Spring框架、IoC容器)
DI(Dependancy Injection)依賴注⼊
IoC和DI描述的是同一件事情(對象實例化及依賴關係維護),只不過角度不同罷了
IoC:站在對象的角度,對象實例化及其管理交給容器
DI:站在容器角度,容器會把對象依賴的其餘對象注入
OOP是一種垂直繼承體系(三大特徵:封裝、繼承和多態)
AOP爲解決縱向重複問題,抽取橫向邏輯代碼,分離業務邏輯和橫切邏輯,解耦合
解決縱向重複問題,抽取橫向邏輯代碼,分離業務邏輯和橫切邏輯,解耦合
純 xml 實現(bean 信息定義所有配置在 xml 中)
// JavaSE 應用啓動
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// 或者讀取絕對路徑下的配置文件(不推薦)
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:/beans.xml");
// JavaWeb 應用
/** 配置 ContextLoaderListener 類(web.xml) 監聽器機制加載 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>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
</listener>
</web-app>
複製代碼
xml + 註解(部分 bean 使用 xml 定義,部分 bean 使用註解定義)
和純 xml 實現一致
純註解 (全部 bean 都是用註解來定義)
// JavaSE 應用啓動
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring.class);
// JavaWeb 應用
/** 配置 ContextLoaderListener 類(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>
<!--告訴ContextloaderListener知道咱們使⽤註解的⽅式啓動ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<paramvalue>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>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
</listener>
</web-app>
複製代碼
BeanFactory是Spring框架中IoC容器的頂層接⼝,它只是⽤來定義⼀些基礎功能,定義⼀些基礎規範,⽽ApplicationContext是它的⼀個⼦接⼝,因此ApplicationContext是具有BeanFactory提供的所有功能的。
ApplicationContext是容器的⾼級接⼝,⽐ BeanFactory要擁有更多的功能,⽐如說國際化⽀持和資源訪問(xml,java配置類)等等
使用無參構造器
<!--配置service對象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
</bean>
複製代碼
使用靜態方法建立
<!--使⽤靜態⽅法建立對象的配置⽅式-->
<bean id="userService" class="com.lagou.factory.BeanFactory" factory-method="getTransferService"></bean>
複製代碼
使用實例化方法建立
<!--使⽤實例⽅法建立對象的配置⽅式-->
<bean id="beanFactory" class="com.lagou.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory" factory-method="getTransferService"></bean>
複製代碼
singleton(單例模式)
單例模式的 bean 對象生命週期與容器相同
prototype(多例模式)
多例模式的 bean 對象,Spring 框架只負責建立,不負責銷燬
依賴注入分類
按注入的方式分類
按注入的數據類型分類
基本類型、String
複雜類型 [Array,List,Set,Map,Properties]
示例( 以set方法注入爲例,構造函數注入一樣 ):
<!-- Array -->
<property name="myArray">
<array>
<value>array1</value>
<value>array2</value>
</array>
</property>
<!-- List -->
<property name="myArray">
<list>
<value>list1</value>
<value>list2</value>
</list>
</property>
<!-- Set -->
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<!-- Map -->
<property name="myMap">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
</map>
</property>
<!-- Properties -->
<property name="myProperties">
<map>
<prop key="key1" value="value1"/>
<prop key="key2" value="value2"/>
</map>
</property>
複製代碼
在List結構的集合數據注⼊時,array , list , set這三個標籤通⽤,另外注值的value標籤內部 能夠直接寫值,也可使⽤bean標籤配置⼀個對象,或者⽤ref標籤引⽤⼀個已經配合的bean 的惟⼀標識。 在Map結構的集合數據注⼊時,map標籤使⽤entry⼦標籤實現數據注⼊,entry標籤可使 ⽤key和value屬性指定存⼊map中的數據。使⽤value-ref屬性指定已經配置好的bean的引⽤。 同時entry標籤中也可使⽤ref標籤,可是不能使⽤bean標籤。⽽property標籤中不能使 ⽤ref或者bean標籤引⽤對象
其餘 Bean 類型
xml 與註解相結合模式下,一般第三方jar 中的bean 定義在 xml 中,本身開發的 bean 定義使用註解
配置方面
IoC
xml形式 | 對應的註解形式 |
---|---|
<bean> 標籤 | @Component("accountDao"),註解加在類上 bean的id屬性內容直接配置在註解後⾯若是不配置,默認定義個這個bean的id爲類 的類名⾸字⺟⼩寫; 另外,針對分層代碼開發提供了@Componenet的三種別名@Controller、 @Service、@Repository分別⽤於控制層類、服務層類、dao層類的bean定義,這 四個註解的⽤法徹底⼀樣,只是爲了更清晰的區分⽽已 |
scope屬性 | @Scope("prototype"),默認單例,註解加在類上 |
標籤的 init-method 屬性 | @PostConstruct,註解加在⽅法上,該⽅法就是初始化後調⽤的⽅法 |
destory-method 屬性 | @PreDestory,註解加在⽅法上,該⽅法就是銷燬前調⽤的⽅法 |
DI
做用:
BeanFactory接口:容器頂級接口,定義容器的一些基礎行爲
FactoryBean接口:藉助他自定義Bean,做用相似於 Bean 建立方式的靜態方法和實例化方法
使用方法
建立 Bean 對應的 Factory 類,實現 FactoryBean 接口,重寫 getObject() 、getObjectType() 和 isSingleton()
在 xml 中配置該 Factory 類
容器初始化過程當中 會自動實例化 Factory 對象並調用其 getObject() 並管理
注:若是須要 Factory 對象,可獲取容器中的 &${id} 對象
<bean id="testBean" class="com.test.TestBeanFactory"></bean>
<!-- getBean("testBean") 獲取的是 TestBean 對象 getBean("&testBean") 獲取的是 TestBeanFactory 對象 -->
複製代碼
Spring 提供兩種後置處理 bean 的擴展接口
BeanPostProcessor:Bean 對象實例化且裝配後調用
該接口提供兩個方法:
BeanFactoryPostProcessor:BeanFactory 初始化後(bean 還未實例化,此時 bean 剛被解析成 BeanDefinition對象)調用
此接口提供了⼀個⽅法,⽅法參數爲ConfigurableListableBeanFactory,該參數類型定義了⼀些⽅法其中有個⽅法名爲getBeanDefinition的⽅法,咱們能夠根據此⽅法,找到咱們定義bean 的 BeanDefinition對象。而後咱們能夠對定義的屬性進⾏修改。
**BeanDefinition對象:**咱們在 XML 中定義的 bean標籤,Spring 解析 bean 標籤成爲⼀個 JavaBean, 這個JavaBean 就是 BeanDefinition
![](img/Spring IoC容器繼承體系.jpg)
Bean對象建立的⼏個關鍵時機點代碼層級的調⽤都在 AbstractApplicationContext 類 的 refresh ⽅法中
關鍵點 | 觸發代碼 |
---|---|
構造器 | refresh#finishBeanFactoryInitialization(beanFactory)(beanFactory) |
BeanFactoryPostProcessor 初始化 | refresh#invokeBeanFactoryPostProcessors(beanFactory) |
BeanFactoryPostProcessor ⽅法調⽤ | refresh#invokeBeanFactoryPostProcessors(beanFactory) |
BeanPostProcessor 初始化 | registerBeanPostProcessors(beanFactory) |
BeanPostProcessor ⽅法調⽤ | refresh#finishBeanFactoryInitialization(beanFactory) |
源碼以下:
public void refresh() throws BeansException, IllegalStateException {
// 對象鎖加鎖
synchronized (this.startupShutdownMonitor) {
/* Prepare this context for refreshing. 刷新前的預處理 表示在真正作refresh操做以前須要準備作的事情: 設置Spring容器的啓動時間, 開啓活躍狀態,撤銷關閉狀態 驗證環境信息裏一些必須存在的屬性等 */
prepareRefresh();
/* Tell the subclass to refresh the internal bean factory. 獲取BeanFactory;默認實現是DefaultListableBeanFactory 加載BeanDefition 並註冊到 BeanDefitionRegistry */
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/* Prepare the bean factory for use in this context. BeanFactory的預準備工做(BeanFactory進行一些設置,好比context的類加載器等) */
prepareBeanFactory(beanFactory);
try {
/* Allows post-processing of the bean factory in context subclasses. BeanFactory準備工做完成後進行的後置處理工做 */
postProcessBeanFactory(beanFactory);
/* Invoke factory processors registered as beans in the context. 實例化實現了BeanFactoryPostProcessor接口的Bean,並調用接口方法 */
invokeBeanFactoryPostProcessors(beanFactory);
/* Register bean processors that intercept bean creation. 註冊BeanPostProcessor(Bean的後置處理器),在建立bean的先後等執行 */
registerBeanPostProcessors(beanFactory);
/* Initialize message source for this context. 初始化MessageSource組件(作國際化功能;消息綁定,消息解析); */
initMessageSource();
/* Initialize event multicaster for this context. 初始化事件派發器 */
initApplicationEventMulticaster();
/* Initialize other special beans in specific context subclasses. 子類重寫這個方法,在容器刷新的時候能夠自定義邏輯;如建立Tomcat,Jetty等WEB服務器 */
onRefresh();
/* Check for listener beans and register them. 註冊應用的監聽器。就是註冊實現了ApplicationListener接口的監聽器bean */
registerListeners();
/* Instantiate all remaining (non-lazy-init) singletons. 初始化全部剩下的非懶加載的單例bean 初始化建立非懶加載方式的單例Bean實例(未設置屬性) 填充屬性 初始化方法調用(好比調用afterPropertiesSet方法、init-method方法) 調用BeanPostProcessor(後置處理器)對實例bean進行後置處理 */
finishBeanFactoryInitialization(beanFactory);
/* Last step: publish corresponding event. 完成context的刷新。主要是調用LifecycleProcessor的onRefresh()方法,而且發佈事件(ContextRefreshedEvent) */
finishRefresh();
}catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
複製代碼
注:參考IoC 循環依賴時序圖
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
// 全部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);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
複製代碼
如源碼:
單例 bean 構造器參數循環依賴(⽆法解決)
prototype 原型 bean循環依賴(⽆法解決)
對於原型bean的初始化過程當中不管是經過構造器參數循環依賴仍是經過setXxx⽅法產⽣循環依 賴,Spring都 會直接報錯處理。
單例bean經過setXxx或者@Autowired進⾏循環依賴
Spring 的循環依賴的理論依據基於 Java 的引⽤傳遞,當得到對象的引⽤時,對象的屬性是能夠延後設置的,可是構造器必須是在獲取引⽤以前
Spring 實現AOP 思想使用的是動態代理技術
默認狀況下
若是 被代理類 實現接口 ⇒ 選擇 JDK 動態代理
不然 ⇒ 選擇 CGLIB 動態代理
可經過配置方式設置強制使用 CGLIB 動態代理
須要注意的點:
切入點表達式
指的是遵循特定語法結構的字符串,其 做⽤是⽤於對符合語法格式的鏈接點進⾏加強。
// 示例
// 全限定⽅法名 訪問修飾符 返回值 包名.包名.包名.類名.⽅法名(參數列表)
// 全匹配⽅式:
public void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account) // 訪問修飾符能夠省略
void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)
// 返回值可使⽤*,表示任意返回值
* com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)
// 包名可使⽤.表示任意包,可是有⼏級包,必須寫⼏個
* ....TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)
// 包名可使⽤..表示當前包及其⼦包
* ..TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)
// 類名和⽅法名,均可以使⽤.表示任意類,任意⽅法
* ...(com.lagou.pojo.Account)
// 參數列表,可使⽤具體類型 基本類型直接寫類型名稱 : int
// 引⽤類型必須寫全限定類名:java.lang.String
// 參數列表可使⽤*,表示任意參數類型,可是必須有參數
* *..*.*(*)
// 參數列表可使⽤..,表示有⽆參數都可。有參數能夠是任意類型
* *..*.*(..)
// 全通配⽅式:
* *..*.*(..)
複製代碼
改變代理方式的配置
使用<aop:config> 標籤配置
<aop:config proxy-target-class="true">
複製代碼
使⽤<aop:aspectj-autoproxy>標籤配置
<!--此標籤是基於XML和註解組合配置AOP時的必備標籤,表示Spring開啓註解配置AOP 的⽀持-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectjautoproxy>
複製代碼
五種通知類型
前置通知:切入點方法執行前執行
<aop:before>
後置通知:切入點方法正常執行後執行
<aop:after-returning>
異常通知:切⼊點⽅法執⾏產⽣異常以後執⾏
<aop:after-throwing>
最終通知:切⼊點⽅法執⾏完成以後,切⼊點⽅法返回以前執⾏(不管是否異常都會執行,finally)
<aop:after>
環繞通知:較特殊,藉助的ProceedingJoinPoint接⼝及其實現類, 實現⼿動觸發切⼊點⽅法的調⽤
XML 中開啓 Spring 對註解 AOP 的⽀持
<!--開啓spring對註解aop的⽀持-->
<aop:aspectj-autoproxy/>
複製代碼
示例
/** * 模擬記錄⽇志 */
@Component
@Aspect
public class LogUtil {
/** * 咱們在xml中已經使⽤了通⽤切⼊點表達式,供多個切⾯使⽤,那麼在註解中如何使⽤呢?* 第⼀步:編寫⼀個⽅法 * 第⼆步:在⽅法使⽤@Pointcut註解 * 第三步:給註解的value屬性提供切⼊點表達式 * 細節: * 1.在引⽤切⼊點表達式時,必須是⽅法名+(),例如"pointcut()"。 * 2.在當前切⾯中使⽤,能夠直接寫⽅法名。在其餘切⾯中使⽤必須是全限定⽅法名。 */
@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");
}
/** * 環繞通知 * @param pjp * @return */
@Around("pointcut()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
//定義返回值
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;
}
}
複製代碼
**編程式事務:**在業務代碼中添加事務控制代碼,這樣的事務控制機制就叫作編程式事務
**聲明式事務:**經過xml或者註解配置的⽅式達到事務控制的⽬的,叫作聲明式事務
**原⼦性(Atomicity):**原⼦性是指事務是⼀個不可分割的⼯做單位,事務中的操做要麼都發⽣,要麼都不發⽣。
是從操做的角度描述的
⼀致性(Consistency): 事務必須使數據庫從⼀個⼀致性狀態變換到另外⼀個⼀致性狀態。
從數據的角度描述
**隔離性(Isolation):**事務的隔離性是多個⽤戶併發訪問數據庫時,數據庫爲每⼀個⽤戶開啓的事務, 每一個事務不能被其餘事務的操做數據所⼲擾,多個併發事務之間要相互隔離。
**持久性(Durability):**持久性是指⼀個事務⼀旦被提交,它對數據庫中數據的改變就是永久性的,接下來即便數據庫發⽣故障 也不該該對其有任何影響。
問題:
數據庫四種隔離級別:
Serializable(串⾏化):可避免髒讀、不可重複讀、虛讀狀況的發⽣。(串⾏化) 最⾼
Repeatable read(可重複讀):可避免髒讀、不可重複讀狀況的發⽣。(幻讀有可能發⽣) 第⼆
該機制下會對要update的⾏進⾏加鎖
Read committed(讀已提交):可避免髒讀狀況發⽣。不可重複讀和幻讀⼀定會發⽣。 第三
Read uncommitted(讀未提交):最低級別,以上狀況均⽆法保證。(讀未提交) 最低
注意:級別依次升⾼,效率依次下降
MySQL的默認隔離級別是:REPEATABLE READ
經常使用前兩條(加粗)
PROPAGATION_REQUIRED | 若是當前沒有事務,就新建⼀個事務,若是已經存在⼀個事務中, 加⼊到這個事務中。這是最常⻅的選擇。 |
---|---|
PROPAGATION_SUPPORTS | ⽀持當前事務,若是當前沒有事務,就以⾮事務⽅式執⾏。 |
PROPAGATION_MANDATORY | 使⽤當前的事務,若是當前沒有事務,就拋出異常。 |
PROPAGATION_REQUIRES_NEW | 新建事務,若是當前存在事務,把當前事務掛起。 |
PROPAGATION_NOT_SUPPORTED | 以⾮事務⽅式執⾏操做,若是當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER | 以⾮事務⽅式執⾏,若是當前存在事務,則拋出異常。 |
PROPAGATION_NESTED | 若是當前存在事務,則在嵌套事務內執⾏。若是當前沒有事務,則 執⾏與PROPAGATION_REQUIRED相似的操做。 |
注:與AOP配置相似,配置<tx:advice>
開啓spring對註解事務的⽀持
<tx:annotation-driven transactionmanager="transactionManager"/>
或 @EnableTransactionManagement
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
調⽤
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
調⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization(後置處理器AbstractAutoProxyCreator完成bean代理對象建立)
調⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
調⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy(在這⼀步把委託對象的aop加強和通⽤攔截進⾏合併,最終給代理對象)
調⽤
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
調⽤
org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader )
複製代碼
@EnableTransactionManagement 註解
1)經過@import引⼊了TransactionManagementConfigurationSelector類
它的selectImports⽅法導⼊了另外兩個類:AutoProxyRegistrar和ProxyTransactionManagementConfiguration
2)AutoProxyRegistrar類分析
⽅法registerBeanDefinitions中,引⼊了其餘類,經過AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)引⼊
InfrastructureAdvisorAutoProxyCreator,它繼承了AbstractAutoProxyCreator,是⼀個後置處理器類
3)ProxyTransactionManagementConfiguration 是⼀個添加了@Configuration註解的配置類 (註冊bean)
註冊事務加強器(注⼊屬性解析器、事務攔截器)
屬性解析器:AnnotationTransactionAttributeSource,內部持有了⼀個解析器集合Set<TransactionAnnotationParser> annotationParsers;具體使⽤的是SpringTransactionAnnotationParser解析器,⽤來解析@Transactional的事務屬性
事務攔截器:TransactionInterceptor實現了MethodInterceptor接⼝,該通⽤攔截會在產⽣代理對象以前和aop加強合併,最終⼀起影響到代理對象
TransactionInterceptor的invoke⽅法中invokeWithinTransaction會觸發原有業
務邏輯調⽤(加強事務)
複製代碼