美文网首页Spring AOP/IOC深入与问题
从Transactional与Async同时使用的错误到动态代理

从Transactional与Async同时使用的错误到动态代理

作者: minute_5 | 来源:发表于2019-07-18 23:09 被阅读0次

    从Transactional与Async注解说起

    当需要对某个方法开启异步线程,同时开启事务时,使用Spring的新手大多会犯一个错误,将@Transactional@Async联合使用。
    这样使用的一个直接后果是明明加了@Transactional注解为什么事务没有成功执行。

    原因其实很简单:

    1. Spring 实现这两个注解的方式都是通过AOP
    2. 在实现时,Async注解强制覆盖AOP的order为最小值(它认为Async应该是执行的AOP链中的第一个advisor -- https://jira.spring.io/browse/SPR-7147),而且order不可配置。
    3. 但是在实现Transactional注解时,却没有覆盖order,这意味着它仍然为默认的Integer.MAX_VALUE,order可配置。所以异步切面会先于事务切面执行。
    4. 假设@Transactional能先于Async切面执行,但由于spring事务管理依赖的是ThreadLocal,所以在开启的异步线程里面感知不到事务,说细点就是在Spring开启事务之后,会设置一个连接到当前线程,但这个时候又开启了一个新线程,执行实际的SQL代码时,通过ThreadLocal获取不到连接就会开启新连接,也不会设置autoCommit,所以这个函数整体将没有事务。

    动态代理

    动态代理就是通过反射机制或者字节码操纵等技术动态地获取要被代理对象的类型,从而获取相关特性进行代理。AOP就是动态代理的一种具体形式。

    因为在开发过程中就遇到过,所以拿上面的例子做个开篇,在当时为了解决这个错误,理解其中原理,从而去深入探究了一番。

    Spring实现的动态代理有两种方式,JDK与Cglib。

    这里不去探究深层次源码,而是让大家能在不同的开发场景中灵活使用动态代理这门重要的技术。

    • 这里有一点需要注意:
      1. JDK代理的实现前提是被代理的类必须实现接口,因为我们代理的时候,正是根据继承自接口然后构造的代理类。而Cglib则没这个要求。
      2. cglib是针对类来实现代理的,其原理是对指定的要被代理类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

    假设有一个接口 TestP 与其实现类 TestPImpl

    JDK动态代理实现

    /**
      *JDK代理的实现前提是被代理的类,必须实现了接口,因为我们代理的时候,
      *正是根据继承自接口然后构造的代理类。
    */
    public class JdkProxy implements InvocationHandler{
     
        private Object target;
        /** 
         * 绑定需要被代理的对象
         * 返回代理类 
         */  
        public Object bind(Object target) {
            this.target = target;  
            //取得代理对象  
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                    target.getClass().getInterfaces(), this);  
        }  
      
        @Override  
        public Object invoke(Object proxy, Method method, Object[] args)  
                throws Throwable {  
            Object result=null;  
            System.out.println("代理开始");  
            //你的方法
            result=method.invoke(target, args);  
            System.out.println("代理结束");  
            return result;  
        }  
    }
    
    public class TestJdkProxy {
     
        public static void main(String[] args) {
            JdkProxy jp = new JdkProxy();
            TestP tp = (TestP)jp .bind(new TestPImpl());
            tp.someMethod();
        }
        
    }
    

    Cglib动态代理实现

    public class CglibProxy implements MethodInterceptor {
     
        private Object target;
     
        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            // 设置回调方法  
            enhancer.setCallback(this);
            // 创建代理对象  
            return enhancer.create();
        }
     
        @Override
        // 回调方法  
        public Object intercept(Object obj, Method method, Object[] args,
                MethodProxy proxy) throws Throwable {
            System.out.println("代理开始");
            //你的方法
            proxy.invokeSuper(obj, args);
            System.out.println("代理开始");
            return null;
     
        }
     
    }
    
    public class TestCglibProxy {
     
        public static void main(String[] args) {
            
            CglibProxy cp= new CglibProxy();
            TestP  tp = (TestP)cp.getInstance(new TestPImpl()); 
            tp.someMethod();
        }
        
    }
    

    相关文章

      网友评论

        本文标题:从Transactional与Async同时使用的错误到动态代理

        本文链接:https://www.haomeiwen.com/subject/jtcbyqtx.html