1.前言

系统应用开发过程中通常需要使用事务来保证业务数据的一致性,实现方式如:开启事务、执行数据库写操作、提交或者回滚事务,这种标准实现方式适用于少量一致性业务,如果存在大量需要保证数据一致性的业务,不仅会让开发人员重复编码,还会给系统造成冗余代码。基于这些问题,伟大的Spring框架为我们提供了@Transactional注解,那么它是如何使用一个注解就解决了我们的烦恼呢?我们该如何着手进行分析呢?
SpringBoot集成的功能往往要从一个xxxAutoConfiguration开始说起

2.自动配置

打开TransactionAutoConfiguration自动配置类可以看到一个比较重要的注解@EnableTransactionManagement用于开启事务管理功能,@EnableTransactionManagement注解又导入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration

2.1 事务配置

ProxyTransactionManagementConfiguration中声明了一个切面BeanFactoryTransactionAttributeSourceAdvisor,看到切面必定会有相对应的切点TransactionAttributeSourcePointcut(用于声明切入的范围)和通知TransactionInterceptor(用于实现切入目标的后续操作)。

2.2 声明@Transactional注解处理器

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();
}

AnnotationTransactionAttributeSource实例化指定了注解解析器为SpringTransactionAnnotationParser

可以看到该解析器主要用来处理@Transactional注解

2.3 注入自动代理注册器

在2.自动配置提到@EnableTransactionManagement还引入了AutoProxyRegistrar,向IOC容器中注入InfrastructureAdvisorAutoProxyCreator

@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

InfrastructureAdvisorAutoProxyCreator实现了BeanPostProcessor接口,具有拦截并处理Bean的能力

2.3.1 Bean后置处理

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}

2.3.2 从容器中获取所有的Advisor

/*** Find all candidate Advisors to use in auto-proxying.* @return the List of candidate Advisors*/
protected List<Advisor> findCandidateAdvisors() {Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");return this.advisorRetrievalHelper.findAdvisorBeans();
}

2.3.3 筛选出符合条件的Advisor

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {// 1.获取切点对应的MethodMatcherMethodMatcher methodMatcher = pc.getMethodMatcher();for (Class<?> clazz : classes) {// 2.获取当前类中的所有方法Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {// 3.判断是否符合切点要求,此处的methodMatcher为TransactionAttributeSourcePointcutif (methodMatcher.matches(method, targetClass)) {return true;}}}return false;
}

判断方法上是否有@Transactional注解,如果有则使用SpringTransactionAnnotationParser进行解析并生成TransactionAttribute

2.3.4 Advisor排序

sortAdvisors(eligibleAdvisors);

2.3.4 小结

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 1.从容器中获取所有类型为Advisor的切面List<Advisor> candidateAdvisors = findCandidateAdvisors();// 2.筛选出符合条件的切面(也就是类或方法上被@Transactional注解标注)List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {// 3.对符合条件的切面进行升序排序eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}

2.4 选择代理方法

@Override

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (!IN_NATIVE_IMAGE &&(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();// 1.如果实现接口则选择jdk代理if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// 2.选择cglib代理return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}
}

2.4.1 生成代理

此处以cglib为例

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {try {Class<?> rootClass = this.advised.getTargetClass();Class<?> proxySuperClass = rootClass;// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));Callback[] callbacks = getCallbacks(rootClass);}
}
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {// Choose an "aop" interceptor (used for AOP calls).Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
}

创建Enhancer并指定回调为DynamicAdvisedInterceptor

2.5 调用代理

执行被代理对象目标方法userService.saveUser(user);时会调用DynamicAdvisedInterceptor的intercept()方法

2.5.1 筛选满足条件的Advice

@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {// 1.遍历所有满足条件的Advisor,也就是2.3.3章节返回的Advisorfor (Advisor advisor : advisors) {if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();// 2. 判断是否满足切点要求boolean match = mm.matches(method, actualClass);// 3.满足切点要求if (match) {// 3. 获取切面对应的通知,也就是TransactionInterceptorMethodInterceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}}}// 4.返回满足条件的通知return interceptorList;
}

2.5.2 执行满足条件的Advice

@Override
@Nullable
public Object proceed() throws Throwable {// 1.如果没有advice可以执行,则执行目标方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}// 2.从advice列表中取出一个adviceObject interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);// 3.执行advice的invoke方法,也就是TransactionInterceptor的invokereturn ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}

3. 事务调用

3.1 获取事务配置属性

TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

也就是@Transactional注解声明的属性

3.2 获取事务管理器

final TransactionManager tm = determineTransactionManager(txAttr);

从容器中获取DataSourceTransactionManagerAutoConfiguration自动配置类中声明的事务管理器JdbcTransactionManager

public class DataSourceTransactionManagerAutoConfiguration {@Bean@ConditionalOnMissingBean(TransactionManager.class)JdbcTransactionManager transactionManager(DataSource dataSource,ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {JdbcTransactionManager transactionManager = new JdbcTransactionManager(dataSource);transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));return transactionManager;}
}

3.3 执行事务

4. 总结

@Transactional实现原理三要素切面、切点、通知

  • InfrastructureAdvisorAutoProxyCreator后置处理器拦截所有Bean
  • 遍历所有类型为Advisor的切面
  • 返回满足切点条件的切面列表
  • 选择代理方法
  • 生成代理
  • 调用通知的invoke()方法
    开启事务
    调用其它通知的invoke()方法,如果没有执行目标方法
    执行异常,回滚事务
    执行成功,提交事务
  • 执行目标方法

了解@Transactional注解实现原理,不仅可以让我们对切面、切点、通知有一个清晰的认识,还可以让我们通过其思想实现类似功能,如@Cache注解实现应用缓存,@Async注解实现业务异步执行

Java高级架构面试知识点整理

我整理的《最全Java高级架构面试知识点整理》已升级为2.0版本,200个知识点,178页。私信我“Java”获取。

你必须懂!也可以懂的@Transactional原理!相关推荐

  1. 【分享】老调重弹,既懂技术又懂管理的人才发展中的实际问题

    背景:最近参加了校友组织的计算机技术小圈子(20+人)分享会,分享主题:老调重弹,既懂技术又懂管理的人才发展中的实际问题,分享人是马老师,86级校友.果然是满满干货,本文结合分享内容和大家对分享内容不 ...

  2. 23、90秒快速“读懂”STP(生成树)工作原理

    23.90秒快速"读懂"STP(生成树)工作原理 https://www.toutiao.com/i6794982558787437063/?tt_from=weixin& ...

  3. 像哆啦A梦懂大雄一样懂客户,我们也会拥有百宝箱

    一般卫生间的标语是这样的: 来也匆匆,去也冲冲 阿里云 卫生间的标语是这样的: 像哆啦A梦懂大雄一样懂客户 我们也会拥有百宝箱 这样的: 如果白居易是个产品经理 老奶奶玩的6就发布 老奶奶玩不转就继续 ...

  4. 如何用 IT 业者能听懂的话介绍量子计算的原理?

    如何用 IT 业者能听懂的话介绍量子计算的原理? 赞同642 反对,不会显示你的姓名 Summer Clover,机器学习&量子算法 收起 陈旭鹏.杨发枝.彭磊 等人赞同 谢邀. 量子计算/量 ...

  5. 从制造中来到制造中去 华为要做更懂制造的“懂行人”

    通过自身技术实践与能力输出,华为正在帮助制造企业加速向智能制造转型. 出品 | 常言道 作者 | 丁常彦 俗话说,无农不稳,无工不富,无商不活.但在这三个产业中,工业是真正具有强大造血功能的产业,对经 ...

  6. 一文搞懂 Cocos Creator 3.0 坐标转换原理

    一文搞懂 Cocos Creator 3.0 坐标转换原理 屏幕坐标 UI 触点坐标 UI 多分辨率适配方案 UI 触点获取 不同坐标之间的转换 屏幕坐标与 3D 节点世界坐标互转 3D 节点之间的坐 ...

  7. 【转帖】每一个不懂爱的人都会遇到一个懂爱的人,然后经历一场撕心裂肺的爱情。不懂爱的人慢慢懂了。懂爱的人,却不敢再爱了。。。

    每一个不懂爱的人都会遇到一个懂爱的人,然后经历一场撕心裂肺的爱情.不懂爱的人慢慢懂了.懂爱的人,却不敢再爱了... 每一个不懂爱的人都会遇到一个懂爱的人 这个懂爱的人会让那个不懂爱的改变 让他长大 让 ...

  8. 生活需要懂点技巧…懂点策略…懂点计谋……【心灵悟语】

    生活需要懂点计谋(22则) 1.看穿但不说穿.很多事情,只要自己心里有数就好了,没必要说出来. 2.高兴,就笑,让大家都知道.悲伤,就假装什么也没发生. 3.在不违背原则的情况下,对别人要宽容 ,能帮 ...

  9. 懂了!运算放大器的工作原理

    本文旨在学习如何快速简单地对运算放大器进行分析; 1 运算放大器(OPAMP) 2 虚短和虚断 3 反向放大器 3.1 典型电路 3.2 放大倍数 3.3 仿真结果 4 同向放大器 4.1 双电源 4 ...

最新文章

  1. 简单几步搞定ISA ×××
  2. 第1-10个xhtml程序
  3. 初识MQ--mq常见技术介绍
  4. c#12星座速配代码_白羊座今日运势|2020/12/11
  5. c语言uint8的数组怎么转换为uint32_剖析JS和Redis的数据结构设计:数组
  6. MySQL:too many connections
  7. C/C++:各类型变量占用字节
  8. Atitit 硬件 软件 的开源工作 差异对比
  9. poj 2229 Sumsets(类似于n的m划分)
  10. ftk学习记(waitbox篇)
  11. 【数据结构笔记40】哈希表冲突处理方法:开放地址法(线性探测、平方探测、双散列、再散列),分离链接法
  12. php指定编码创建,MYSQL创建数据库时指定编码
  13. AS 中强制类型转换
  14. 火星开发的价值_发现“火星”岩石密度比预想更低,火星探测开发的“九大价值”...
  15. glide 设置宽高_Glide加载ImageView显示不全的问题(宽高比一致,以及fitxy/centerCrop)...
  16. rational license key error解决办法
  17. 华为交换机默认vlan都是通的吗_最全的华为交换机vlan配置教程
  18. 计算机cpu的功能和作用是什么意思,电脑的CPU和内存都起什么作用?
  19. 掌阅标签功能能否自定义名字?
  20. 电脑误删除的文件怎么恢复

热门文章

  1. 前端CDN资源库,再也不用担心vue首次加载慢的问题了,vue项目必备cdn加速
  2. Theano的安装及GPU的配置
  3. GraphQL初探:Java服务示例及Yahoo/Elide
  4. 普通壳的脱壳方法和脱壳技巧
  5. win7如何启用计算机的无线功能,win7如何开启无线功能
  6. 高项_第十章项目沟通管理
  7. 读书札记--《金庸评传》
  8. 2021SC@SDUSC-Zxing(一):Zxing初步认识
  9. NMOS双向转换电路实测以及上升沿尖峰处理
  10. 世界顶级的数据密集型处理系统揭秘