【spring系列】之14:spring宣告式事務實現原理剖析
通過上一節事務環境搭建,我們知道,在搭建的5個步驟中,有兩個是spring為我們提供底層去稍作配置,然後使用的,
這兩個操作涉及的便是:
- @EnableTransactionManagement
- PlatformTransactionManager
其中,PlatformTransactionManager是底層的事務控制器,它來控制我們的整個操作時提交還是回滾等。
我們只要配置我們具體需要的事務實現即可。
@EnableTransactionManagement是真正讓框架實現事務代理,攔截的核心,下面,我們通過原始碼來看看它的實現和工作原理。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { /** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code @Transactional}. For example, other beans marked with Spring's * {@code @Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ boolean proxyTargetClass() default false; /** * Indicate how transactional advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ int order() default Ordered.LOWEST_PRECEDENCE; }
通過原始碼發現,他有三個屬性:
proxyTargetClass:預設值為false,表示不是CGLIB代理,如果我們要強制使用CGLIB代理,將值設定為true即可
mode:預設值為PROXY,表示走代理實現事務,如果走切面攔截器實現事務,只需將其設定為ASPECTJ即可
order:預設值是Integer.MAX_VALUE,表示如果有多個切面攔截器,事務的攔截器執行優先順序最低,保證其他都能執行
那為什麼加上這個註解就開啟了事務功能呢?我們注意到,在註解定義的時候,引入了一個TransactionManagementConfigurationSelector
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { .... }
也就是在spring容器中有註冊了一個事務選擇器外掛,那這個外掛又是幹啥的呢?我們繼續扒程式碼:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* {@inheritDoc}
* @return {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
* {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
一看便知,TransactionManagementConfigurationSelector又給容器註冊了兩個新元件:
- AutoProxyRegistrar
- ProxyTransactionManagementConfiguration
備註:這個是通過預設配置實現的,如果將adviceMode設定為ASPECTJ,那註冊的元件將會是AspectJTransactionManagementConfiguration
下面我們接著研究預設註冊進來的兩個外掛是幹啥用的。
AutoProxyRegistrar
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
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;
}
}
}
}
if (!candidateFound) {
String name = getClass().getSimpleName();
logger.warn(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}
注意到以上程式碼中有一句很關鍵:AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
跟蹤程式碼,最終發現,這句話的作用就是在容器中註冊了InfrastructureAdvisorAutoProxyCreator物件,那他又是幹啥用的呢?
程式碼中有這樣一句:
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
由此可見:InfrastructureAdvisorAutoProxyCreator利用後置處理器機制在物件建立以後,包裝物件,返回一個代理物件(增強器),代理物件執行方法利用攔截器鏈進行呼叫;
ProxyTransactionManagementConfiguration
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor());
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
上面的程式碼,是給容器註冊了事務增強器。
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
事務增強器要用事務註解的資訊,AnnotationTransactionAttributeSource解析事務註解
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource());
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
事務攔截器:TransactionInterceptor;儲存了事務屬性資訊,事務管理器;
他是一個 MethodInterceptor,在目標方法執行的時候,執行攔截器鏈。
具體的事務攔截器執行流程如下:
- 先獲取事務相關的屬性
- 再獲取PlatformTransactionManager,如果事先沒有新增指定任何transactionmanger,最終會從容器中按照型別獲取一個PlatformTransactionManager;
- 執行目標方法 如果異常,獲取到事務管理器,利用事務管理回滾操作; 如果正常,利用事務管理器,提交事務