springboot

Spring事务自调用失效原因和Spring AOP原理

2018-10-31  本文已影响0人  苏小小北

前言


非事务方法的调用对象是代理对象,但是spring 事务机制只查看带有@Transcational注解的方法,所有非事务方法不会以事务方法执行;事务方法的调用对象是原对象,是不会触发通知(事务)的。

Spring事务自调用失效

@Transcational在某些场景下会失效,在实际生产中,这是个要注意的问题。一个类方法A调用该类的方法B,当用代理类执行方法A时,方法A是由代理对象反射完成的,而方法B并不是反射执行的。下面是一个Spring 事务的例子,

public class TestService {
    public int insertUsers(List<User> userList) {
        System.out.println("insertUsers ...");
        int count = 0;
        for (User user: userList) {
            count += insertUser(user);
        }
        return count;
    }
    @Transactional(isolation = Isolation.READ_COMMITED,
        propagation = Propogation.REQUIRES_NEW)
    public int insertUser(User user) {
        System.out.println("insertUser ...");
        return 1;
    }
}

非事务方法insertUsers中调用的自身类的事务方法insertUser方法,

Spring AOP原理

spring aop主要是通过代理类和反射来实现的,主要两个方式:JDK代理和cglib代理。区别在于代理类是否实现了接口,实现了接口一般用JDK实现,没有实现接口一般用cglib实现,这里主要介绍JDK代理原理。

(1)首先定义一个接口类HelloService

public interface HelloService {
    void sayHello(String name);
    void say(String word);
}

(2)定义一个接口实现类HelloServiceImp,也就是我们要代理的类

public class HelloServiceImp implements HelloService {

    @Override
    public void sayHello(String name) {
        System.out.println("hello " + name);
    }


    @Override
    public void say(String word) {
        System.out.println(word);
    }
}

(3)定义Interceptor接口

按照spring aop的切面来定义,分为五种

public interface Interceptor {
    boolean before();
    void after();
    Object around(Invocation invocation)
        throws InvocationTargetException, IllegalAccessException;
    void afterReturning();
    void afterThrowing();
}

(4)定义Interceptor实现类

Invocation类是反射执行方法,定义见(5)Invocation

public class MyInterceptor implements Interceptor {

    @Override
    public boolean before() {
        System.out.println("before ...");
        return true;
    }
    @Override
    public void after() {
        System.out.println("after ...");
    }

    /**
     * 
     * @param invocation 反射执行方法,定义见下Invocation类
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    @Override
    public Object around(Invocation invocation)
        throws InvocationTargetException, IllegalAccessException {
        System.out.println("around before ...");
        Object obj = invocation.proceed();//反射执行方法
        System.out.println("around after ...");
        return obj;
    }
    @Override
    public void afterReturning() {
        System.out.println("afterReturning ...");
    }
    @Override
    public void afterThrowing() {
        System.out.println("afterThrowing ...");
    }
}

(5)定义Invocation,用于反射执行方法

public class Invocation {
    private Object[] params;//代理方法参数
    private Method method;//代理方法
    private Object target;//代理对象
    public Invocation(Object target, Method method, Object[] params) {
        this.target = target;
        this.method = method;
        this.params = params;
    }

    public Object proceed() 
        throws InvocationTargetException, IllegalAccessException {
        //反射执行方法
        return method.invoke(target, params);
    }
}

(4)代理类ProxyBean

代理类的实现主要两个jdk类:Proxy类和InvacationHandler接口
先通过InvocationHandler接口的invoke方法来定义(反射方法执行和各种aop通知)
再通过Proxy.newProxyInstance方法传入InvocationHandler对象获取代理对象。这样代理对象就会执行被代理对象的方法,同时还能在代理时发出aop(5种)通知。

public class ProxyBean implements InvocationHandler {
    private Object target = null;
    private Interceptor interceptor = null;
    public static Object getProxyBean(Object target, Interceptor interceptor) {
        ProxyBean proxyBean = new ProxyBean();
        proxyBean.target = target;
        proxyBean.interceptor = interceptor;
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
            target.getClass().getInterfaces(), proxyBean);
        return proxy;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable {
        System.out.println("通过代理对象执行方法:" + method.getName());
        boolean exceptionFlag = false;
        Invocation invocation = new Invocation(target, method, args);
        Object retObj = null;
        try {
            if (this.interceptor.before()) {
                retObj = this.interceptor.around(invocation);
            } else {
                retObj = method.invoke(target, args);
            }
        } catch (Exception e) {
            exceptionFlag = true;
        }
        this.interceptor.after();
        if (exceptionFlag) {
            this.interceptor.afterThrowing();
        } else {
            this.interceptor.afterReturning();
            return retObj;//正常返回
        }
        return null;//异常返回
    }
}

(4)测试

public class Test {
    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImp();
        HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, 
            new MyInterceptor());
        proxy.sayHello("Hinson");
    }
}

结果:

通过代理对象执行方法:sayHello
before ...
around before ...
hello Hinson
around after ...
after ...
afterReturning ...

这就是spring aop的原理,如果在HelloServiceImp中的sayHello方法调用say方法,那么say方法是由代理对象来执行的,还是由原对象执行的呢?
HelloServiceImp修改如下

public class HelloServiceImp implements HelloService {
    @Override
    public void sayHello(String name) {
//        System.out.println("hello " + name);
        say("hello " + name);
    }
    @Override
    public void say(String word) {
        System.out.println(word);
    }
}

执行结果:

通过代理对象执行方法:sayHello
before ...
around before ...
hello Hinson
around after ...
after ...
afterReturning ...

自调用方法,调用类是原对象,而aop中只有调用类是代理对象时,才能出发各种通知,spring 事务就是基于通知来实现的。所以spring非事务方法调用事务方法过程中,非事务方法的调用对象是代理对象,但是spring 事务机制只查看带有@Transcational注解的方法,所有非事务方法不会以事务方法执行;事务方法的调用对象是原对象,是不会触发通知(事务)的。

其他


本人也是在慢慢学习中,如有错误还请原谅、敬请指出,谢谢!

上一篇 下一篇

猜你喜欢

热点阅读