辅助文章 超详细全文总结 请查看:链接: 事务详细解析
首先类内部调用事务失效是因为AOP 实现的 我们了解AOP的同学都知道 AOP 的强大点能在对 事务 日志等方面处理上体现出来
因为 @Transactional 的工作机制是基于 AOP
实现,AOP 是使用动态代理实现的,如果通过代理直接调用 方法,通过 AOP 会前后进行增强,增强的逻辑其实就是在 方法 的前后分别加上开启、提交事务的逻辑 但是如果调用方没增加事务控制 被调用方增加 那就会造成 调用方没有AOP增强 出现异常事务也就监控不到了
例子:
这种情况下事务不会生效
我们进入源码解析 其中 是个人理解 如果有误差请大佬们指出问题共同进步
解释:
MethodInterceptor
:方法拦截器,它是一个接口,用于Spring AOP编程中的动态代理.实现该接口可以对需要增强的方法进行增强
:
大多用于事务管理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
符合条件 进入:
解释:我们可以跳过创建 方法调用:直接调用目标。请注意,最终调用者必须是 调用者拦截器,因此我们知道它只对目标执行反射操作,并且没有热交换或花哨的代理
还是上面那句话 没有拦截链就没有通知的资格 也就没有代理增强 也就仅仅是方法调用 事务失效
出现错误 不调用回滚方法 直接抛出
和失效相反 执行拦截器解析
参数解释:
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
:方法代理