javascript
点击事件为什么会失效_Spring事务原理?事务在方法间如何传播?为什么会失效?...
推薦學習
- 瘋狂膜拜!阿里出品Spring Security王者晉級文檔
- 肝了十天半月,獻上純手繪“Spring/Cloud/Boot/MVC”全家桶腦圖
前言
事務我們都知道是什么,而Spring事務就是在數據庫之上利用AOP提供聲明式事務和編程式事務幫助我們簡化開發,解耦業務邏輯和系統邏輯。但是Spring事務原理是怎樣?事務在方法間是如何傳播的?為什么有時候事務會失效?接下來咱就一一解答~重點分析Spring事務源碼,讓我們徹底搞懂Spring事務的原理。
正文
XML標簽的解析
配置過事務的應該都不陌生,上面這個配置就是Spring開啟事務注解(@Transactional)支持的配置,而看過我之前文章的應該知道,這個帶前綴的標簽叫自定義標簽,我在之前的文章也分析過自定義標簽的解析過程,所以這里我直接找到對應的handler:
public class TxNamespaceHandler extends NamespaceHandlerSupport {static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}}可以看到對應的注解解析器就是AnnotationDrivenBeanDefinitionParser類,在該類中一定會有一個parse方法:
public BeanDefinition parse(Element element, ParserContext parserContext) {registerTransactionalEventListenerFactory(parserContext);String mode = element.getAttribute("mode");if ("aspectj".equals(mode)) {// mode="aspectj"registerTransactionAspect(element, parserContext);if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {registerJtaTransactionAspect(element, parserContext);}}else {// mode="proxy"AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;}首先拿到mode屬性的值判斷是使用AspectJ生成代理還是JDK生成代理,這里我們主要看proxy模式,進入configureAutoProxyCreator方法:
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {// 注冊AOP的入口類AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {Object eleSource = parserContext.extractSource(element);// Create the TransactionAttributeSource definition.// @Transactional注解的屬性封裝RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);// Create the TransactionInterceptor definition.// AOP執行鏈RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 拿到transaction-manager屬性的值registerTransactionManager(element, interceptorDef);interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);// Create the TransactionAttributeSourceAdvisor definition.RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);advisorDef.setSource(eleSource);advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);if (element.hasAttribute("order")) {advisorDef.getPropertyValues().add("order", element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));parserContext.registerComponent(compositeDef);}}這里的流程比較長,但邏輯很簡單。首先來看注冊事務AOP入口類是哪個:
public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {// 將優先級更高的AOP入口類放入到IOC容器中BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));// 設置代理生成的方式以及是否緩存代理類到當前線程useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);registerComponentIfNecessary(beanDefinition, parserContext);}主要看registerAutoProxyCreatorIfNecessary方法:
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);}private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 判斷傳進來的類和ICO中當前存在的類哪個優先級更高,將更高的放入IOC中if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName(cls.getName());}}return null;}//把AOP入口類封裝成beanDefinition對象,要實例化RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);//注解aop入口類的beanName名稱 AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAMEregistry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}首先判斷容器中是否已經存在AOP入口類,如果不存在則直接創建InfrastructureAdvisorAutoProxyCreator的BeanDefinition對象注冊到容器中,這個類也是我之前分析的AOP入口類AbstractAutoProxyCreator的子類,再來看看其繼承關系:
你會不會疑惑,這么多子類,到底會使用哪一個呢?回到剛剛的代碼中,可以看到如果已經存在一個入口類了,就會通過findPriorityForClass獲取兩個類的優先級,最終就會使用優先級更大的那個,那么它們的優先級順序是怎樣的呢?
private static final List> APC_PRIORITY_LIST = new ArrayList<>(3);static {// Set up the escalation list...APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);}private static int findPriorityForClass(@Nullable String className) {// 索引即是優先級,越大優先級越高,IOC中只會存在一個事務AOP入口類for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {Class> clazz = APC_PRIORITY_LIST.get(i);if (clazz.getName().equals(className)) {return i;}}throw new IllegalArgumentException("Class name [" + className + "] is not a known auto-proxy creator class");}可以看到,InfrastructureAdvisorAutoProxyCreator是優先級最低的,基本上不會起作用;AspectJAwareAdvisorAutoProxyCreator是當我們配置了標簽時會注冊,也就是xml配置的AOP的入口類;而AnnotationAwareAspectJAutoProxyCreator是當我們配置了或使用@EnableAspectJAutoProxy注解時注冊,因此大部分情況下都是使用的AnnotationAwareAspectJAutoProxyCreator。
注冊完AOP的入口類后,回到configureAutoProxyCreator方法:
AnnotationTransactionAttributeSource類的作用就是封裝事務注解@Transactional的屬性,這里需要記住其繼承體系以及熟悉該類和其父類的屬性和方法,對后面分析事物切面執行原理有幫助:
緊接著就是創建了TransactionInterceptor對象,專門的事務攔截器,并且該類是MethodInterceptor的子類,看到這個應該不陌生了,我們知道AOP調用鏈在執行過程中主要就是調用該類的invoke的方法,因此它是事務切面執行的入口。既然有了Interceptor,那么必不可少的還應該有Advisor,而Advisor又是由Advice和Poincut組成的,這樣才能構成一個完整的切面,所以該方法后面就是創建這兩個對象。以上就是xml配置AOP注解支持的原理,很簡單,下面再來看看零配置又是如何實現的。
AOP零配置原理
使用過SpringBoot的都知道,如果需要開啟事務注解的支持,只需要一個注解就能搞定:@EnableTransactionManagement,不用再配置xml文件,這個又是怎么做到的呢?不多說,我們直接來看其源碼:
@Import(TransactionManagementConfigurationSelector.class)public @interface EnableTransactionManagement {boolean proxyTargetClass() default false;AdviceMode mode() default AdviceMode.PROXY;int order() default Ordered.LOWEST_PRECEDENCE;}在該注解下使用@Import導入了一個類TransactionManagementConfigurationSelector,首先該注解的作用就是導入一個類的實例到IOC容器中,你可能會說不是在類上加@Component注解就行了么,但是有些類它并不在你掃描的路徑下,而該注解依然可以將其導入進來,所以我們主要看TransactionManagementConfigurationSelector類中做了些啥:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector {@Overrideprotected 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;}}private String determineTransactionAspectClass() {return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);}}可以看到在selectImports方法中返回了AutoProxyRegistrar和ProxyTransactionManagementConfiguration類,返回后會被封裝為BeanDefinition對象,那這個方法是在哪里調用的呢?這個在之前的文章中也分析過,ConfigurationClassPostProcessor類中會調用ConfigurationClassParser類的parse方法解析@Configuration、@Import、@ImportSource等注解,具體過程這里就不再贅述了。我們繼續來分別看看AutoProxyRegistrar和ProxyTransactionManagementConfiguration類:
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {private final Log logger = LogFactory.getLog(getClass());@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {boolean candidateFound = false;Set 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) {//注冊事務AOP的入口類InfrastructureAdvisorAutoProxyCreator,實際上這個AOP入口類起不了作用AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);if ((Boolean) proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);return;}}}}}}public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {/** 明顯是創建事務切面實例* BeanFactoryTransactionAttributeSourceAdvisor** */@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.getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}/** 創建事務advice* TransactionInterceptor* */@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;}}看到這就很清楚了,前者是注冊AOP的入口類(這里注冊的入口類依然是InfrastructureAdvisorAutoProxyCreator),后者則是創建事務AOP的組件的實例到IOC中,到這里相信不僅僅是對于事務的零配置,而是整個SpringBoot的零配置實現原理都心中有數了。
總結
本篇結合之前所學分析了事務配置解析的原理,也帶出了SpringBoot零配置實現的原理。我們需要在腦海將加載、解析和調用串聯起來,從微觀到宏觀整體把握Spring,才能真正的理解Spring。
作者:夜勿語
原文鏈接:https://blog.csdn.net/l6108003/article/details/106650023
總結
以上是生活随笔為你收集整理的点击事件为什么会失效_Spring事务原理?事务在方法间如何传播?为什么会失效?...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么把路由器和电脑连接起来千兆企业路由器
- 下一篇: springboot怎么返回404_Sp