Spring学习:在本类中调用带@Async注解的方法与Spring的AOP动态代理

在类中,调用本类的含有@Async注解的异步方法,不能直接调用(带有@Transactional的也是)

public class AsyncService{

 public void syncHandleData() {
    	String name = Thread.currentThread().getName();
    	System.out.println("-----------线程1------------------"+name);
    	AsyncService currentProxy = (AsyncService) AopContext.currentProxy();
    	currentProxy.syncHandleData1();
    }
    
    @Async("asyncExecutor")
    public CollectionBatch syncHandleData1() {
    	String name = Thread.currentThread().getName();
    	System.out.println("-----------线程2------------------"+name);
        System.out.println("开始异步处理");
     }
}

因为@Async异步是通过spring的Aop动态代理, 若在类中直接调用,相当于this.syncHandleData1的方式,即是通过AsyncService对象直接调用的syncHandleData1方法,而实际上@Async注解异步的实现是通过AsyncService的代理对象调用实现的,所以这样直接调用就让异步无效了。解决的方法有三种:

1.把这个异步方法抽出来放在一个新类里。调用的时候把新类对象通过依赖注入的注解方式放进来直接调用就可以了。因为通过注解注入,那么这个类对象就肯定是由Spring管理的了,Spring容器会对这个类进行一系列的渲染,包括需要用的组件。

2.在AsyncService类中通过Spring上下文获取它的对象(写一个SpringUtil工具类实现ApplicationContextAware ,具体实现可看我另一篇博客),即可以正常调用异步方法。所以该方法获取的对象实际上是AsyncService的代理对象

3.通过AopContext动态代理获取对象,即上面代码所示,然后在启动类中加上下面这个注解,开启cglib代理。(因为默认是jdk代理)。此刻可以看到打印出来的是两个不同线程。

@EnableAspectJAutoProxy(exposeProxy=true)

Spring容器对对象的创建不单单是像我们new一个新对象那样简单,而是在初始化时对它进行了一系列的渲染。比如Spring在初始化对象时,扫描到这个Bean的@Async注解,就会给它装配异步组件,扫描到它的@Transactional注解,就会给它装配事务的功能,最后我们获取的就是这个bean的代理对象,这就是Spring的动态代理。你需要什么样的对象,就动态的给你创建一个什么样的代理对象供使用。(对于@Transactional注解,放在类上,或在两个方法上都加上该注解,事务也是有效的)

所以如果你用了Spring的方便的注解,那么在你需要调用这个注解方法时,一定要用Spring管理的对象去调用它,因为没有被Spring容器管理的对象是不具备这些注解的组件功能的。

你可能感兴趣的:(Spring,小记)