1、spring声明式事务概述2、本地事务源码之事务方法是如何被AOP代理拦截到的?
1、现象(单数据源多DB)2、如何生成AOP代理类
1、如何获取该bean的Interceptors?2、如何创建代理对象? 3、执行目标方法时被CglibAopProxy拦截4、总结 1、spring声明式事务概述
事务管理对于企业应用来说是至关重要的,当出现异常情况时,它可以保证数据的一致性。spring支持编程式事务管理和声明式事务管理两种方式。
首先我们来看看spring框架的事物抽象。Spring的事务策略由TransactionManager接口定义,PlatformTransactionManager接口和ReactiveTransactionManager接口继承了TransactionManager接口。我们的程序大多用的都是PlatformTransactionManager接口。
Spring 5.0之后引入了reactive web框架webflux,与webflux平级的就是webmvc,webflux是一个完全的响应式并且非阻塞的web框架,因此spring 5.2之后Spring还为响应式web框架提供了事务管理抽象,即ReactiveTransactionManager接口。我们下面主要讲的是PlatformTransactionManager事务管理抽象。
下面是PlatformTransactionManager接口的源码:
public interface PlatformTransactionManager extends TransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; void commit(TransactionStatus var1) throws TransactionException; void rollback(TransactionStatus var1) throws TransactionException;}
getTransaction方法通过传入一个TransactionDefinition类型的参数,来获取一个TransactionStatus对象,如果当前的调用堆栈里已经存在一个匹配的事务,TransactionStatus代表的就是这个已存在的事务,否则TransactionStatus代表一个新的事务。TransactionStatus接口为事务代码提供了一些控制事务执行和查询事务状态的方法。TransactionDefinition是一个接口,该接口里面有一些默认方法,这些默认方法的返回值是声明一个事务的必要属性,比如getPropagationBehavior()、getIsolationLevel()、getTimeout()、isReadonly()。getPropagationBehavior方法指获取事务的传播行为,getIsolationLevel方法指获取事务的隔离级别,getTimeout方法指获取事务的超时时间,isReadOnly方法指是否为只读事务,即只有读操作而没有写操作的事务。由于TransactionDefinition是一个接口,所以需要实现才能被使用,实现类可以覆盖接口的默认方法,也可以不覆盖,如果不覆盖则使用默认值。commit方法则用于提交事务,rollback方法用于回滚事务。
声明式事务一般使用@Transactional注解,并且使用@EnableTransactionManagement开启事务管理就足够了,但接下来我们讲的是其背后的工作原理。
声明式事务是建立在AOP之上的,首先我们的应用程序会通过XML的方式或者注解的方式提供元数据,AOP与事务元数据结合产生一个代理。当执行目标方
法时拦截器TransactionInterceptor会对目标方法进行拦截,然后在目标方法的前后调用代理。其本质是在目标方法开始之前创建或者加入一个事务,在执行完目标
方法之后根据执行情况提交或者回滚事务。
拦截器TransactionInterceptor通过检查方法返回类型来检测是哪种事务管理风格。如果返回的是响应式类型(例如Publisher)的方法则符合响应式事务管理的条
件,其他返回类型包括void则使用PlatformTransactionManager。
@Transactional注解是基于注解的声明式事务,当然基于XMl配置的也可以,但因为是在Spring Boot应用中使用,所以一律使用基于注解的声明式事务。
@Transactional可以作用于接口定义上、接口方法上、类定义上和类的公共方法上,如果作用于私有方法或者包可见的方法上,虽然不会引发错误,但是并不会激
活事务的一些行为。另外,将@Transactional作用于类上要比作用于接口上要更好,下面我来说明下原因。Spring AOP框架中有两种模式,分别是基于代理和
基于AspectJ,而基于代理又分为两种,一种是基于接口的,一种是基于类的,如果是基于类的代理或是基于AspectJ,则将@Transactional作用于接口上不会起到
任何作用。
上面说到了事务是基于AOP的,那么如何让事务支持多种模式呢?@EnableTransactionManagement注解提供了相关的支持,@EnableTransactionManagement注解的源码如下:
public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default 2147483647;}
可以看到,事务管理默认模式为基于代理,即mode=AdviceMode.PROXY,且默认的代理方式为基于接口的,因为proxyTargetClass=false。
下面来看一下@Transactional注解的属性有哪些:
无论您在 Spring 中选择声明式还是编程式事务管理,定义正确的TransactionManager实现都是绝对必要的,TransactionManager的实现通常与它们工作的环境有关:JDBC、JTA、Hibernate 等等。
如果是纯 JDBC,则使用DataSourceTransactionManager,如下代码所示。
如果是Hibernate,则使用HibernateTransactionManager。(Hibernate相较于纯JDBC多了一层sessionFactory)
如下图所示,由于test_1和test_2这两个DB在同一个数据源下面,因此是本地事务,spring事务是直接就可以支持的。
@Service@Transactionalpublic class MultiDBServiceImpl implements IMultiDBService { @Autowired private JdbcTemplate jdbcTemplate; @Override public void save() { String sql1 = "insert into test_2.t_class(Fname,Fnum) values("303班",30);"; String sql2 = "insert into test_1.t_student(Fname,Fage,Fclass) values("曹操",30,3);"; jdbcTemplate.execute(sql1); jdbcTemplate.execute(sql2); // 回滚 throw new RuntimeException(); }}
CREATE TABLE t_student ( Fid int(10) UNSIGNED NOT NULL AUTO_INCREMENT, Fname varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, Fage int(255) NULL DEFAULT NULL, Fclass int(255) NULL DEFAULT NULL, PRIMARY KEY (Fid) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;CREATE TABLE t_class ( Fid int(10) UNSIGNED NOT NULL AUTO_INCREMENT, Fname varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, Fnum int(11) NULL DEFAULT NULL, PRIMARY KEY (Fid) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
如果没打@Transactional注解,则直接进入目标方法,否则,进入到cglib生成的代理类中,如下图所示。
可以看到,cglib生成的代理为是com.bobo.springbootdemo.service.impl.MultiDBServiceImpl$$ EnhancerBySpringCGLIB$$2afb0e3b类型,
且实现了3个接口(其中Advised接口颇为重要),继承自目标类即com.bobo.springbootdemo.service.impl.MultiDBServiceImpl。
2、如何生成AOP代理类AOP代理对象的生成是在Bean初始化方法中,有一些BeanPostProcessor对象,其中有一个叫AnnotationAwareAspectJAutoProxyCreator,是专门用于创建代理对象的,AnnotationAwareAspectJAutoProxyCreator是AbstractAutoProxyCreator的子类,AbstractAutoProxyCreator是BeanPostProcessor的子类,AbstractAutoProxyCreator实现了postProcessAfterInitialization方法,如下所示。
@Overridepublic 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;}
wrapIfNecessary方法核心代码如下图所示。
这里分两步叙述:
如何获取该bean的Interceptors?如何创建代理对象? 1、如何获取该bean的Interceptors?
getAdvicesAndAdvisorsForBean方法点进去,会找到如下方法。
protected List findEligibleAdvisors(Class<?> beanClass, String beanName) {// 找到候选的AdvisorsList candidateAdvisors = findCandidateAdvisors(); // 从候选Advisors中找到符合条件的Advisors,判断是否符合条件前面已经介绍过了,说白了就是有事务注解List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}
下面介绍如何找候选的Advisors。
通过一路跟踪,最终是走到了BeanFactoryAdvisorRetrievalHelper类的findAdvisorBeans方法。
候选的Advisors的寻找方法实际上就等价于从Bean工厂中找到所有Advisor类型的bean。
如上图所示,会从BeanFactory中找到一个beanName为org.springframework.transaction.config.internalTransactionAdvisor、类型为Advisor的bean BeanFactoryTransactionAttributeSourceAdvisor,该bean通过依赖注入了Advice类型的advice属性,TransactionInterceptor是Advice的子类。
通过全局搜索可以得知,TransactionInterceptor和BeanFactoryTransactionAttributeSourceAdvisor这两个bean的配置在ProxyTransactionManagementConfiguration类里面,如下图所示。
BeanFactoryTransactionAttributeSourceAdvisor间接继承自PointcutAdvisor,实现了getPointcut方法,如下图所示。
TransactionAttributeSource只是个接口,那么它的实现是什么,如何找到切入点的?
TransactionAttributeSource实现前面的图中已经给出了,是AnnotationTransactionAttributeSource,我们来看看这个类是怎么找到事务方法的。
这个类中有两个重要的方法,源码如下。
@Override@Nullableprotected TransactionAttribute findTransactionAttribute(Class<?> clazz) {return determineTransactionAttribute(clazz);}@Override@Nullableprotected TransactionAttribute findTransactionAttribute(Method method) {return determineTransactionAttribute(method);}
可以看到参数可以是Class类型也可以是Method类型,这说明@Transactional注解既可以作用于类上也可以作用于方法上。
点进findTransactionAttribute方法,如下所示。
@Nullableprotected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {for (TransactionAnnotationParser parser : this.annotationParsers) {TransactionAttribute attr = parser.parseTransactionAnnotation(element);if (attr != null) {return attr;}}return null;}
TransactionAnnotationParser的实现有:Ejb3TransactionAnnotationParser、JtaTransactionAnnotationParser和SpringTransactionAnnotationParser,由于这是Spring事务,因此点进去SpringTransactionAnnotationParser看,源码如下。
@Override@Nullablepublic TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false);if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}}
可以看到,其实就是看有没有Transactional注解,如果有,则进入parseTransactionAnnotation方法,parseTransactionAnnotation是用来解析Transactional注解的各种属性的。
下面通过debug验证一下。
通过debug,可以看到将Transactional注解打在MultiDBServiceImpl上之后,确实走到了findTransactionAttribute(Class<?> clazz)方法,如下图所示。
2、如何创建代理对象?AbstractAutoProxyCreator.createProxy方法如下图所示。
proxyFactory.getProxy方法如下所示。
public Object getProxy(@Nullable ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);}// createAopProxy方法protected final synchronized AopProxy createAopProxy() { // AopProxyFactory接口目前只有一种实现:DefaultAopProxyFactoryreturn getAopProxyFactory().createAopProxy(this);}// DefaultAopProxyFactory的createAopProxy方法public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);} // 这里的config实际上就ProxyFactory对象,ProxyFactory是ProxyConfig的子类return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}
ObjenesisCglibAopProxy是CglibAopProxy的子类,CglibAopProxy.getProxy方法如下图所示。
getProxy方法主要代码如下所示。
Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));// 获取callbacks,其中就包括CglibAopProxy的内部类DynamicAdvisedInterceptorCallback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// 创建代理类的Class对象和实例return createProxyClassAndInstance(enhancer, callbacks);
3、执行目标方法时被CglibAopProxy拦截点进去之后,发现被CglibAopProxy拦截,如下图所示。
4、总结当用@Autowired注入IMultiDBService Bean并且调用它的save时,会经过如下过程:
Bean实例化Bean初始化执行BeanPostProcessor(AbstractAutoProxyCreator)的postProcessAfterInitialization方法获取该bean的Advisors(获取候选的Advisors(事先配置好的))获取满足条件的Advisors使用Enhancer结合callbacks(DynamicAdvisedInterceptor)生成动态代理对象执行代理对象的方法(IMultiDBService.save)被DynamicAdvisedInterceptor(Callback)拦截获取Advisors链执行Advisors链(TransactionInterceptor)后面就是TransactionInterceptor的逻辑了,本教程暂不介绍。