zoukankan      html  css  js  c++  java
  • Spring系列.事务管理原理简析

    Spring的事务管理功能能让我们非常简单地进行事务管理。只需要进行简单的两步配置即可:

    step1:开启事务管理功能

    @Configuration
    //@EnableTransactionManagement注解有以下几个属性
    //proxyTargetClass属相:指定事务的AOP是通过JDK动态代理实现,还是CGLIB动态代理实现。true的话是CGLIB,false的话是JDK动态代理
    //                     需要注意的是这个属性只有在AdviceMode设置成AdviceMode.PROXY的情况下才会生效,加入使用ASPECTJ这AOP框架的话,这个属性就失效了。
    //                     另外,这个属性的设定可能会影响其他需要动态代理的类。比如说将这个属性设置成true,@Async注解的方法也会使用CGLIB生成代理类。
    //                     但是总的来说,这个属性的设置不会造成什么负面影响,毕竟JDK动态代理和CGLIB动态代理都能实现我们的需求
    
    //mode属性:Spring提供的AOP功能有两种实现方式,一种是Spring自带的AOP功能,主要靠JDK代理和CGLIB代理实现,另外一种是通过第三方框架ASPECTJ实现。这个选项
    //        就是设定Spring用哪种方式提供AOP功能。AdviceMode.PROXY表示用Spring自带的AOP功能,AdviceMode.ASPECTJ表示使用AdviceMode提供AOP功能。
    //        需要注意的是Spring自带的AOP功能不支持本地调用的代理功能,也就是说同一个类中的方法互相调用不会“触发”代理方法。如果想让自调用触发代理,可以考虑使用ASPECTJ。
    
    //order属性:表示当一个连接点(方法)被切多次时(也就是说有多个Advice和连接点关联),这些连接点的执行顺序。
    @EnableTransactionManagement
    public class TxConfig {
    }
    

    step2:在需要事务管理的方法上添加@Transactional注解

    @Override
    @Transactional
    public int saveSysUser(SysUser user) {
        int i = sysUserMapper.insert(user);
        return i;
    }
    

    整个使用流程就这么简单。这篇博客就来简单分析下Spring是怎么实现事务管理的。

    对事务管理进行AOP的过程

    Spring的很多功能都是通过AOP功能实现的,事务管理也是。我们之前的文章分析过Spring基础AOP实现的原理。这边再简单提下Spring实现AOP的
    原理:

    Spring基础的AOP功能的开关是@EnableAspectJAutoProxy,这个注解注册了一个Bean——AnnotationAwareAspectJAutoProxyCreator,这个Bean才是实现AOP功能的关键。
    这个Bean实现了InstantiationAwareBeanPostProcessor接口(这个接口是BeanPostProcessor的子接口)。熟悉Spring的读者知道,实现BeanPostProcessor接口的Bean
    会在其他Bean初始化之前初始,然后在其他Bean初始化的时候,BeanPostProcessor的实现会对这些Bean进行“加工处理”。

    这边AnnotationAwareAspectJAutoProxyCreator就承担了加工处理类的角色。这个Bean在其他Bean初始化前后会判断这个Bean中的方法是不是有对应的Advice,如果有的话就会
    通过动态代理的方式生成动态代理类将通知织入进去。

    我们发现开启事务管理的方式和开启AOP功能的方式很像,也是通过Enable注解开启。所以很自然就猜想事务管理是不是也是通过BeanPostProcessor的方式实现的。带着这个猜想去看下@EnableTransactionManagement注解。

    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(TransactionManagementConfigurationSelector.class)
    public @interface EnableTransactionManagement {
    
    	boolean proxyTargetClass() default false;
    	AdviceMode mode() default AdviceMode.PROXY;
    	int order() default Ordered.LOWEST_PRECEDENCE;
    }
    
    

    看到上面的代码,我们很自然的会去看TransactionManagementConfigurationSelector的代码。Spring有两种方式提供AOP功能,一种是自带的动态代理的功能,一种是
    通过ASPECTJ的方式提供。这边主要讨论Spring自带的AOP功能。

    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            //用代理的方式实现事务管理的AOP功能
            case PROXY:
                return new String[] {AutoProxyRegistrar.class.getName(),
                        ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {determineTransactionAspectClass()};
            default:
                return null;
        }
    }
    

    上面的代码中,我们主要关注PROXY这个case中的方法。这个case中注册了两个类:AutoProxyRegistrar和ProxyTransactionManagementConfiguration。

    首先我们来看AutoProxyRegistrar这个类,层层点进入,我们发现这个类最终就是注册了InfrastructureAdvisorAutoProxyCreator这个类。仔细看InfrastructureAdvisorAutoProxyCreator
    这个类实现的接口的话,你会发现这个类也是BeanPostProcesser系列的类。看到这里,我的直觉是事务管理的AOP过程和Spring基础的AOP功能原理可能是一样的。

    再仔细看InfrastructureAdvisorAutoProxyCreator对BeanPostProcesser系列接口的实现,你会发现都是继承的AbstractAutoProxyCreator。看到这个验证了我之前的想法。

    下面是Spring对事务管理进行AOP的过程,你会发现和基础的AOP功能是一套代码。

    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    
        // Create proxy if we have advice.
        // 代码1
        // 这边是获取Advice和Advisor的具体代码
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //生成代理类
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
    
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    
    

    再来看看ProxyTransactionManagementConfiguration做了些啥?点进源代码你会发现这个类的功能很简单,就是注册了下面几个事务管理相关的基础Bean。

    • BeanFactoryTransactionAttributeSourceAdvisor;
    • TransactionAttributeSource;
    • TransactionInterceptor。

    事务管理的生效过程

    上面的章节中讲了Spring是怎么生成事务相关的AOP代理类的。这边来讲下Spring的事务管理是怎么生效的——怎么开启事务,怎么回滚事务,怎么提交事务,Spring中的事务传播
    机制是怎么生效的。

    这块的代码主要是在TransactionAspectSupport的invokeWithinTransaction方法中(不要问我是怎么找到这段代码的...)。下面讲下这个方法中的几个关键点。

    
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
    			final InvocationCallback invocation) throws Throwable {
    
        // If the transaction attribute is null, the method is non-transactional.
        TransactionAttributeSource tas = getTransactionAttributeSource();
        //获取TransactionAttribute,这个类主要是@Transactional注解的配置信息
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        //确认事务管理器
        final TransactionManager tm = determineTransactionManager(txAttr);
    
        if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
            ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
                if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
                    throw new TransactionUsageException(
                            "Unsupported annotated transaction on suspending function detected: " + method +
                            ". Use TransactionalOperator.transactional extensions instead.");
                }
                ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
                if (adapter == null) {
                    throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
                            method.getReturnType());
                }
                return new ReactiveTransactionSupport(adapter);
            });
            return txSupport.invokeWithinTransaction(
                    method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
        }
    
        PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
        if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
    
            Object retVal;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // target invocation exception
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
    
            if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
                // Set rollback-only in case of Vavr failure matching our rollback rules...
                TransactionStatus status = txInfo.getTransactionStatus();
                if (status != null && txAttr != null) {
                    retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                }
            }
    
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }else {
            final ThrowableHolder throwableHolder = new ThrowableHolder();
    
            // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
                    TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
                    try {
                        Object retVal = invocation.proceedWithInvocation();
                        if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
                            // Set rollback-only in case of Vavr failure matching our rollback rules...
                            retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                        }
                        return retVal;
                    }
                    catch (Throwable ex) {
                        if (txAttr.rollbackOn(ex)) {
                            // A RuntimeException: will lead to a rollback.
                            if (ex instanceof RuntimeException) {
                                throw (RuntimeException) ex;
                            }
                            else {
                                throw new ThrowableHolderException(ex);
                            }
                        }
                        else {
                            // A normal return value: will lead to a commit.
                            throwableHolder.throwable = ex;
                            return null;
                        }
                    }
                    finally {
                        cleanupTransactionInfo(txInfo);
                    }
                });
    
                // Check result state: It might indicate a Throwable to rethrow.
                if (throwableHolder.throwable != null) {
                    throw throwableHolder.throwable;
                }
                return result;
            }
            catch (ThrowableHolderException ex) {
                throw ex.getCause();
            }
            catch (TransactionSystemException ex2) {
                if (throwableHolder.throwable != null) {
                    logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    ex2.initApplicationException(throwableHolder.throwable);
                }
                throw ex2;
            }
            catch (Throwable ex2) {
                if (throwableHolder.throwable != null) {
                    logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                }
                throw ex2;
            }
        }
    }
    
    

    事务操作的主要代码都在这个方法中,要详细将这个方法能写很多内容。这边就不详细展开了,大家感兴趣的可以仔细研究下这个方法。

    重要类总结

    • InfrastructureAdvisorAutoProxyCreator:事务管理AOP注册
    • BeanFactoryTransactionAttributeSourceAdvisor:Spring事务管理基础Bean
    • TransactionAttributeSource:Spring事务管理基础Bean
    • TransactionInterceptor:Spring事务管理基础Bean
    • TransactionAspectSupport的invokeWithinTransaction方法:事务处理的主要方法

    相关注解

    如果你仔细看过Spring的相关源代码,会发现Spring的Enable系列的注解都是上面的“套路”,熟悉了@EnableTransactionManagement注解生效的原理,其他注解都是类似的生效规则。比如

    • @EnableAsync
    • @EnableScheduling

    希望大家能做到触类旁通。

  • 相关阅读:
    一步一步学习sharepoint2010 workflow 系列第二部分:sharepoint无代码工作流 第3章 自定义工作流(Custom Designer workflows)
    巧用BroadcastReceiver实现开机“自”启动
    webview 与 javascript
    android webview设置内容的字体大小
    android中使用BitmapFactory的decodeStream()方法解码图片失败问题
    在ListView的item项里使用CheckBox或者Button时,OnItemClickListener无响应的问题
    修改Android中strings.xml文件, 动态改变数据
    在使用google map 时出现Android关于java.lang.NoClassDefFoundError问题
    android中String.xml可以传参数的资源
    android 代码实现应用强制装到手机内存
  • 原文地址:https://www.cnblogs.com/54chensongxia/p/13177933.html
Copyright © 2011-2022 走看看