Springboot源码分析之@Transactional
摘要:
对SpringBoot
有多了解,其实就是看你对Spring Framework
有多熟悉~ 比如SpringBoot
大量的模块装配的设计模式,其实它属于Spring Framework提供的能力。SpringBoot大行其道的今天,基于XML
配置的Spring Framework的使用方式注定已成为过去式。注解驱动应用,面向元数据编程已然成受到越来越多开发者的偏好了,毕竟它的便捷程度、优势都是XML方式不可比拟的。
@Configuration
@ConditionalOnClass({PlatformTransactionManager.class})
@AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
@EnableConfigurationProperties({TransactionProperties.class})
public class TransactionAutoConfiguration {
public TransactionAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public TransactionManagerCustomizers platformTransactionManagerCustomizers(ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {
return new TransactionManagerCustomizers((Collection)customizers.orderedStream().collect(Collectors.toList()));
}
@Configuration
@ConditionalOnBean({PlatformTransactionManager.class})
@ConditionalOnMissingBean({AbstractTransactionManagementConfiguration.class})
public static class EnableTransactionManagementConfiguration {
public EnableTransactionManagementConfiguration() {
}
@Configuration
@EnableTransactionManagement(
proxyTargetClass = true
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = {"proxy-target-class"},
havingValue = "true",
matchIfMissing = true
)
//默认采用cglib代理
public static class CglibAutoProxyConfiguration {
public CglibAutoProxyConfiguration() {
}
}
@Configuration
@EnableTransactionManagement(
proxyTargetClass = false
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = {"proxy-target-class"},
havingValue = "false",
matchIfMissing = false
)
public static class JdkDynamicAutoProxyConfiguration {
public JdkDynamicAutoProxyConfiguration() {
}
}
}
@Configuration
//当PlatformTransactionManager类型的bean存在并且当存在多个bean时指定为Primary的 PlatformTransactionManager存在时,该配置类才进行解析
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {
private final PlatformTransactionManager transactionManager;
public TransactionTemplateConfiguration(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
// 由于TransactionAutoConfiguration是在DataSourceTransactionManagerAutoConfiguration之后才被解析处理的,而在DataSourceTransactionManagerAutoConfiguration中配置了transactionManager,因此, TransactionTemplateConfiguration 会被处理.
@Bean
@ConditionalOnMissingBean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(this.transactionManager);
}
}
}
PlatformTransactionManager后续章节会分析
提示:使用@EnableTransactionManagement
注解前,请务必保证你已经配置了至少一个PlatformTransactionManager
的Bean,否则会报错。(当然你也可以实现TransactionManagementConfigurer
来提供一个专属的,只是我们一般都不这么去做~~~)
开启注解驱动
@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 2147483647;
}
简直和@EnableAsync
注解的一模一样。不同之处只在于@Import
导入器导入的这个类.
对比一下
@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
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
public TransactionManagementConfigurationSelector() {
}
protected String[] selectImports(AdviceMode adviceMode) {
switch(adviceMode) {
case PROXY:
return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[]{this.determineTransactionAspectClass()};
default:
return null;
}
}
private String determineTransactionAspectClass() {
return ClassUtils.isPresent("javax.transaction.Transactional", this.getClass().getClassLoader()) ? "org.springframework.transaction.aspectj.AspectJJtaTransactionManagementConfiguration" : "org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";
}
}
依然可以看出和@EnableAsync
导入的AsyncConfigurationSelector
如出一辙,都继承自AdviceModeImportSelector
,毕竟模式一样,触类旁通,一通百通~
AdviceModeImportSelector
目前所知的三个子类是:AsyncConfigurationSelector
、TransactionManagementConfigurationSelector
、CachingConfigurationSelector
。由此可见后面还会着重分析的Spring
的缓存体系@EnableCaching
,模式也是和这个极其类似的~~~
AutoProxyRegistrar
它是个ImportBeanDefinitionRegistrar
,可以实现自己向容器里注册Bean的定义信息
// @since 3.1
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
private final Log logger = LogFactory.getLog(this.getClass());
public AutoProxyRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
// 这里面需要特别注意的是:这里是拿到所有的注解类型~~~而不是只拿@EnableAspectJAutoProxy这个类型的
// 原因:因为mode、proxyTargetClass等属性会直接影响到代理得方式,而拥有这些属性的注解至少有:
// @EnableTransactionManagement、@EnableAsync、@EnableCaching等~~~~
// 甚至还有启用AOP的注解:@EnableAspectJAutoProxy它也能设置`proxyTargetClass`这个属性的值,因此也会产生关联影响~
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
Iterator var5 = annTypes.iterator();
while(var5.hasNext()) {
String annType = (String)var5.next();
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate != 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) {
// 它主要是注册了一个`internalAutoProxyCreator`,但是若出现多次的话,这里不是覆盖的形式,而是以优先级的形式
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
//看要不要强制使用CGLIB的方式(由此可以发现 这个属性若出现多次,是会是覆盖的形式)
if ((Boolean)proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
}
if (!candidateFound && this.logger.isInfoEnabled()) {
String name = this.getClass().getSimpleName();
this.logger.info(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 occurred 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));
}
}
}
跟踪AopConfigUtils
的源码你会发现,事务这块向容器注入的是一个InfrastructureAdvisorAutoProxyCreator
,并且看看是采用CGLIB
还是JDK
代理。它主要是读取Advisor
类,并对符合的bean进行二次代理。
ProxyTransactionManagementConfiguration
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
public ProxyTransactionManagementConfiguration() {
}
@Bean(
name = {"org.springframework.transaction.config.internalTransactionAdvisor"}
)
@Role(2)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(this.transactionAttributeSource());
advisor.setAdvice(this.transactionInterceptor());
if (this.enableTx != null) {
// 顺序由@EnableTransactionManagement注解的Order属性来指定 默认值为:Ordered.LOWEST_PRECEDENCE
advisor.setOrder((Integer)this.enableTx.getNumber("order"));
}
return advisor;
}
@Bean
@Role(2)
// TransactionAttributeSource 这种类特别像 `TargetSource`这种类的设计模式
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(2)
// 事务拦截器,它是个`MethodInterceptor`,它也是Spring处理事务最为核心的部分
// 请注意:你可以自己定义一个TransactionInterceptor(同名的),来覆盖此Bean
public TransactionInterceptor transactionInterceptor() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(this.transactionAttributeSource());
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
@Nullable
protected AnnotationAttributes enableTx;
// 此处:注解的默认的事务处理器(可议通过实现接口TransactionManagementConfigurer来自定义配置)
// 因为事务管理器这个东西,一般来说全局一个就行,但是Spring也提供了定制化的能力~~~
@Nullable
protected PlatformTransactionManager txManager;
public AbstractTransactionManagementConfiguration() {
}
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTx = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
//这个注解@EnableTransactionManagement是必须的~~~~~~~~~~~~~~~~否则报错了
if (this.enableTx == null) {
throw new IllegalArgumentException("@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
}
}
@Autowired(
required = false
)
// 这里和@Async的处理一样,配置文件可以实现这个接口。然后给注解驱动的给一个默认的事务管理器~~~~
void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
// 同样的,最多也只允许你去配置一个~~~
if (configurers.size() > 1) {
throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
} else {
TransactionManagementConfigurer configurer = (TransactionManagementConfigurer)configurers.iterator().next();
this.txManager = configurer.annotationDrivenTransactionManager();
}
}
}
@Bean(
name = {"org.springframework.transaction.config.internalTransactionalEventListenerFactory"}
)
@Role(2)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
return new TransactionalEventListenerFactory();
}
}
BeanFactoryTransactionAttributeSourceAdvisor
TransactionAttributeSourcePointcut
这个就是事务的匹配Pointcut
切面,决定了哪些类需要生成代理对象从而应用事务。
// 首先它的访问权限事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();
}
@Transactional
这个事务注解可以用在类上,也可以用在方法上。
- 将事务注解标记到服务组件类级别,相当于为该服务组件的每个服务方法都应用了这个注解
- 事务注解应用在方法级别,是更细粒度的一种事务注解方式
注意 : 如果某个方法和该方法所属类上都有事务注解属性,优先使用方法上的事务注解属性。
另外,Spring 支持三个不同的事务注解 :
- Spring 事务注解
org.springframework.transaction.annotation.Transactional
- JTA事务注解 ·javax.transaction.Transactional·
- EJB 3 事务注解 ·javax.ejb.TransactionAttribute·
自定义TransactionManagementConfigurer
@Configuration
@EnableTransactionManagement
public class MyTransactionManagementConfigurer implements TransactionManagementConfigurer {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
return dataSourceTransactionManager;
}
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return null;
}
}
@EnableAspectJAutoProxy和@EnableTransactionManagement优先级?
@EnableAspectJAutoProxy
会像容器注入AnnotationAwareAspectJAutoProxyCreator
@EnableTransactionManagement
会像容器注入InfrastructureAdvisorAutoProxyCreator
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
这个自动代理创建器~~~
Springboot源码分析之@Transactional的更多相关文章
- SpringBoot源码分析之SpringBoot的启动过程
SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30 | 分类于 springboot | 0 Comments | 阅读次数 SpringB ...
- Springboot源码分析之项目结构
Springboot源码分析之项目结构 摘要: 无论是从IDEA还是其他的SDS开发工具亦或是https://start.spring.io/ 进行解压,我们都会得到同样的一个pom.xml文件 4. ...
- 从SpringBoot源码分析 配置文件的加载原理和优先级
本文从SpringBoot源码分析 配置文件的加载原理和配置文件的优先级 跟入源码之前,先提一个问题: SpringBoot 既可以加载指定目录下的配置文件获取配置项,也可以通过启动参数( ...
- Springboot源码分析之事务拦截和管理
摘要: 在springboot的自动装配事务里面,InfrastructureAdvisorAutoProxyCreator ,TransactionInterceptor,PlatformTrans ...
- springboot源码分析-SpringApplication
SpringApplication SpringApplication类提供了一种方便的方法来引导从main()方法启动的Spring应用程序 SpringBoot 包扫描注解源码分析 @Spring ...
- Springboot源码分析之jar探秘
摘要: 利用IDEA等工具打包会出现springboot-0.0.1-SNAPSHOT.jar,springboot-0.0.1-SNAPSHOT.jar.original,前面说过它们之间的关系了, ...
- Springboot源码分析之代理三板斧
摘要: 在Spring的版本变迁过程中,注解发生了很多的变化,然而代理的设计也发生了微妙的变化,从Spring1.x的ProxyFactoryBean的硬编码岛Spring2.x的Aspectj注解, ...
- Springboot源码分析之Spring循环依赖揭秘
摘要: 若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效.或许刚说到这,有的小伙伴就会大惊失色了.Spring不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让我 ...
- SpringBoot源码分析(二)启动原理
Springboot的jar启动方式,是通过IOC容器启动 带动了Web容器的启动 而Springboot的war启动方式,是通过Web容器(如Tomcat)的启动 带动了IOC容器相关的启动 一.不 ...
随机推荐
- Surging实践经验
背景 在去年9月份的时候,我入职一家做航空软件产品的公司.当时公司部门领导决定构建一个技术平台(或称为技术中台),通过该技术平台进而孵化各个业务系统.说白了就是需要通过一个分布式框架或是微服务框架提高 ...
- Winform 连接Web Service 记录
一般自己控制的项目都会使用webApi,比较少使用WS,感觉要配置一堆东西很繁琐. 场景:多个系统间数据交互. 角色:我们属于下游系统,要把一部分数据格式化后上传到SAP中. SAP提供了一个WS,使 ...
- sql nvarchar类型和varchar类型存储中文字符长度
今天遇到了,随手记录一下. sql server 存储数据里面 NVARCHAR 记录中文的时候是 一个中文对应一个字符串长度,记录英文也是一个字母一个长度 标点符号也是一样. ...
- JDK(Windows)
百度云:链接:http://pan.baidu.com/s/1dEEsIUd 密码:15cn 官网下载网址:http://www.oracle.com/technetwork/java/jav ...
- 什么是Java EE
Java:是一门编程语言,可以用来编写各种应用程序. Java EE:是一个标准中间件体系结构,旨在简化和规范分布式多层企业应用系统的开发和部署. Java EE是一个标准中间件体系结构 Java E ...
- springboot - 登录+静态资源访问+国际化
1.项目目录结构 2.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmln ...
- thinkphp 多对多表查询
1.表 班级表classes 学生表student 中间表classes_students 2.使用模型关联查询 新建模型 Classes在里面添加代码 ClassesStudent中间表模型,可以不 ...
- C语言编程入门之--第三章编写第一个C语言程序
第三章 编写第一个C语言程序 导读:一般学一门计算机语言的第一堂上机课(“上机”顾名思义,上了计算机),就是往屏幕输出“hello world”,本章也不例外. 1.1 Hello,World! 这一 ...
- EasyUI combobox下拉列表实现搜索过滤(模糊匹配)
项目中的某个下拉列表长达200多个项,这么巨大的数量一个一个找眼镜都得看花,于是就得整了个搜索功能.看网上别人帖子有只能前缀匹配的方案,但只能前缀匹配的话用起来也不是很方便.于是就记录一下模糊匹配的方 ...
- 19 个 JavaScript 编码小技巧
这篇文章适合任何一位基于JavaScript开发的开发者.我写这篇文章主要涉及JavaScript中一些简写的代码,帮助大家更好理解一些JavaScript的基础.希望这些代码能从不同的角度帮助你更好 ...