spring事务源码-代理对象生成过程解析
spring事务源码
spring事务的源码,要从@EnableTransactionManagement注解拉开序幕
首先,如果我们要使用spring事务,只需要在配置类上添加@EnableTransactionManagement注解,并且在业务方法上添加@Transactional注解即可(以spring项目为例,SpringBoot项目后面博客中再另说)
@EnableTransactionManagement
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {/** proxyTargetClass* true:* 无论目标方法是否实现了接口,都使用CGLIB代理* false:* 如果目标方法实现了接口,使用JDK动态代理* 如果目标方法没有实现接口,使用CGLIB代理*/boolean proxyTargetClass() default false;/*** @return** 事务通知模式(切面织入方式):* 默认是代理模式*/AdviceMode mode() default AdviceMode.PROXY;int order() default 2147483647;
}
可以看到,这个注解也挺简单的,就是通过@Import注解,引入了另外一个bean,通过查看TransactionManagementConfigurationSelector的类继承关系,可以发现,这个类实现了ImportSelector注解,所以,会实现selectImports()方法,在该方法中,注入了两个重要的bean
- AutoProxyRegistrar
- ProxyTransactionManagementConfiguration
AutoProxyRegistrar
AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以,在registerBeanDefinitions方法中,注入一个bean
InfrastructureAdvisorAutoProxyCreator
可以发现,这个类是一个后置处理器,继承了InstantiationAwareBeanPostProcessor所以:这就是@EnableTransactionManagement的第一个作用,注入了一个后置处理器,这个后置处理器就是用来对事务注解进行增强的
ProxyTransactionManagementConfiguration
该类只有一个注解@Configuration,所以该类是一个配置类,在该类中,通过三个@Bean注解,向spring容器中注入了事务执行时要用到的组件
/*** @return* 注入事务增强器* 这里是创建一个advisor,然后设置切点(TransactionInterceptor)和通知(TransactionAttributeSource)* 这里的BeanFactoryTransactionAttributeSourceAdvisor类似于aop中的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());if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}/*** @return* 往spring容器中注入事务解析器(解析事务注解上的各个参数)* 在执行第八个后置处理器的时候,判断是否需要增强的时候,会解析transaction注解** 这里在new AnnotationTransactionAttributeSource()对象的时候,有一个非常关键的点:* publicMethodsOnly 这里在调用构造函数的时候,默认初始化该值为true;该值的意思是:只允许public方法进行事务代理** 在后面判断是否可以对方法进行增强的时候,会判断该值,以及对应method是否是public,如果是非public修饰的方法,直接return null,不进行代理增强*/@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}/*** @return* 定义事务拦截器,并将事务拦截器注入到spring容器中*/@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;}
所以:该类是给spring容器中,注入了三个bean,分别是:事务拦截器、事务解析器、事务增强器
判断是否需要代理
我们知道,AOP的动态代理是在第八个后置处理器调用的时候,判断是否需要增强,如果需要增强,就通过JDK或者CGLIB进行代理,注解版的事务,也是利用了AOP实现的
这是在添加事务注解的方法对应的bean在初始化时,调用到第八个后置处理器的时候,判断是否需要进行增强的调用链,由于这里和AOP是一样的调用链,所以中间的这些过程就不做过多解释了,直接看最后匹配是否需要增强的代码
我们可以暂时先认为:methodMatcher.matches(method, targetClass) 如果这里返回的是true,就是需要进行增强,返回false,就继续遍历下一个方法,进行判断
org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut#matches
// 这里是用获取到的tas来判断method是否有添加注解,如果这里返回false,就表示当前method无需增强,返回true,需要增强
public boolean matches(Method method, Class<?> targetClass) {TransactionAttributeSource tas = this.getTransactionAttributeSource();return tas == null || tas.getTransactionAttribute(method, targetClass) != null;
}org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute/*** 这里是在解析method之前,先判断下之前是否已经解析过*/
@Override@Nullablepublic TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}// First, see if we have a cached value./*** 根据method和targetClass生成一个cacheKey* 如果这里已经对方法进行了一次解析,就会把解析之后获取到的TransactionAttribute对象和对应的key存入到一个map集合中* 这样下次再有地方用到这个方法的时候,就无须再次解析,直接从map中获取即可* 如果这里没有获取到,返回的null,就将value设置为null,写入到map中* 如果获取到对应的txAttr,就setDescriptor设置下该属性,然后写入到map集合中*/Object cacheKey = getCacheKey(method, targetClass);TransactionAttribute cached = this.attributeCache.get(cacheKey);if (cached != null) {// Value will either be canonical value indicating there is no transaction attribute,// or an actual transaction attribute.if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return cached;}}else {// We need to work it out./*** 如果是第一次进入到这里,一定会走这个方法* 这里就是判断当前方法是否是public,是否有添加@Transactional注解*/TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// Put it in the cache./*** 如果当前方法没有添加事务注解,或者不满足生成代理对象的要求,就将value设置为null,存入到这个map集合中*/if (txAttr == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {// 这里是满足增强的条件,将txAttr放到map集合中,并返回String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);}if (logger.isDebugEnabled()) {logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);}this.attributeCache.put(cacheKey, txAttr);}return txAttr;}}
下面这个方法主要是判断当前method是否是public修饰的,然后在方法中调用findTransactionAttribute来判断方法或者class是否有添加@Transactional注解
@Nullableprotected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required./*** 判断当前方法是否是public修饰的,以及是否只支持public方法进行事务代理*/if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// First try is the method in the target class./*** 2.查找当前方法是否有添加@Transactional注解,如果有添加,就解析对应的注解信息* 下面有几个重复查找的动作,这里还没有搞明白依次获取到的是什么,总之都是判断入参的这个方法或者class有没有事务注解(大致的意思应该是先判断方法有没有添加注解,然后再判断类上是否添加事务注解)*/TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// Second try is the transaction attribute on the target class.txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}if (specificMethod != method) {// Fallback is to look at the original method.txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// Last fallback is the class of the original method.txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;}
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#determineTransactionAttributeorg.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)// 这个方法中调用了findMergedAnnotationAttributes来判断当前element是否有事务注解,
// 然后调用parseTransactionAnnotation,解析@Transactional注解的配置
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false);if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}}
这里是解析@Transactional注解对应的配置信息,并将配置信息存入到了一个TransactionAttribute对象中
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;}
截止到这里,基本上就解析完毕了;如果有配置@Transactional注解,那么在最上面的methodMatcher.matches(method, targetClass) 方法就会返回true,返回true,那么spring后置处理器就任务当前类中有事务方法,需要进行增强,那就会生成代理对象,和aop的逻辑是一样的:
根据是否实现了接口,来判断是使用CGLIB代理还是使用JDK代理;所以:@Transactional注解就类似于aop中的切点(只有添加了@Transactional注解的方法,才会生成代理对象,在被调用的时候,进行事务拦截器进行处理)
spring事务应用
1.spring事务不支持非public方法的原因
如果在非public方法上添加事务注解,在发生异常的时候,事务是不会回滚的,也即:事务是不生效的
原因:
1.我们知道,spring事务其实就是利用了AOP动态代理的知识,也就是说:如果加了@Transactional注解的方法,spring会为其类生成代理对象,在调用的时候,会通过拦截器来调用
2.如果说spring不支持非public方法,那实现原理也简单:在判断是否需要进行动态代理的时候,首先判断下当前class对应的method是否是public的,如果是非public,就不进行后面的判断,直接返回false,无需代理即可;这样的话,就不会为类生成代理对象
下面这个方法在源码解析中有说到过,是bean在初始化的过程中,执行到第八个后置处理器的时候,调用的,判断是否需要对bean进行代理;在这个问题中,就不详细解释了
我们只需要知道:如果这里返回的是null,就不会对该类和该方法进行增强,如果返回了TransactionAttribute对象,就会进行增强
org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute@Nullableprotected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required./*** 判断当前方法是否是public修饰的,以及是否只支持public方法进行事务代理*/if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// First try is the method in the target class./*** 2.查找当前方法是否有添加@Transactional注解,如果有添加,就解析对应的注解信息*/TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// Second try is the transaction attribute on the target class.txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}if (specificMethod != method) {// Fallback is to look at the original method.txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// Last fallback is the class of the original method.txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;}
针对这个问题,只需要关注这一行代码就可以
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;
}/**
* 这里就是判断当前方法是否是public方法修饰的
*/
public static boolean isPublic(int mod) {return (mod & PUBLIC) != 0;
}org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#allowPublicMethodsOnly
@Override
protected boolean allowPublicMethodsOnly() {return this.publicMethodsOnly;
}在AnnotationTransactionAttributeSource这个类中,搜索可以发现,默认的都是true,并且这个类的初始化是在
ProxyTransactionManagementConfiguration中,在这个ProxyTransactionManagementConfiguration中,通过@Bean注解,注入了一个TransactionAttributeSource对象,@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}所以:这个参数铁定是true;也就是说,只允许public方法才能进行事务代理
spring事务源码-代理对象生成过程解析相关推荐
- Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析
基于最新Spring 5.x,详细介绍了Spring 事务源码,包括< tx:annotation-driven/>标签源码解析. 此前我们已经学习了Spring的<tx:a ...
- spring事务源码解析
前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...
- Spring事务源码分析责任链事务链事务不生效
文章目录 前言 带着问题分析源码 事务源码分析 寻找Spring事务源码类 TransactionInterceptor调用栈 分析Spring AOP责任链 分析TransactionInterce ...
- Spring事务源码(一)
Spring事务源码(一) 前言 一.ProxyTransactionManagementConfiguration 二.BeanFactoryTransactionAttributeSourceAd ...
- Spring源码分析-Spring事务源码分析
导语 在配置Spring事务管理的时候会用到一个类TransactionInterceptor,从下面的类关系图中可以看到TransactionInterceptor继承了MethodInt ...
- Spring事务源码详解
一. 简介 事务: 事务是逻辑上的一组操作,要么都执行,要么都不执行,关于事务的基本知识可以看我的这篇文章:事务的基础知识 Spring事务: Spring 支持两种方式的事务管理:编程式事务管理.声 ...
- 【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)
Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...
- 足够应付面试的Spring事务源码阅读梳理
来源:编程新说 Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答 ...
- 事务注解放到类上面 下面私有方法有效吗_【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)...
Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...
- Spring事务源码分析
首先看例子,这例子摘抄自开涛的跟我学spring3. @Test public void testPlatformTransactionManager() { DefaultTransactionDe ...
最新文章
- linux里与Ctrl组合使用的键
- HTML和jQuery拆分成两个文件
- MySQL里的wait_timeout
- 使鼠标保持按住状态_MouseInc 鼠标手势工具
- 解决import keras后出现的一系列问题
- 初中毕业自考计算机专业难吗,初中学历自考本科难吗?初中毕业自考大专步骤有哪些?...
- Android应用开发相关下载资源
- php简单的mysql类_一个简单的php mysql操作类
- RocketMQ的安装与配置
- MYSQL分页优化查询
- js hover图片放大不遮挡_CSS3+JS 实现放大镜
- linux grep命令 例子,14个grep命令使用例子
- 新想法!华科、清华、康奈尔联合提出“圆形卷积”!聚合方形和圆形优势
- oracle 查看锁死的表
- Virtual Machine Manager 2012 R2利用服务模板部署SQL
- Java面向对象练习题之三角形
- Axure8.0如何汉化?
- 让LYNC安装更容易些
- msi b460m pro wifi黑苹果 efi
- 如何做到像百度云或者网易公开课一样动态更换APP启动图