Spring注解开发(五)声明式事务
先看一个简单的例子:
新建Dao类,代码如下:
@Repository
public class PersonDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert() {
String sql = "insert into tb_person(personName,age) values (?,?)";
String personName = UUID.randomUUID().toString().substring(0, 8);
jdbcTemplate.update(sql, personName, 20);
}
}
新建Service类,代码如下:
@Service
public class PersonService {
@Autowired
private PersonDao personDao;
@Transactional
public void insert() {
personDao.insert();
System.out.println("插入完成");
int a = 10 / 0;
System.out.println(a);
}
}
新建配置类,代码如下:
@Configuration
@EnableTransactionManagement
@ComponentScan("com.practice.bean")
public class MainConfig {
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
执行测试类,代码如下:
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
PersonService bean = applicationContext.getBean(PersonService.class);
bean.insert();
}
结果为:
插入完成
java.lang.ArithmeticException: / by zero
但是数据库中并没有插入数据,
去除service类insert()方法上的@Transactional注解,再次执行测试类,结果如下:
插入完成
java.lang.ArithmeticException: / by zero
依旧会报错,但是数据库中插入了一条,说明没有受到事务控制。
Spring的声明式事务的处理方式和AOP类似,我们依旧从@EnableTransactionManagement注解入手。
查看源码可知:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement
该注解为我们导入了TransactionManagementConfigurationSelector组件。
源码中的方法为:
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
adviceMode在注解中的默认值为AdviceMode.PROXY:
AdviceMode mode() default AdviceMode.PROXY;
根据上面的方法可以看到为容器导入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration:
1.AutoProxyRegistrar做了什么?
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
---》
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件,可以看到该组件实现了:SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware这两个接口,和AOP的一致。所以InfrastructureAdvisorAutoProxyCreator:
- 利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器)。
在创建Bean的时候调用的还是AbstractAutoProxyCreator的wrapIfNecessary方法(AOP)
先调用了下面的方法去查找当前类使用的拦截器:
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
根据org.springframework.transaction.config.internalTransactionAdvisor找到了BeanFactoryTransactionAttributeSourceAdvisor:
advisors.add(this.beanFactory.getBean(name, Advisor.class));
包装成MethodInterceptor并返回。
执行下面的方法:
bject proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
最后包装成一个代理对象并返回。
- 注入了BeanFactory;
2.ProxyTransactionManagementConfiguration做了什么?
根据源码上的@Configuration注解,我们可以看出这是一个配置类:
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration
2.1、给容器中注册事务增强器;
注册了BeanFactoryTransactionAttributeSourceAdvisor,并将AnnotationTransactionAttributeSource和transactionInterceptor赋值给
2.2.注册了事务属性解析器:
注册了AnnotationTransactionAttributeSource,解析SPring提供的事务注解(也支持JTA1.2和EJB3的相关注解(如果存在的话)):
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
上述简单示例中我们根据Debug可以看出,最终是进入了注解解析器的如下方法,读取到了@Transactional的所有属性信息:
this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
----》
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"));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
2.3、注册了事务拦截器:
注册了TransactionInterceptor,实际上就是一个MethodInterceptor(和AOP的拦截器链里的一样):
@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;
}
TransactionInterceptor里保存了事务属性信息和事务管理器;
MethodInterceptor,在目标方法执行的时候执行拦截器链;
invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
先获取事务相关的属性,再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
最终会从容器中按照类型获取一个PlatformTransactionManager,在获取目标类的joinpointIdentification,上实例此处为com.practice.bean.PersonService.insert方法,在创建事务信息。
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
返回的txInfo如下图:
执行目标方法:
try {
// 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) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
如果异常,获取到事务管理器,利用事务管理回滚操作:
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
如果正常,利用事务管理器,提交事务
commitTransactionAfterReturning(txInfo);