10、声明式事务

一、基础回顾 1、导入相关的依赖

org.springframework spring-jdbc 4.3.0.RELEASE c3p0 c3p0 0.9.1.2 mysql mysql-connector-java 5.1.44

2、进行相关的配置操作
  • 配置数据源
  • JdbcTemplate操作数据
  • @EnableTransactionManagement 开启基于注解的事务管理功能
  • 配置事务管理器来控制事务
@EnableTransactionManagement @ComponentScan("com.qiu.tx") @Configuration public class TxConfig {//数据源 @Bean public DataSource dataSource() throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); dataSource.setPassword("123456"); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); return dataSource; }@Bean public JdbcTemplate jdbcTemplate() throws Exception{ //Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource()); return jdbcTemplate; }//注册事务管理器在容器中 @Bean public PlatformTransactionManager transactionManager() throws Exception{ return new DataSourceTransactionManager(dataSource()); }}

3、给方法上标注 @Transactional 表示当前方法是一个事务方法
  • UserDao
@Repository public class UserDao {@Autowired private JdbcTemplate jdbcTemplate; public void insert(){ String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?)"; String username = UUID.randomUUID().toString().substring(0, 5); jdbcTemplate.update(sql, username,19); }}

  • UserService
@Service public class UserService { @Autowired private UserDao userDao; @Transactional public void insertUser(){ userDao.insert(); //otherDao.other(); xxx System.out.println("插入完成..."); //失败则回滚否则成功插入数据库 int i = 10/0; } }

二、原理
  • 前面知道了@EnableXXX注解主要作用是导入相应的功能类,因此从这个注解入手
1、@EnableTransactionManagement
//开始spring事物管理 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented //给容器导入TransactionManagementConfigurationSelector @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { //默认java的jdk代理方式 boolean proxyTargetClass() default false; //默认事物以代理方式进行 AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }

2、TransactionManagementConfigurationSelector类
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: //默认导入AutoProxyRegistrar和ProxyTransactionManagementConfiguration两个组件 return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: //如果是ASPECTJ则导入其他 return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } }}

2.1AutoProxyRegistrar
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {private final Log logger = LogFactory.getLog(getClass()); //根据给定的注解注册相应的bean @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean candidateFound = false; Set annoTypes = importingClassMetadata.getAnnotationTypes(); for (String annoType : annoTypes) { AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType); 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) { //给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 后置处理器组件 //InfrastructureAdvisorAutoProxyCreator 利用后置处理器机制在对象创建以后,包装对象, //返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用; 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 occured 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)); } }}

2.2ProxyTransactionManagementConfiguration
  • 是一个配置类,主要给容器中注册事务增强器
@Configuration public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { //1、事务增强器要用事务注解的信息, @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.getNumber("order")); return advisor; } //2.利用AnnotationTransactionAttributeSource解析事务注解 @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } //3.事务拦截器保存了事务属性信息,事务管理器等信息 @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; } }

  • 事务拦截器工作原理
【10、声明式事务】1、在目标方法执行的时候,执行拦截器链,对事物进行拦截
2、先获取事务相关的属性
3、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager;
4、执行目标方法。如果异常,获取到事务管理器,利用事务管理回滚操作,如果正常,利用事务管理器,提交事务。

    推荐阅读