zoukankan      html  css  js  c++  java
  • Spring源码系列8

    Spring AOP源码分析中我们分析了Spring AOP的工作原理,而Spring的事务管理是基于Spring AOP的。所以,搞懂了Spring AOP的工作流程后再来研究Spring的事务管理会更轻车熟路。

    一、事务程序

    先以一段事务管理的程序来引出后文。首先,在mysql中默认的test库中创建一张tbl_user表。

    接下来写一段程序,用上事务管理。

    下面是pom文件

        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.3.26.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>4.3.26.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>4.3.26.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>4.3.26.RELEASE</version>
            </dependency>
            <!--mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.0.4</version>
                <!--<scope>runtime</scope>-->
            </dependency>
        </dependencies>

    然后是主程序

    @EnableTransactionManagement
    @ComponentScan("com.lp.test.tx")
    @Configuration
    public class TxConfig {
    
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUsername("root");
            dataSource.setPassword("root");
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/test");
    
            return dataSource;
        }
    
        @Bean
        public JdbcTemplate jdbcTemplate() {
            return new JdbcTemplate(dataSource());
        }
    
        @Bean
        public PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dataSource());
        }
    
    }
    
    @Service
    public class UserService {
    
        @Autowired
        private UserDao userDao;
    
        @Transactional
        public void insertUser() {
            userDao.insert();
            System.out.println("insert Success!");
    
            //int i = 1 / 0;
        }
    
    }
    
    @Repository
    public class UserDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void insert() {
            String sql = "INSERT INTO tbl_user(name,age) VALUES(?,?)";
    
            String username = UUID.randomUUID().toString().substring(0, 5);
            jdbcTemplate.update(sql, username, 18);
        }
    
    }
    
    public class TxMain {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
            UserService userService = (UserService) context.getBean("userService");
            userService.insertUser();
        }
    
    }

    二、源码分析

    下面开始源码跟踪和分析。要使用事务功能,首先需要在配置类上加上@EnableTransactionManagement注解,该注解是整个事务管理的开关,所以我们先从该注解入手来进行研究。首先查看一下该注解的声明

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

    发现该注解又通过@Import注解导入了一个TransactionManagementConfigurationSelector选择器组件,我们再进入该组件类中

    public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    
        @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继承了AdviceModeImportSelector,最终实现的是ImportSelector接口。在Spring容器中注册组件的几种方式总结中我们已经提到过ImportSelector搭配@Import可以用来向容器中导入组件。看看其中的selectImports方法,adviceMode有两种模式:PROXY和ASPECTJ。我们再回头看看@EnableTransactionManagement注解的声明,其中的AdviceMode属性默认值为PROXY,所以这里我们直接关注PROXY模式。也就是说默认的PROXY模式下,TransactionManagementConfigurationSelector又会导入两个组件:AutoProxyRegistrarProxyTransactionManagementConfiguration

    由于组件的导入分多个层级,所以可以结合下面我画的图来理解。

    那我们就来开始研究这两个组件。

    1.AutoProxyRegistrar

    public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
        private final Log logger = LogFactory.getLog(getClass());
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            boolean candidateFound = false;
            Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
            for (String annType : annTypes) {
                AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
                if (candidate == null) {
                    continue;
                }
                Object mode = candidate.get("mode");//获取注解属性中的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) {//PROXY模式
                        AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);//【注册组件】
                        if ((Boolean) proxyTargetClass) {
                            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                            return;
                        }
                    }
                }
            }
            if (!candidateFound && logger.isWarnEnabled()) {
                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);
      -->registerAutoProxyCreatorIfNecessary()
        -->registerOrEscalateApcAsRequired()

        public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
            return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);//【InfrastructureAdvisorAutoProxyCreator】
        }
    
        private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    
            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;
            }
    
            //注册组件,类型为方法参数中传入的InfrastructureAdvisorAutoProxyCreator
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
            return beanDefinition;
        }

    调用registerOrEscalateApcAsRequired()时,会注册一个组件InfrastructureAdvisorAutoProxyCreator,那我们就来看看该类。下面是该类继承体系。现在再回头看看Spring源码系列7 - AOP中介绍的AnnotationAwareAspectJAutoProxyCreator类继承体系图,发现它们是从共同父类AbstractAutoProxyCreator一路继承下来。所以,我将AOP部分的AnnotationAwareAspectJAutoProxyCreator也一并画了进来,便于参照理解。

    既然有了参照,我们理解起来应该就很容易了。InfrastructureAdvisorAutoProxyCreator自然也是一个后置处理器,其上层父类AbstractAutoProxyCreator实现了后置处理器中的回调方法postProcessAfterInitialization,该方法在执行时会调用wrapIfNecessary()对bean进行包装,这里的包装就是对Bean创建代理对象(增强)

    2.ProxyTransactionManagementConfiguration

    我们再来看另一个组件ProxyTransactionManagementConfiguration,它是一个配置类,通过该配置类会注册一个事务增强器BeanFactoryTransactionAttributeSourceAdvisor,而该事务增强器需要一个事务属性源和一个事务拦截器,所以配置类中也同时注册一个事务属性源和一个事务拦截器。

    @Configuration
    public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    
        //事务增强器
        @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
        public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
            BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
            advisor.setTransactionAttributeSource(transactionAttributeSource());
            advisor.setAdvice(transactionInterceptor());
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
            return advisor;
        }
    
        //事务属性源
        @Bean
        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
        public TransactionAttributeSource transactionAttributeSource() {
            return new 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;
        }
    
    }

    先来看看这个注解事务属性源,进入构造方法就知道了原来它添加了事务注解解析器,在当前Spring环境下也就是添加了一个SpringTransactionAnnotationParser,该解析器就是用来解析事务属性的,也就是方法上的@Transaction注解中的属性

        public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
            this.publicMethodsOnly = publicMethodsOnly;
            this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(4);
            this.annotationParsers.add(new SpringTransactionAnnotationParser());//Spring事务注解解析器
            if (jta12Present) {
                this.annotationParsers.add(new JtaTransactionAnnotationParser());//Jta
            }
            if (ejb3Present) {
                this.annotationParsers.add(new Ejb3TransactionAnnotationParser());Ejb
            }
        }

    另外,通过声明就知道事务拦截器是一个方法拦截器,其作用就是在目标方法调用时会进行拦截,从而执行增强操作。在下面目标方法调用部分会进行讲解。

    3.目标方法调用

    当我们的程序中insertUser()方法被调用时,拦截器链就要大显身手了。事务管理本质上也是基于AOP的,前面我们写的程序使用的是Cglib动态代理方式,在Spring源码系列7 - AOP源码分析 中我们已经分析过Cglib代理方式下会使用拦截器DynamicAdvisedInterceptor(它是CglibAopproxy类的内部类)拦截目标方法的调用。

    CglibAopProxy.DynamicAdvisedInterceptor.invoke()
      -->ReflectiveMethodInvocation.proceed() //执行拦截器链

    之后的流程就基本上是一样的了。执行拦截器链时,拦截器链中只有一个事务拦截器。

    我们来看看事务拦截器做了些什么操作。

        public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    
            ……
    
            @Override
            public Object invoke(final MethodInvocation invocation) throws Throwable {
                // Work out the target class: may be {@code null}.
                // The TransactionAttributeSource should be passed the target class
                // as well as the method, which may be from an interface.
                Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    
                // Adapt to TransactionAspectSupport's invokeWithinTransaction...
                return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
                    @Override
                    public Object proceedWithInvocation() throws Throwable {
                        return invocation.proceed();
                    }
                });
            }
            
            ……
    
        }

    TransactionInterceptor#invoke
      -->TransactionAspectSupport#invokeWithinTransaction

        protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
                throws Throwable {
    
            //1.获取事务的属性
            // If the transaction attribute is null, the method is non-transactional.    
            final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
            //2.获取平台事务管理器
            final PlatformTransactionManager tm = determineTransactionManager(txAttr);
            final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
            if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
                //3.开启事务
                // Standard transaction demarcation with getTransaction and commit/rollback calls.
                TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    
                Object retVal;
                try {
                    //4.执行方法
                    // 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) {
                    //5.1如果发生异常,进行回滚
                    // target invocation exception
                    completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
                }
                finally {
                    cleanupTransactionInfo(txInfo);
                }
                //5.2.如果正常执行,提交事务
                commitTransactionAfterReturning(txInfo);
                return retVal;
            }
    
            else {
                final ThrowableHolder throwableHolder = new ThrowableHolder();
    
                // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
                try {
                    Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
                            new TransactionCallback<Object>() {
                                @Override
                                public Object doInTransaction(TransactionStatus status) {
                                    TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                                    try {
                                        return invocation.proceedWithInvocation();
                                    }
                                    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;
                }
            }
        }
        
        protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
            ……
            try {
                //拿到事务管理器,执行回滚
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            ……
        }
    
        protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
            ……
            //拿到事务管理器,提交事务
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            ……        
        }

    整个过程我们应该非常熟悉,获取了事务属性和平台事务管理器后,接下来要执行方法了,执行方法前如果有必要的话(开启了事务),则先开启事务。然后执行方法,如果方法正常执行,则提交事务。如果方法执行发生异常,则回滚事务。

    总结

    我们通过@EnableTransactionManagement注解打开事务管理的开关,该注解又通过@Import向容器中导入了一个TransactionManagementConfigurationSelector选择器组件,该选择器组件又会向容器中导入AutoProxyRegistrar和ProxyTransactionManagementConfiguration这两个组件。

    • AutoProxyRegistrar:这个组件又会向容器中注册另外一个组件InfrastructureAdvisorAutoProxyCreator,该组件是一个后置处理器,作用是在Bean创建完并初始化后进行包装操作,具体上就是对Bean创建代理对象(增强)。[回忆一下refresh中有一个注册后置处理器的步骤]
    • ProxyTransactionManagementConfiguration:这是一个配置类,通过该配置类会注册一个事务增强器BeanFactoryTransactionAttributeSourceAdvisor,一个事务属性源AnnotationTransactionAttributeSource和一个事务拦截器TransactionInterceptor 。

    代理对象的方法在执行时,会走拦截器链,拦截器链中的事务拦截器会拦截到目标方法,拦截后就执行事务操作:(如有需要)开启事务,执行目标方法。正常执行完则提交事务,执行异常则回滚事务。

  • 相关阅读:
    链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.Ace.OleDb.12.0" 报错。提供程序未给出有关错误的任何信息。
    iis应用程序池 内存溢出错误 System.OutOfMemoryException
    高性能JavaScript
    HTML5介绍
    Ubuntu 16.04安装Matlab 2016b教程
    C Standard Library: 9 Signals: <signal.h>
    C Standard Library:4 Mathematical Functions: <math.h>
    C Standard Library: File Positioning Functions
    C Standard Library: Formatted Input
    C Standard Library: 2 Character Class Tests: <ctype.h>
  • 原文地址:https://www.cnblogs.com/rouqinglangzi/p/12764926.html
Copyright © 2011-2022 走看看