序言
最近看源码时遇到一个问题苦恼很久,就是业务方法没有加@Transactiona注解 aop之类的已动态代理为原理的技术,但是对应的接口方法还是进入了CGLIB动态代理,找了半天发现原因。
正文
在一个service接口实现类中有一个业务方法添加了@Transactional注解的话,使用它的类在spring的环境下直接@Autowired自动装配注入时 ,类在初始化构造时会直接动态代理整个类
1.在调用有@Transactional注解的方法是会进入cglibAOP中的拦截器调用动态代理的方法
2.在调用没有@Transactional注解的方法是会进入cglibAOP中的拦截器直接反射调用原方法
源码解析
1.没有@Transactiona注解源码执行的流程
CglibAopProxy-> intercept->methodProxy.invoke(target, argsToUse); 反射调用原方法
2.有@Transactiona注解源码执行的流程
CglibAopProxy-> intercept-> CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();->ReflectiveMethodInvocation.proceed();->((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this); 调用动态代理方法
proxy:动态代理生成的代理类
method:当前method方法
args:方法参数
methodProxy:动态代理方法
advised:动态代理的配置信息一类东西
@Nullable
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();
Object var16;
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//获取动态代理源对象
target = targetSource.getTarget();
//反射获得对象
Class<?> targetClass = target != null ? target.getClass() : null;
//获取当前动态代理的拦截器 里面实现代理逻辑
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
//不用动态代理切 方法修饰符为Public
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
//获取方法参数
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
//反射调用原target方法
retVal = methodProxy.invoke(target, argsToUse);
} else {
//需要动态代理 进入动态代理逻辑 并调用动态代理之后的方法得到结果
retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
}
//确认返回类型正确
retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal);
var16 = retVal;
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
return var16;
}
//反射调用
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//反射调用原目标
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
//反射调用增强方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//反射调动态代理增强目标用原目标
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
//反射调用动态代理方法 ReflectiveMethodInvocation
protected ReflectiveMethodInvocation(Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments, @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
@Nullable
public Object proceed() throws Throwable {
//拦截器到末尾直接调用 切入点方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.invokeJoinpoint();
} else {
//获取当前index下拦截器
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass();
return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
} else {
//调用动态代理方法
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
}
//确认返回类型
@Nullable
private static Object processReturnType(Object proxy, @Nullable Object target, Method method, @Nullable Object returnValue) {
if (returnValue != null && returnValue == target && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
returnValue = proxy;
}
Class<?> returnType = method.getReturnType();
if (returnValue == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
} else {
return returnValue;
}
}
这里面有几点需要大家留意:
A. 一个功能是否要事务,必须纳入设计、编码考虑。不能仅仅完成了基本功能就ok。
B. 如果加了事务,必须做好开发环境测试(测试环境也尽量触发异常、测试回滚),确保事务生效。
C. 以下列了事务使用过程的注意事项,请大家留意。
1.不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。
2.不要图省事,将@Transactional放置在类级的声明中,放在类声明,会使得所有方法都有事务。故@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。
3.使用了@Transactional的方法,对同一个类里面的方法调用, @Transactional无效。比如有一个类Test,它的一个方法A,A再调用Test本类的方法B(不管B是否public还是private),但A没有声明注解事务,而B有。则外部调用A之后,B的事务是不会起作用的。(经常在这里出错)
4.使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其他类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。
5.spring的事务在抛异常的时候会回滚,如果是catch捕获了,事务无效。可以在catch里面加上throw new RuntimeException();
6.最后有个关键的一点:和锁同时使用需要注意:由于Spring事务是通过AOP实现的,所以在方法执行之前会有开启事务,之后会有提交事务逻辑。而synchronized代码块执行是在事务之内执行的,可以推断在synchronized代码块执行完时,事务还未提交,其他线程进入synchronized代码块后,读取的数据不是最新的。
所以必须使synchronized锁的范围大于事务控制的范围,把synchronized加到Controller层或者大于事务边界的调用层!
10.1理解MethodProxy
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package net.sf.cglib.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.sf.cglib.core.AbstractClassGenerator;
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastClass.Generator;
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
private final Object initLock = new Object();
private volatile MethodProxy.FastClassInfo fastClassInfo;
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
return proxy;
}
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
Generator g = new Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
// 为了便于观看,省略部分非关键代码
public static MethodProxy find(Class type, Signature sig) {
try {
Method m = type.getDeclaredMethod("CGLIB$findMethodProxy", MethodInterceptorGenerator.FIND_PROXY_TYPES);
return (MethodProxy)m.invoke((Object)null, sig);
} catch (NoSuchMethodException var3) {
throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
} catch (IllegalAccessException var4) {
throw new CodeGenerationException(var4);
} catch (InvocationTargetException var5) {
throw new CodeGenerationException(var5);
}
}
//调用被代理对象
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
//调用增强类 调用代理对象
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
private static class CreateInfo {
// 省略
}
private static class FastClassInfo {
// 省略
}
}
1.代码里实际维护了两个东西,一个指向被代理的对象,一个指向代理对象。且和被代理对象相关的变量以1结尾,而与代理对象相关的变量则以2结尾这个通过create方法的源码即可知道。
2.我们在调用create方法时,第一个参数var1,对应create方法中的形参class1,而var1指向的是被代理类,所以在MethodProxy类中,xxx1就是和被代理类有关;类似的,xxx2就是和代理类相关。这是最关键的一点!
如果没有批评,赞美将毫无意义,欢迎指正批评。
路漫漫其修远兮,吾将上下而求索