Spring事务的启动过程-源码解析

背景

Spring使用声明式让我们无需关系事务的复杂逻辑处理,只要添加注解@Transactional即可为业务添加事务支持

如何启用Spring事务

注解方式@EnableTransactionManagement
  • 还需要加@Configuration
  • 使用注解方法启动必须使用基于注解的容器启动,这里就不列出例子了
        //基于注解的容器
     	AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
     	//扫包
     	ctx.scan("com.example");
    
XML配置文件启用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"
       default-autowire="byName">
    <!--启用声明式事务-->
    <tx:annotation-driven/>

    <!--当然是配置datasource了-->
    <jdbc:embedded-database id="dataSource" type="H2">
        <!--一定要是先DDL,即数据库定义语言-->
        <jdbc:script location="classpath:sql/h2-schema.sql"/>
        <!--然后才是DML,数据库操作语言-->
        <jdbc:script location="classpath:sql/h2-data.sql" encoding="UTF-8"/>
    </jdbc:embedded-database>

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--查询服务-->
    <bean id="userService" class="com.example.springtest.transaction.UserServiceImpl"/>
</beans>
数据库脚本
--	h2-schame.sql
drop table if exists t_user ;

-- 创建表
create table t_user(
  id int  primary key auto_increment,
  name varchar(20),
  age int
);

-- 插入表数据 h2-data.sql
insert into t_user(name,age) values('小明',23);
insert into t_user(name,age) values('小白',24);
在查询方法上声明注解@Transactional

默认发生RuntimeException异常时,事务会回滚

    @Transactional
    public void insertUser() throws SQLException {
        //使用Spring提供的工具类,获取当前线程的连接-已经在事务拦截器中创建了连接
        Connection connection = DataSourceUtils.getConnection(dataSource);
        //获取JDBC 执行SQL的对象
        Statement statement = connection.createStatement();
        //执行插入语句
        statement.execute("insert into t_user(name,age) values('小黑',100)");
    }

Spring如何支持声明式事务

使用XML方式启动
首先需要支持解析自定义标签annotation-driven
  • 全局搜索annotation-driven
  • 在TxNamespaceHandler中支持自定义标签annotation-driven
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);
	}


	@Override
	public void init() {
		registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
		//支持声明式事务标签
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
	}
}
AnnotationDrivenBeanDefinitionParser
  • 解析标签并注册Spring事务相关的Bean
  • 下面主要注册四大Bean
    • InfrastructureAdvisorAutoProxyCreator
      • 继承了SmartInstantiationAwareBeanPostProcessor 用于执行Bean的后置处理器,返回代理Bean的Proxy对象
    • TransactionInterceptor
      • 继承了MethodIntercetor和Advice 用于代理增强
      • 用于执行事务相关的逻辑
    • BeanFactoryTransactionAttributeSourceAdvisor
      • 用于判断Bean中的方法是否需要代理增强
    • AnnotationTransactionAttributeSource
      • 保存注解中的一些属性信息
  • parse() 被容器调用的标签解析方法,从中执行事务相关的BeanDefinition的创建逻辑
public BeanDefinition parse(Element element, ParserContext parserContext) {
		registerTransactionalEventListenerFactory(parserContext);
		String mode = element.getAttribute("mode");
		//使用aspectj支持事务
		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;
	}
  • configureAutoProxyCreator() 默认配置方法
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
			//主要用于注册InfrastructureAdvisorAutoProxyCreator的BeanDefinition
			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.
				RootBeanDefinition sourceDef = new RootBeanDefinition(
						"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
				sourceDef.setSource(eleSource);
				sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

				//创建TransactionInterceptor definition,用于拦截事务
				//就是这个类对事务的逻辑处理
				RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
				interceptorDef.setSource(eleSource);
				interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				registerTransactionManager(element, interceptorDef);
				//构造Bean定义的 transactionAttributeSource='sourceName的Bean名称'
				interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
				String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

				// 创建 TransactionAttributeSourceAdvisor definition.
                // 用于判断Bean中的Method是否需要使用事务的Advice进行争强
				RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
				advisorDef.setSource(eleSource);
				advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				//构造Bean定义的属性
				advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
				advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
				if (element.hasAttribute("order")) {
					advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
				}
				//注册BeanFactoryTransactionAttributeSourceAdvisor的BeanDefinition
				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);
			}
		}
使用注解方式启动@EnableTransactionManagement
  • @EnableTransactionManagement 等同于xml中的annotation-driven
    • TransactionManagementConfigurationSelector中就是声明事务相关的Bean,与XML方式创建的Bean是一样的
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//导入事务相关的Bean的配置类
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	/*
	* 是否强制使用GCLIB生成代理对象
	* */
	boolean proxyTargetClass() default false;

	/*
	* 使用proxy或aspectj支持事务
	* */
	AdviceMode mode() default AdviceMode.PROXY;

	/*
	* 事务拦截器在增强器中的执行顺序
	* */
	int order() default Ordered.LOWEST_PRECEDENCE;

}

Spring何时对Bean进行事务增强

首先对Bean添加事务注解
  • 我在UserService中对insertUser()添加了@Transactional注解
public class UserService {
    DataSource dataSource;
    PlatformTransactionManager transactionManager;

    @Transactional
    public void insertUser() throws SQLException {
        //使用Spring提供的工具类,获取当前线程的连接-已经在事务拦截器中创建了连接
        Connection connection = DataSourceUtils.getConnection(dataSource);
        //获取JDBC 执行SQL的对象
        Statement statement = connection.createStatement();
        //执行插入语句
        statement.execute("insert into t_user(name,age) values('小黑',100)");
    }
  • 并在XML配置文件中进行了Bean的声明
 <bean id="userService" class="com.example.springtest.transaction.UserServiceImpl"/>
从容器中获取UserService
  • 从图中能看到返回的是UserService的代理类
    在这里插入图片描述
很明显能看到使用了JdkDynamicAopProxy
  • 该类用于Spring AOP中使用JDK动态代理创建代理对象
  • 在上一篇文章中Spring AOP初始化及执行过程
    ,最后就是使用JdkDynamicAopProxy对Bean进行代理增强
  • 所以说,Spring使用了AOP对事务进行了增强
既然Spring使用AOP对事务进行增强,那么我们知道Spring AOP在AbstractAutoProxyCreator类中的postProcessAfterInitialization()返回代理对象
  • 首先来到AbstractAutoProxyCreator并在postProcessAfterInitialization()中打条件断点

  • 启动容器

  • 如图,当前执行对Bean UserService对象进行代理判断
    在这里插入图片描述

  • 断点进入wrapIfNecessary()
    在这里插入图片描述

  • 从这里我们看到Bean是在Bean的后置处理器InfrastructureAdvisorAutoProxyCreator的父类AbstractAutoProxyCreator中的postProcessAfterInitialization()使用事务增强并被代理返回的

Spring AOP使用了哪个增强器对事务进行增强

  • 继续打断点
  • 从新启动容器
  • 图中看到
    • 增强器为BeanFactoryTransactionAttributeSourceAdvisor
    • 对应的增强类(通知Advice)为TransactionInterceptor
      在这里插入图片描述

总结

  • Spring可以使用XML或注解方式启动事务
  • Spring通过AOP对事务进行实现
  • 在Bean后置处理器InfrastructureAdvisorAutoProxyCreator中对Bean进行代理返回代理Bean
  • 事务增强器BeanFactoryTransactionAttributeSourceAdvisor
    • 用于匹配当前Bean是否需要事务增强
    • 其他略
  • 使用TransactionInterceptor对Bean进行事务增强逻辑
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值