前言
上篇文章:
【小家Spring】Spring-jdbc的使用以及Spring事务管理的8种方式介绍(声明式事务 编程式事务)
介绍了Spring事务的众多使用方式,其中讲到全注解@Transactional
方式的时候一笔带过了,那么本文就以当下最流行的Spring事务的使用方式:全注解的@Transactional
使用方式为切入点,扒开Spring事务管理的神秘面纱~
全注解@Transactional方式的Spring事务
SpringBoot
大行其道的今天,基于XML配置的Spring Framework
的使用方式注定已成为过去式。
注解驱动应用,面向元数据编程
已然成受到越来越多开发者的偏好了,毕竟它的便捷程度、优势都是XML方式不可比拟的。
对SpringBoot有多了解,其实就是看你对
Spring Framework
有多熟悉~ 比如SpringBoot大量的模块装配
的设计模式,其实它属于Spring Framework
提供的能力
@Transactional的使用
1、开启注解驱动
代码语言:javascript复制@EnableTransactionManagement // 开启注解驱动
@Configuration
public class JdbcConfig { ... }
提示:使用
@EnableTransactionManagement
注解前,请务必保证你已经配置了至少一个PlatformTransactionManager
的Bean,否则会报错。(当然你也可以实现TransactionManagementConfigurer
来提供一个专属的,只是我们一般都不这么去做~~~)
2、在你想要加入事务的方法上(或者类(接口)上)标注@Transactional
注解
@Service
public class HelloServiceImpl implements HelloService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
@Override
public Object hello() {
// 向数据库插入一条记录
String sql = "insert into user (name,age) values ('fsx',21)";
jdbcTemplate.update(sql);
// 做其余的事情 可能抛出异常
System.out.println(1 / 0);
return "service hello";
}
}
单元测试:
代码语言:javascript复制@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RootConfig.class, JdbcConfig.class})
public class TestSpringBean {
@Autowired
private HelloService helloService;
@Test
public void test1() {
System.out.println(helloService.getClass()); //class com.sun.proxy.$Proxy29
helloService.hello();
}
}
就这么简单,事务就生效了(这条数据并没有insert成功~)。
从使用步骤上,有没有一种似曾相识的感觉??
没错,特别特别的像@Async
或者@Scheduled
的使用。其实,原理和@Async
也非常的类似(其实还有有本质区别的,一个借助的自动代理创建器,一个自己使用的后置处理器
),因此强烈建议可议先参阅博文:
【小家Spring】Spring异步处理@Async的使用以及原理、源码分析(@EnableAsync)
还有这个:
【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)
接下来分析注解驱动事务的原理,同样的我们从@EnableTransactionManagement
开始:
@EnableTransactionManagement
代码语言:javascript复制@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;
}
简直不要太面熟好不好,属性和@EnableAsync
注解的一毛一样。不同之处只在于@Import
导入器导入的这个类,但是我们依然能发现端倪:它也是个ImportSelector
附@EnableAsync
注解源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
// 支持自定义注解类型 去支持异步~~~
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
TransactionManagementConfigurationSelector
它所在的包为org.springframework.transaction.annotation
,jar属于:spring-tx(若引入了spring-jdbc,这个jar会自动导入)
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
// 很显然,绝大部分情况下,我们都不会使用AspectJ的静态代理的~~~~~~~~
// 这里面会导入两个类~~~
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}
依然可以看出,和@EnableAsync
导入的AsyncConfigurationSelector
如出一辙,都继承自AdviceModeImportSelector
,这就是为何我强烈建议先看关于@Async
那篇博文的原因,毕竟模式一样,触类旁通,一通百通~
AdviceModeImportSelector
目前所知的三个子类是:AsyncConfigurationSelector
、TransactionManagementConfigurationSelector
、CachingConfigurationSelector
。由此可见后面还会着重分析的Spring的缓存体系@EnableCaching
,模式也是和这个极其类似的~~~
AutoProxyRegistrar
从名字是意思是:自动代理注册器。它是个ImportBeanDefinitionRegistrar
,可以实现自己向容器里注册Bean的定义信息
// @since 3.1
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
// 这里面需要特别注意的是:这里是拿到所有的注解类型~~~而不是只拿@EnableAspectJAutoProxy这个类型的
// 原因:因为mode、proxyTargetClass等属性会直接影响到代理得方式,而拥有这些属性的注解至少有:
// @EnableTransactionManagement、@EnableAsync、@EnableCaching等~~~~
// 甚至还有启用AOP的注解:@EnableAspectJAutoProxy它也能设置`proxyTargetClass`这个属性的值,因此也会产生关联影响~
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
if (candidate == null) {
continue;
}
// 拿到注解里的这两个属性
// 说明:如果你是比如@Configuration或者别的注解的话 他们就是null了
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
// 如果存在mode且存在proxyTargetClass 属性
// 并且两个属性的class类型也是对的,才会进来此处(因此其余注解相当于都挡外面了~)
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
// 标志:找到了候选的注解~~~~
candidateFound = true;
if (mode == AdviceMode.PROXY) {
// 这一部是非常重要的~~~~又到了我们熟悉的AopConfigUtils工具类,且是熟悉的registerAutoProxyCreatorIfNecessary方法
// 它主要是注册了一个`internalAutoProxyCreator`,但是若出现多次的话,这里不是覆盖的形式,而是以第一次的为主
// 当然它内部有做等级的提升之类的,这个之前也有分析过~~~~
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
// 看要不要强制使用CGLIB的方式(由此可以发现 这个属性若出现多次,是会是覆盖的形式)
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
// 如果一个都没有找到(我在想,肿么可能呢?)
// 其实有可能:那就是自己注入这个类,而不是使用注解去注入(但并不建议这么去做)
if (!candidateFound && logger.isInfoEnabled()) {
// 输出info日志(注意并不是error日志)
}
}
}
这一步最重要的就是向Spring容器注入了一个自动代理创建器:org.springframework.aop.config.internalAutoProxyCreator
,并且看看是采用CGLIB还是JDK代理。
跟踪
AopConfigUtils
的源码你会发现,事务这块向容器注入的是一个InfrastructureAdvisorAutoProxyCreator
,它主要是读取Advisor
类,并对符合的bean进行二次代理。在Spring AOP博文中有过详细介绍,这里略过~ 参考:【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)
ProxyTransactionManagementConfiguration
它是一个@Configuration
,所以看看它向容器里注入了哪些Bean
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
// 这个Advisor可是事务的核心内容。。。。。也是本文重点分析的对象
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor());
// 顺序由@EnableTransactionManagement注解的Order属性来指定 默认值为:Ordered.LOWEST_PRECEDENCE
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
// TransactionAttributeSource 这种类特别像 `TargetSource`这种类的设计模式
// 这里直接使用的是AnnotationTransactionAttributeSource 基于注解的事务属性源~~~
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
// 事务拦截器,它是个`MethodInterceptor`,它也是Spring处理事务最为核心的部分
// 请注意:你可以自己定义一个TransactionInterceptor(同名的),来覆盖此Bean(注意是覆盖)
// 另外请注意:你自定义的BeanName必须同名,也就是必须名为:transactionInterceptor 否则两个都会注册进容器里面去~~~~~~
@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;
}
}
// 父类(抽象类) 它实现了ImportAware接口 所以拿到@Import所在类的所有注解信息
@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
@Nullable
protected AnnotationAttributes enableTx;
/**
* Default transaction manager, as configured through a {@link TransactionManagementConfigurer}.
*/
// 此处:注解的默认的事务处理器(可议通过实现接口TransactionManagementConfigurer来自定义配置)
// 因为事务管理器这个东西,一般来说全局一个就行,但是Spring也提供了定制化的能力~~~
@Nullable
protected PlatformTransactionManager txManager;
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
// 此处:只拿到@EnableTransactionManagement这个注解的就成~~~~~ 作为AnnotationAttributes保存起来
this.enableTx = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
// 这个注解是必须的~~~~~~~~~~~~~~~~
if (this.enableTx == null) {
throw new IllegalArgumentException("@EnableTransactionManagement is not present on importing class " importMetadata.getClassName());
}
}
// 这里和@Async的处理一样,配置文件可以实现这个接口。然后给注解驱动的给一个默认的事务管理器~~~~
// 设计模式都是想通的~~~
@Autowired(required = false)
void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
// 同样的,最多也只允许你去配置一个~~~
if (configurers.size() > 1) {
throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
}
TransactionManagementConfigurer configurer = configurers.iterator().next();
this.txManager = configurer.annotationDrivenTransactionManager();
}
// 注册一个监听器工厂,用以支持@TransactionalEventListener注解标注的方法,来监听事务相关的事件
// 后面会专门讨论,通过事件监听模式来实现事务的监控~~~~
@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
return new TransactionalEventListenerFactory();
}
}
关于事务相关的基础类打点,建议先参阅:
【小家Spring】Spring事务相关的基础类打点(spring-jdbc和spring-tx两个jar),着重讲解AnnotationTransactionAttributeSource
下面重中之重来了,那就是BeanFactoryTransactionAttributeSourceAdvisor
这个增强器
BeanFactoryTransactionAttributeSourceAdvisor
首先看它的父类:AbstractBeanFactoryPointcutAdvisor
这个之前在讲述AOP的时候已经大篇幅解释过:
【小家Spring】Spring AOP之Advisor、PointcutAdvisor、IntroductionAdvisor、IntroductionInterceptor(引介增强)
父类是一个和Bean工厂有关的Advisor。
再看自己,它是一个和Bean工厂和事务都有关系的Advisor
从上面的配置类可议看出,它是new出来一个。
使用的Advice为:advisor.setAdvice(transactionInterceptor())
,既容器内的事务拦截器
使用的事务属性源为:advisor.setTransactionAttributeSource(transactionAttributeSource())
,既一个new AnnotationTransactionAttributeSource()
支持三种事务注解来标注~~~
// @since 2.5.5
// 它是一个AbstractBeanFactoryPointcutAdvisor ,关于这个Advisor 请参阅之前的博文讲解~~~
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private TransactionAttributeSource transactionAttributeSource;
// 这个很重要,就是切面。它决定了哪些类会被切入,从而生成的代理对象~
// 关于:TransactionAttributeSourcePointcut 下面有说~
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
// 注意此处`getTransactionAttributeSource`就是它的一个抽象方法~~~
@Override
@Nullable
protected TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
};
// 可议手动设置一个事务属性源~
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
this.transactionAttributeSource = transactionAttributeSource;
}
// 当然我们可以指定ClassFilter 默认情况下:ClassFilter classFilter = ClassFilter.TRUE; 匹配所有的类的
public void setClassFilter(ClassFilter classFilter) {
this.pointcut.setClassFilter(classFilter);
}
// 此处pointcut就是使用自己的这个pointcut去切入~~~
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
}
下面当然要重点看看TransactionAttributeSourcePointcut
,它是怎么切入的
TransactionAttributeSourcePointcut
这个就是事务的匹配Pointcut切面,决定了哪些类需要生成代理对象从而应用事务。
代码语言:javascript复制// 首先它的访问权限事default 显示是给内部使用的
// 首先它继承自StaticMethodMatcherPointcut 所以`ClassFilter classFilter = ClassFilter.TRUE;` 匹配所有的类
// 并且isRuntime=false 表示只需要对方法进行静态匹配即可~~~~
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
// 方法的匹配 静态匹配即可(因为事务无需要动态匹配这么细粒度~~~)
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 实现了如下三个接口的子类,就不需要被代理了 直接放行
// TransactionalProxy它是SpringProxy的子类。 如果是被TransactionProxyFactoryBean生产出来的Bean,就会自动实现此接口,那么就不会被这里再次代理了
// PlatformTransactionManager:spring抽象的事务管理器~~~
// PersistenceExceptionTranslator对RuntimeException转换成DataAccessException的转换接口
if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
return false;
}
// 重要:拿到事务属性源~~~~~~
// 如果tas == null表示没有配置事务属性源,那是全部匹配的 也就是说所有的方法都匹配~~~~(这个处理还是比较让我诧异的~~~)
// 或者 标注了@Transaction这样的注解的方法才会给与匹配~~~
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
...
// 由子类提供给我,告诉事务属性源~~~~ 我才好知道哪些方法我需要切嘛~~~
@Nullable
protected abstract TransactionAttributeSource getTransactionAttributeSource();
}
关于
matches
方法的调用时机:只要是容器内
的每个Bean,都会经过AbstractAutoProxyCreator#postProcessAfterInitialization
从而会调用wrapIfNecessary
方法,因此容器内所有的Bean的所有方法
在容器启动时候都会执行此matche方法,因此请注意缓存的使用~
这样结合这篇博文:
【小家Spring】Spring事务相关的基础类打点(spring-jdbc和spring-tx两个jar),着重讲解AnnotationTransactionAttributeSource
就能知道Spring最终会给哪些类生成代理对象,事务可以作用在哪些方法上
真正的执行事务方法,还是在
TransactionInterceptor
这个增强器里,这个因为比价比较复杂,所以以一篇单独摘取出来详细讲解~~~请持续关注,就在下一篇哦~
@Transactional简单解释
这个事务注解可以用在类上,也可以用在方法上。
- 将事务注解标记到服务组件类级别,相当于为该服务组件的每个服务方法都应用了这个注解
- 事务注解应用在方法级别,是更细粒度的一种事务注解方式
注意 : 如果某个方法和该方法所属类上都有事务注解属性,优先使用方法上的事务注解属性。
另外,Spring 支持三个不同的事务注解 :
- Spring 事务注解
org.springframework.transaction.annotation.Transactional(纯正血统,官方推荐)
- JTA事务注解
javax.transaction.Transactional
- EJB 3 事务注解
javax.ejb.TransactionAttribute
代码语言:javascript复制上面三个注解虽然语义上一样,但是使用方式上不完全一样,若真要使其它的时请注意各自的使用方式~
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
// value 和 transactionManager 属性 它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。
@AliasFor("transactionManager")
String value() default "";
// @since 4.2
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED; //事务的传播行为
Isolation isolation() default Isolation.DEFAULT; // 事务的隔离级别
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; //事务的超时时间,默认值为-1
boolean readOnly() default false; //是否只读 默认是false
// 需要回滚的异常 可以指定多个异常类型 不指定默认只回滚RuntimeException和Error
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
// 不需要回滚的异常们~~~
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
自定义注解驱动的事务管理器 TransactionManagementConfigurer
注解的事务管理器会有一个人默认的,然后@Transaction里也可以通过value
属性进行制定~~。
改变默认的有一个非常优雅的方式,那就是使用TransactionManagementConfigurer
接口来提供:
@EnableTransactionManagement
@Configuration
public class JdbcConfig implements TransactionManagementConfigurer {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
return dataSourceTransactionManager;
}
// 复写提供一个即可。可以自己new一个,当然也可议向着一样从容器中获取~~~
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
// 直接使用容器内的事务管理器~~~
return transactionManager(dataSource());
}
}
思考:若既有@EnableAspectJAutoProxy
又有@EnableTransactionManagement
,那么自动代理创建器怎么注入谁呢?和注解的标注的先后顺序有关吗?
根据之前的分析和本文的分析,我们知道:
@EnableAspectJAutoProxy
会像容器注入AnnotationAwareAspectJAutoProxyCreator
@EnableTransactionManagement
会像容器注入InfrastructureAdvisorAutoProxyCreator
那么它俩同时使用时,形如下面:
@EnableTransactionManagement
@EnableAspectJAutoProxy
@Configuration
public class JdbcConfig {
...
}
那么最终会注入哪个代理创建类呢?
其实之前我们有分析过,核心代码在这:AopConfigUtils#registerOrEscalateApcAsRequired
方法
public abstract class AopConfigUtils {
...
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
// 可以发现这里有一个很巧妙的处理:会对自动代理创建器进行升级~~~~
// 所以如果你第一次进来的是`InfrastructureAdvisorAutoProxyCreator`,第二次进来的是`AnnotationAwareAspectJAutoProxyCreator`,那就会取第二次进来的这个Class
// 反之则不行。这里面是维护的一个优先级顺序的,具体参看本类的static代码块,就是顺序 最后一个`AnnotationAwareAspectJAutoProxyCreator`才是最为强大的
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;
}
...
}
从上面的分析可以知道:无论你的这些注解有多少个,无论他们的先后顺序如何,它内部都有咯优先级提升
的机制来保证向下的覆盖兼容。因此一般情况下,我们使用的都是最高级的AnnotationAwareAspectJAutoProxyCreator
这个自动代理创建器~~~