文档章节

spring 事务源码赏析(一)

o
 osc_pn11u1x9
发布于 2018/08/07 19:10
字数 2428
阅读 0
收藏 0

精选30+云产品,助力企业轻松上云!>>>

在本系列中,我们会分析:1.spring是如何开启事务的。2.spring是如何在不影响业务代码的情况下织入事务逻辑的。3.spirng事务是如何找到相应的的业务代码的。4.spring事务的传播行为是如何实现的。5.spring的事务隔离级别是如何实现的

因篇幅原因,本系列会分为两部分,第一部分会介绍前3个的实现,第二部分会介绍后两个的实现。

 

我们知道spring的事务管理分为两大部分:声明式和编程式,两种方式均为我们提供便捷的事务管理方法,各自优劣。

声明式的事务管理对业务代码基本0入侵,能够很好的把事务管理和业务代码剥离开来,提高代码扩展性和可读性但是控制的粒度只能是方法级别而且必须是public,同时还不能在一个类中调用等。

编程式事务则需要通过编写具体的事务代码来获得事务的管理能力,TransactionTemplate,或者直接使用PlatformTransactionManager,好处是控制粒度小,没有太多限制,坏处就是对业务代码有入侵,如果事务需要嵌套或者事务本身很繁琐,使用编程式则会十分麻烦。

虽然spring有两种事务管理方式,但是核心代码却是一份,本篇将从声明式事务做入口,和大家一起赏析spring的事务管理源码。

我们先从样例代码开始。 

@Configuration
@ComponentScan("com.flyingrain.tx")
@EnableTransactionManagement
public class TxConfig {
    @Autowired
    private TxConf txConf;

    @Bean
    PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    DataSource dataSource() {
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setUrl(txConf.getUrl());
        basicDataSource.setUsername(txConf.getUserName());
        basicDataSource.setPassword(txConf.getPassword());
        basicDataSource.setDriverClassName(txConf.getDriverName());
        return basicDataSource;
    }
    
}

 

@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class)
    public void transactionOperation(Employee employee){
        employeeMapper.insert(employee);
        employee.setId(1);
        employee.setName("wulei");
        employeeMapper.update(employee);
    }

我们通过在spring配置类上加上@EnableTransactionManagement注解来开启spring的事务管理,通过@Transactional来告诉spring事务的开始,没错这和开启spring aop十分类似,我的另一篇博客有详细介绍spring @EnableAspectJAutoProxy背后的那些事(spring AOP源码赏析)

      这里再提一下,通过@Enable....注解,之所以能够开启一个功能,是因为在这些注解中,偷偷给容器注册了一些功能性的bean,通过bean的前置处理,或后置处理来改变bean的行为。比如@EnableAspectJAutoProxy中注入了AnnotationAwareAspectJAutoProxyCreator,在bean装载时通过后置处理代理特定的bean。

      同样,@EnableTransactionManagement也注入了这样的bean:TransactionManagementConfigurationSelector

@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;

}

通过@Import注解在spring初始化时调用TransactionManagementConfigurationSelector的selectImports方法,装载指定的bean,至于spring何时何地如何解析装载@Import注解中的类的,请看我spring5之容器始末源码赏析 系列相关博客,这个系列详细介绍了spring使如何启动的本篇就不做赘述。

TransactionManagementConfigurationSelector主要责任是根据配置选择特定的bean装载进容器,这里有两个选择,通过EnableTransactionManagement 的mode()方法配置,默认是PROXY,这个配置的作用是告诉spring如何寻找事务的切面,是通过aspectJ的方式,还是spring自己的事务切面,一般我们使用默认的方式:使用spring自己的事务切面。如果选择aspecJ的话还需要引入spring aspects相关依赖。本篇着重介绍PROXY模式,aspectJ的话本质上同proxy是一样的。

我们选择PROXY模式后,TransactionManagementConfigurationSelector 将会在spring中加入两个bean:AutoProxyRegistrar,ProxyTransactionManagementConfiguration,一个是实现了ImportBeanDefinitionRegistrar,所有实现了这个接口的类都会在spring初始化时被调用registerBeanDefinitions方法(如何以及何时被调用,请参考spring5之容器始末源码赏析 系列相关博客)。另一个是有@Configuration的配置类。我们先看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> 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));
        }
    }

}

我们看到这个方法把注册bean的任务委托给了AopConfigUtils.registerAutoProxyCreatorIfNecessary,继续往下看:

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
            @Nullable 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;
        }

        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;
    }

它实际上是注册了InfrastructureAdvisorAutoProxyCreator这个bean,并设置角色为基础spring内部的基础bean,这个‘bean就有点意思了,它跟我们之前分析的spring aop源码用的是统一个父类

结合spring @EnableAspectJAutoProxy背后的那些事(spring AOP源码赏析)。中的分析我们可以得出,这个类就是开启transaction切面的类,它的核心代码和aop的原理一样,实现了BeanPostProcessor接口,在bean装载完成后会调用postProcessAfterInitialization,在postProcessAfterInitialization方法中会去加载切面,并且根据切点匹配相应的方法,对对象进行动态代理。具体的代码在父类AbstractAutoProxyCreator中,并且我们在spring @EnableAspectJAutoProxy背后的那些事(spring AOP源码赏析)也分析过,这里不在赘述。

所以AutoProxyRegistrar就是负责装载InfrastructureAdvisorAutoProxyCreator,来开启事务的代理,那么用来代理的切面和切点在哪里呢,没错,另一个配置类

ProxyTransactionManagementConfiguration,就是用来装载事务的切面和切点:
@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());
        if (this.enableTx != null) {
            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;
    }

}

这里主要有3个类:

BeanFactoryTransactionAttributeSourceAdvisor,AnnotationTransactionAttributeSource,TransactionInterceptor

这三个类的作用分别是:BeanFactoryTransactionAttributeSourceAdvisor为事务aop提供建言advisor,里面包含了切点的判定AnnotationTransactionAttributeSource,和切面的逻辑:TransactionInterceptor。

 

 我们先来看一下切点的判定,spring通过切点的matches方法来判断是否需要对目标对象进行代理:

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

    @Override
    public boolean matches(Method method, @Nullable Class<?> targetClass) {
        if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

   //。。。。。此处省略不相关代码

}

主要看标红的那行,我们看到,其实判断是否需要事务,就是通过tas(AnnotationTransactionAttributeSource)的getTransactionAttribute方法返回是否为空决定的,我们来到AnnotationTransactionAttributeSource的父类AbstractFallbackTransactionAttributeSource来看看它如何获取TransactionAttribute的:

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        }
        // Ignore CGLIB subclasses - introspect the actual user class.
        Class<?> userClass = (targetClass != null ? ClassUtils.getUserClass(targetClass) : null);
        // The method may be on an interface, but we need attributes from the target class.
        // If the target class is null, the method will be unchanged.
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
        // If we are dealing with method with generic parameters, find the original method.
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

        // First try is the method in the target class.
        TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
        if (txAttr != null) {
            return txAttr;
        }

        // Second try is the transaction attribute on the target class.
        txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }

        if (specificMethod != method) {
            // Fallback is to look at the original method.
            txAttr = findTransactionAttribute(method);
            if (txAttr != null) {
                return txAttr;
            }
            // Last fallback is the class of the original method.
            txAttr = findTransactionAttribute(method.getDeclaringClass());
            if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                return txAttr;
            }
        }

        return null;
    }

我们看到,第一行就是判断方法的访问权限是否是public,否则直接返回null,后面是对class的处理,防止是cglib生成的代理类,关键代码看findTransactionAttribute方法

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
        for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
            TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
            if (attr != null) {
                return attr;
            }
        }
        return null;
    }

我们看到,spring把解析注解的功能委托给TransactionAnnotationParser,而TransactionAnnotationParser的实现有3个SpringTransactionAnnotationParser,JtaTransactionAnnotationParser,Ejb3TransactionAnnotationParser,SpringTransactionAnnotationParser是在启动时默认装载的,剩下两个spring会根据类路径下是否存在相关类来判断是否需要装载,我们来看SpringTransactionAnnotationParser:

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                ae, Transactional.class, false, false);
        if (attributes != null) {
            return parseTransactionAnnotation(attributes);
        }
        else {
            return null;
        }
    }
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
        RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
        Propagation propagation = attributes.getEnum("propagation");
        rbta.setPropagationBehavior(propagation.value());
        Isolation isolation = attributes.getEnum("isolation");
        rbta.setIsolationLevel(isolation.value());
        rbta.setTimeout(attributes.getNumber("timeout").intValue());
        rbta.setReadOnly(attributes.getBoolean("readOnly"));
        rbta.setQualifier(attributes.getString("value"));
        ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>();
        Class<?>[] rbf = attributes.getClassArray("rollbackFor");
        for (Class<?> rbRule : rbf) {
            RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] rbfc = attributes.getStringArray("rollbackForClassName");
        for (String rbRule : rbfc) {
            RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
        for (Class<?> rbRule : nrbf) {
            NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
        for (String rbRule : nrbfc) {
            NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        rbta.getRollbackRules().addAll(rollBackRules);
        return rbta;
    }

可以看到,在这里主要是解析@transactional注解,并返回,到此,事务的aop已经能够匹配到具体的切点了,剩下的就是把切面了逻辑也加进去。

总结:本篇主要介绍了事务的前一部分:aop的开启和切点的匹配,spring通过@EnableTransactionManagement注解注入InfrastructureAdvisorAutoProxyCreator这个aop的基础组件,然后提供切点TransactionAttributeSourcePointcut匹配要被代理的方法,最后生成代理加入切面逻辑。

   如果把事务比作机器,那@EnableTransactionManagement便是开关,InfrastructureAdvisorAutoProxyCreator是机器的外壳,提供机器运作所需的条件,TransactionAttributeSourcePointcut是齿轮,只有匹配上了才能运作,TransactionInterceptor是机器的核心,也可以说是发动机,我们将在下一篇单独介绍事务的核心逻辑:TransactionInterceptor,我们将会看到:

1.事务的传播行为的实现

2.事务的隔离级别的实现

3.事务生命周期的管理

本篇到此结束,有不足或理解错误的地方希望大家指正!谢谢~

                                                                                                                                                                                  转载请注明出处~

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。

暂无文章

dict.items()和dict.iteritems()有什么区别?

问题: Are there any applicable differences between dict.items() and dict.iteritems() ? dict.items()和dict.iteritems()之间是否有适用的区别? From the Python docs: 从Python文档中......

法国红酒甜
今天
20
0
R中“ =”和“ <-”赋值运算符有什么区别?

问题: What are the differences between the assignment operators = and <- in R? R中赋值运算符=和<-之间有什么区别? I know that operators are slightly different, as this example ......

fyin1314
今天
20
0
之间的区别 和

问题: I'm learning Spring 3 and I don't seem to grasp the functionality behind <context:annotation-config> and <context:component-scan> . 我正在学习Spring 3,并且似乎不太了解<......

javail
今天
15
0
业内首款,百度工业视觉智能平台全新亮相

本文作者:y****n 业内首款全国产化工业视觉智能平台——百度工业视觉智能平台亮相中国机器视觉展(Vision China),该平台所具有的核心AI能力完全自主可控,在质检、巡检等场景中具有高效、...

百度开发者中心
昨天
7
0
我们如何制作xkcd样式图? - How can we make xkcd style graphs?

问题: Apparently, folk have figured out how to make xkcd style graphs in Mathematica and in LaTeX . 显然,民间已经想出了如何在Mathematica和LaTeX中制作xkcd风格的图形。 Can we d......

富含淀粉
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部