@Transactional注解 和Spring AOP(CGLIB)

序言

最近看源码时遇到一个问题苦恼很久,就是业务方法没有加@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就是和代理类相关。这是最关键的一点!

如果没有批评,赞美将毫无意义,欢迎指正批评。

路漫漫其修远兮,吾将上下而求索

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值