一周深度学习一个知识--事务类内部调用失效 学习总结篇

17 篇文章 0 订阅
15 篇文章 0 订阅

前言:

辅助文章 超详细全文总结 请查看:链接: 事务详细解析

首先类内部调用事务失效是因为AOP 实现的 我们了解AOP的同学都知道 AOP 的强大点能在对 事务 日志等方面处理上体现出来
因为 @Transactional 的工作机制是基于 AOP 实现,AOP 是使用动态代理实现的,如果通过代理直接调用 方法,通过 AOP 会前后进行增强,增强的逻辑其实就是在 方法 的前后分别加上开启、提交事务的逻辑 但是如果调用方没增加事务控制 被调用方增加 那就会造成 调用方没有AOP增强 出现异常事务也就监控不到了
例子:
在这里插入图片描述
这种情况下事务不会生效

源码解析 失效

我们进入源码解析 其中 是个人理解 如果有误差请大佬们指出问题共同进步

图一

解释:

  • MethodInterceptor:方法拦截器,它是一个接口,用于Spring AOP编程中的动态代理.实现该接口可以对需要增强的方法进行增强
  • <aop:advisor>:大多用于事务管理
  • this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass) 获取拦截器 判断方法需不需要增强 如果需要增强就返回拦截链 这个直接决定 是否启用AOP 增强 也就关系到 事务是否有效
  • chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method): 判断当前方法时候有拦截链 如果没有就 不进行动态代理 官方解释:检查我们是否只有一个 调用拦截器:也就是说,没有真正的建议,而只是对目标的反射调用 没有拦截链就是 调用不增强
  • new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(): 调用Cglib 方法 进行后续处理

框起来的都是接下来比较重点的方法
在这里插入图片描述

解读:
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	        // 旧代理
			Object oldProxy = null;
			// 设置代理上下问
			boolean setProxyContext = false;
			// 目标
			Object target = null;
			// 目标资源数据
			TargetSource targetSource = this.advised.getTargetSource();
			try {
			     // 是否暴露代理 默认是false
				if (this.advised.exposeProxy) {

					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
                //返回一个目标实例。在 AOP 框架调用 AOP 方法调用的“目标”之前立即调用。包含连接.         点的目标对象,如果没有实际的目标实例,则返回 null 抛出:异常 - 如果无法解析目标对象
                // 数据详情请看 图1.1
				target = targetSource.getTarget();
				// targetClass = class com.boailian.excelhander.serviceimpl.AffairServiceImpl
				Class<?> targetClass = (target != null ? target.getClass() : null);
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;

				if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {

					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					try {
						retVal = methodProxy.invoke(target, argsToUse);
					}
					catch (CodeGenerationException ex) {
						CglibMethodInvocation.logFastClassGenerationFailure(method);
						retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
					}
				}
				else {

					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

图 1.1
在这里插入图片描述

拦截器🌟🌟🌟🌟🌟

this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
方法内部代码

  • this.methodCache: 以 Method 为键,以顾问链 List 为值进行缓存
    cached == null条件成立 这个时候就说明当前方法 还没有进行判断 是否拥有拦截链 决定方法是否可以增强 (已经判断 但是没有拦截链 是 0)这个时候就会去调用
this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass) // 获取拦截器和动态拦截建议 				

当Method是:affairExamination(com.boailian.excelhander.entity.Affair)
targetClass:class com.boailian.excelhander.serviceimpl.AffairServiceImpl
在这里插入图片描述
进入getInterceptorsAndDynamicInterceptionAdvice 方法

	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        //config是ProxyFactory对象,获取增强器,也就是我们自定义的通知方法
		Advisor[] advisors = config.getAdvisors();
        //要返回的结果对象
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;
        //遍历所有的增强器
		for (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();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
                     //根据目标类型和方法,与方法匹配器是否匹配
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
					 //检查目标方法是否有资格获得通知
						match = mm.matches(method, actualClass);
					}
                    //如果匹配成功,则该方法要增强
					if (match) {
                       //将该增强器转换为方法拦截器
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
						// 添加拦截链
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
 
		return interceptorList;
	}

校验结果是false。不进行方法增强 当前方法没有拦截链 不具备通知的资格 也就是不会对方法进行增强
在这里插入图片描述
处理完之后会将结果放到 this.methodCache.put(cacheKey, cached);
在这里插入图片描述
调用affairExamination 返回0 进入下一步 :

在这里插入图片描述
还有一个判断 :CglibMethodInvocation.isMethodProxyCompatible(method) 判断方法代理是否兼容
可以看出 有两个比较明显的判断条件 :Modifier.isPublic(method.getModifiers()) 是否是公共方法
method.getDeclaringClass() != Object.class 是否是Object.class 的派生类 我们都符合 所以是true
在这里插入图片描述
符合条件 进入:
解释:我们可以跳过创建 方法调用:直接调用目标。请注意,最终调用者必须是 调用者拦截器,因此我们知道它只对目标执行反射操作,并且没有热交换或花哨的代理
还是上面那句话 没有拦截链就没有通知的资格 也就没有代理增强 也就仅仅是方法调用 事务失效
在这里插入图片描述
出现错误 不调用回滚方法 直接抛出
在这里插入图片描述

小总结
  1. 直接调用方 没有添加事务注解 所以没有获取到拦截器链 不具备通知资格
  2. 没有拦截链 就不进行AOP 代理增强 出现异常 事务不回滚 事务失效

直接调用方 增加事务注解 生效

在这里插入图片描述

执行拦截器链

和失效相反 执行拦截器解析
参数解释:
proxy:代理类(com.boailian.excelhander.serviceimpl.AffairServiceImpl )
target:目标 (com.baomidou.mybatisplus.core.override.MybatisMapperProxy)
method:方法 (AffairServiceImpl.affairExamination(com.boailian.excelhander.entity.Affair))
args:参数 (Affair(id=null, name=事务测试, affairType=1))
targetClass:目标类(class com.boailian.excelhander.serviceimpl.AffairServiceImpl)
chain:事务拦截器
methodProxy:方法代理
在这里插入图片描述

处理拦截器

在这里插入图片描述

出现异常调用 回滚方法

在这里插入图片描述

小总结

  1. 判断方法有没有事务拦截链
  2. 有的话就进行拦截链处理
  3. 对方法进行增强。增加连接点 允许通知 在方法前后增加提交回滚
  4. 执行sql
  5. 出现异常 调用回滚方法
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

变成派大星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值