springboot启动流程(十二)springboot事务自动配置
所有文章
https://www.cnblogs.com/lay2017/p/11478237.html
正文
在上一篇文章中,我们简单了解了aop的处理过程。代理增强之前,先生成Advisor,然后利用cglib或者jdk动态代理把可以应用到当前Bean的Advisor增强到Bean上。
springboot的事务,也是基于aop实现。那么我们就需要把事务相关的配置生成Advisor,然后一样地增强到Bean上。
生成Advisor
首先,我们先找到事务的自动配置类TransactionAutoConfiguration
@Configuration
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
// 省略 @Configuration
@ConditionalOnBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration { // 省略 @Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration { } } }
这里,我们注意到@EnableTransactionManagement这个注解。熟悉spring的我们都知道,这个意味着开启事务管理。我们打开这个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
//
}
跟其它@Enable ** 形式的注解一样,通过@Import注解导入了一个类,跟进TransactionManagementConfigurationSelector看看
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
// 默认是proxy
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
//
}
这里将会导入两个类AutoProxyRegistrar和ProxyTransactionManagementConfiguration,前者是处理代理,后者是处理事务配置的。上一篇讲述aop的时候我们说过,创建代理将会通过AnnotationAwareAspectJAutoProxyCreator来处理,所以这里的AutoProxyRegistrar比AnnotationAwareAspectJAutoProxyCreator的优先级低。
ProxyTransactionManagementConfiguration是处理事务配置的,我们跟进它
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor());
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
} @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
} @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;
} }
正如我们开篇说的,事务是基于aop的,会去生成Advisor。我们看到transactionAdvisor将会返回一个BeanFactoryTransactionAttributeSourceAdvisor。我们看看它的uml类图

这里就是生成了一个Advisor,并作为Bean存在于BeanFactory当中。
增强到Bean
我们打开AbstractAutoProxyCreator的wrapIfNecessary方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//
// 获取可以增强到当前Bean的Advisor
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
getAdvicesAndAdvisorsForBean将会获取到事务生成的Advisor,然后createProxy将会进行代理增强,cglib或者jdk动态代理的方式。
跟进AbstractAdvisorAutoProxyCreator的findEligibleAdvisors方法
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 找到Bean工厂里所有Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 获取可以增强到当前Bean的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
跟进findAdvisorsThatCanApply看看怎么判断是否可以增强
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 找到可以增强的Advisor
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
继续跟进findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
//
List<Advisor> eligibleAdvisors = new ArrayList<>();
//
for (Advisor candidate : candidateAdvisors) {
//
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
判断逻辑落在了canApply方法上,跟进它
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
// 进入这里分支
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
return true;
}
}
这里的canApply将会判断PointcutAdvisor是否能增强到targetClass上,继续跟进canApply
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
//
MethodMatcher methodMatcher = pc.getMethodMatcher();
//
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
// 获取所有方法
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
// 判断是否能够应用在方法上
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
总结
本文简单过了一下事务的自动配置到代理增强。事务基于自动配置机制和aop,自动配置机制将会生成Advisor,然后通过代理增强到Bean上,从而实现事务的代理增强。
springboot启动流程(十二)springboot事务自动配置的更多相关文章
- springboot启动流程(二)SpringApplication run方法核心逻辑
所有文章 https://www.cnblogs.com/lay2017/p/11478237.html run方法逻辑 在上一篇文章中,我们看到SpringApplication的静态方法最终是去构 ...
- Spring Boot2 系列教程(二十一) | 自动配置原理
微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 这个月过去两天了,这篇文章才跟大家见面,最近比较累,大家见谅下.下班后闲着无聊看了下 SpringBoot 中的自动配置,把我 ...
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(二):SpringApplication的run方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(六):IoC容器依赖注入
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程及其原理
Spring Boot.Spring MVC 和 Spring 有什么区别? 分别描述各自的特征: Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等:但他们的 ...
- SpringBoot启动流程解析
写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...
- SpringBoot启动流程分析(一):SpringApplication类初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(四):IoC容器的初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
随机推荐
- flutter showDatePicker显示中文日期_Flutter时间控件显示中文
flutter showDatePicker showTimePicker显示中文日期 1.配置flutter_localizations依赖 找到pubspec.yaml配置flutter_loca ...
- python数据可视化:pyecharts
发现了一个做数据可视化非常好的库:pyecharts.非常便捷好用,大力推荐!! 官方介绍:pyecharts 是一个用于生成 Echarts 图表的类库.Echarts 是百度开源的一个数据可视化 ...
- 教你如何在linux上装逼,shell中颜色的设置
linux启动后环境变量加载的顺序为:etc/profile → /etc/profile.d/*.sh → ~/.bash_profile → ~/.bashrc → [/etc/bashrc] 想 ...
- Ubuntu 16.04 haproxy + keeplive
WEB架构
- sublime Text3文件路径提示功能
1>启动sublime Text3编辑器: 2> 点开菜单“preferences”——“package control”: 3>在弹出的下拉框中选择“install package ...
- node.js使用superagent实现模拟登陆功能(包含下载验证码功能)
superagent版本:3.8.3 样例代码: var process = require('process'); var superagent = require('superagent'); v ...
- JS读取本地文件及目录的方法
Javascript是网页制作中离不开的脚本语言,依靠它,一个网页的内容才生动活泼.富有朝气.但也许你还没有发现并应用它的一些更高级 的功能吧?比如,对文件和文件夹进行读.写和删除,就象在VB.VC等 ...
- 第二十四章 在线会话管理——《跟我学Shiro》
目录贴:跟我学Shiro目录贴 有时候需要显示当前在线人数.当前在线用户,有时候可能需要强制某个用户下线等:此时就需要获取相应的在线用户并进行一些操作. 本章基于<第十六章 综合实例>代码 ...
- java的mock工具:mockito
https://site.mockito.org https://github.com/mockito/mockito https://github.com/hehonghui/mockito-doc ...
- maven项目创建.m2文件夹
创建为.m2.,m2前后都要有点,然后去掉后面的点 settings.xml文件如下: <?xml version="1.0" encoding="UTF-8&qu ...