zoukankan      html  css  js  c++  java
  • Spring中@Async注解执行异步任务 & @Async Could not find unique TaskExecutor bean; NoUniqueBeanDefinitionException;

    Spring中@Async注解执行异步任务

    https://segmentfault.com/a/1190000015190901

    引言

    在业务处理中,有些业务使用异步的方式更为合理。比如在某个业务逻辑中,把一些数据存入到redis缓存中,缓存只是一个辅助的功能,成功或者失败对主业务并不会产生根本影响,这个过程可以通过异步的方法去进行。

    Spring中通过在方法上设置@Async注解,可使得方法被异步调用。也就是说该方法会在调用时立即返回,而这个方法的实际执行交给Spring的TaskExecutor去完成。

    代码示例

    项目是一个普通的Spring的项目,Spring的配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:task="http://www.springframework.org/schema/task"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/task
           http://www.springframework.org/schema/task/spring-task.xsd">
    
        <!-- 包扫描 -->
        <context:component-scan base-package="com.lzumetal.ssm"/>
    
        <!-- 执行异步任务的线程池TaskExecutor -->
        <task:executor id="myexecutor" pool-size="5"  />
        <task:annotation-driven executor="myexecutor"/>
    
    </beans>

    两个Service类:

    package com.lzumetal.ssm.anotation.service;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    
    /**
     * 业务Service
     */
    @Service
    public class BusinessService {
    
        private static final Logger log = LoggerFactory.getLogger(BusinessService.class);
    
        @Autowired
        private CacheService cacheService;
    
    
        public void doBusiness() {
            log.error("start to deal with our business");
            cacheService.cacheData();
            log.error("comlete service operation");
        }
    
        /**
         * 获取异步方法执行的返回值
         */
        public void doBusinessWithAsyncReturn() throws ExecutionException, InterruptedException {
            log.error("start to deal with our business");
            Future<String> future = cacheService.cacheDataWithReturn();
            log.error(future.get()); //future.get()方法是会阻塞的
            log.error("comlete service operation");
        }
    }
    package com.lzumetal.ssm.anotation.service;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.AsyncResult;
    import org.springframework.stereotype.Service;
    
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 缓存服务
     */
    @Service
    public class CacheService {
    
        private static final Logger log = LoggerFactory.getLogger(CacheService.class);
    
    
        @Async(value = "myexecutor")    //指定执行任务的TaskExecutor
        public void cacheData() {
            try {
                TimeUnit.SECONDS.sleep(3L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.error("success store the result to cache");
        }
    
    
        @Async
        public Future<String> cacheDataWithReturn() {
            try {
                TimeUnit.SECONDS.sleep(3L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.error("success store the result to cache");
            //返回的结果需要通过AsyncResult这个类包装
            return new AsyncResult<>("Async operation success");
        }
    }
    

    测试类:

    package com.lzumetal.ssm.anotation.test;
    
    import com.lzumetal.ssm.anotation.service.BusinessService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import java.util.concurrent.TimeUnit;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:spring-context.xml"})
    public class MainTest {
    
    
        @Autowired
        private BusinessService businessService;
    
    
        @Test
        public void test() throws InterruptedException {
            businessService.doBusiness();
            //不让主线程过早结束,否则控制台看不到异步方法中的输出内容
            TimeUnit.SECONDS.sleep(5L);     
        }
    
        @Test
        public void testAsyncReturn() throws Exception {
            businessService.doBusinessWithAsyncReturn();
            TimeUnit.SECONDS.sleep(5L);
        }
    
    }

    执行test()方法的结果:

    22:20:33,207  INFO main support.DefaultTestContextBootstrapper:260 - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
    22:20:33,226  INFO main support.DefaultTestContextBootstrapper:209 - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
    22:20:33,227  INFO main support.DefaultTestContextBootstrapper:187 - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@100fc185, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@643b1d11, org.springframework.test.context.support.DirtiesContextTestExecutionListener@2ef5e5e3, org.springframework.test.context.transaction.TransactionalTestExecutionListener@36d4b5c, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@6d00a15d]22:20:33,324  INFO main xml.XmlBeanDefinitionReader:317 - Loading XML bean definitions from class path resource [spring-context.xml]
    22:20:33,585  INFO main support.GenericApplicationContext:583 - Refreshing org.springframework.context.support.GenericApplicationContext@4f7d0008: startup date [Wed May 30 22:20:33 CST 2018]; root of context hierarchy
    22:20:33,763  INFO main concurrent.ThreadPoolTaskExecutor:165 - Initializing ExecutorService 
    22:20:33,766  INFO main support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:325 - Bean 'myexecutor' of type [org.springframework.scheduling.config.TaskExecutorFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    22:20:33,767  INFO main support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:325 - Bean 'myexecutor' of type [org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    22:20:34,107 ERROR main service.BusinessService:24 - start to deal with our business
    22:20:34,113 ERROR main service.BusinessService:26 - comlete service operation
    22:20:37,166 ERROR myexecutor-1 service.CacheService:28 - success store the result to cache
    22:20:39,117  INFO Thread-0 support.GenericApplicationContext:984 - Closing org.springframework.context.support.GenericApplicationContext@4f7d0008: startup date [Wed May 30 22:20:33 CST 2018]; root of context hierarchy
    22:20:39,118  INFO Thread-0 concurrent.ThreadPoolTaskExecutor:203 - Shutting down ExecutorService

    执行testAsyncReturn()方法的结果:

    21:38:16,908  INFO main support.DefaultTestContextBootstrapper:260 - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
    21:38:16,926  INFO main support.DefaultTestContextBootstrapper:209 - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
    21:38:16,927  INFO main support.DefaultTestContextBootstrapper:187 - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@100fc185, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@643b1d11, org.springframework.test.context.support.DirtiesContextTestExecutionListener@2ef5e5e3, org.springframework.test.context.transaction.TransactionalTestExecutionListener@36d4b5c, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@6d00a15d]21:38:17,025  INFO main xml.XmlBeanDefinitionReader:317 - Loading XML bean definitions from class path resource [spring-context.xml]
    21:38:17,263  INFO main support.GenericApplicationContext:583 - Refreshing org.springframework.context.support.GenericApplicationContext@4f7d0008: startup date [Wed May 30 21:38:17 CST 2018]; root of context hierarchy
    21:38:17,405  INFO main concurrent.ThreadPoolTaskExecutor:165 - Initializing ExecutorService 
    21:38:17,407  INFO main support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:325 - Bean 'myexecutor' of type [org.springframework.scheduling.config.TaskExecutorFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    21:38:17,407  INFO main support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:325 - Bean 'myexecutor' of type [org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    21:38:17,692 ERROR main service.BusinessService:35 - start to deal with our business
    21:38:20,833 ERROR myexecutor-1 service.CacheService:39 - success store the result to cache
    21:38:20,834 ERROR main service.BusinessService:37 - Async operation success
    21:38:20,835 ERROR main service.BusinessService:38 - comlete service operation
    21:38:25,838  INFO Thread-0 support.GenericApplicationContext:984 - Closing org.springframework.context.support.GenericApplicationContext@4f7d0008: startup date [Wed May 30 21:38:17 CST 2018]; root of context hierarchy
    21:38:25,839  INFO Thread-0 concurrent.ThreadPoolTaskExecutor:203 - Shutting down ExecutorService

    @Async的使用注意点

    1. 返回值:不要返回值直接void;需要返回值用AsyncResult或者CompletableFuture
    2. 可自定义执行器并指定例如:@Async("otherExecutor")
    3. @Async必须不同类间调用: A类—>B类.C方法()(@Async注释在B类/方法中),如果在同一个类中调用,会变同步执行,例如:A类.B()—>A类.@Async C()。
    4. @Async也可以加到类,表示这个类的所有方法都是异步执行,并且方法上的注解会覆盖类上的注解。但一般不这么用!
    ___________________________________________________________________________________________________________________________________________________________
     
     

    @Async Could not find unique TaskExecutor bean; NoUniqueBeanDefinitionException;

    异步任务,项目启动或接口调用,控制台报错:Could not find unique TaskExecutor bean;

    或者报错:org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.core.task.TaskExecutor' available: expected single matching bean but found 2: applicationTaskExecutor,taskScheduler

    原因为:@Async所注解的方法存在相同的方法名,或者同一个异步任务被多个任务同时调用

    解决方法:

    1、将@Async所注解的方法名修改为不同的方法名

    2、增加异步任务配置类:

    import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.AsyncConfigurer;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
     
    import java.util.concurrent.Executor;
     
    /**
     * @description 异步任务配置类,为防止异步任务如下错误:
     * No qualifying bean of type 'org.springframework.core.task.TaskExecutor' available:
     * expected single matching bean but found 2: applicationTaskExecutor,taskScheduler
     * @author: chenping
     * @create: 2020-01-17
     **/
    @Configuration
    @ComponentScan("cn.com.*.*.*.service")//异步任务所在的包
    @EnableAsync //开启异步任务支持
    public class TaskExecutorConfig implements AsyncConfigurer {
     
        /**
         * @description:  实现AsyncConfigurer接口并重写getAsyncExecutor方法,
         *  并返回一个ThreadPoolTaskExecutor,这样我们就获得了一个基于线程池TaskExecutor
         * @param:
         * @return: java.util.concurrent.Executor
         * @author: chenping
         * @date: 2020/1/17
         **/
        @Override
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            taskExecutor.setCorePoolSize(10);
            taskExecutor.setMaxPoolSize(80);
            taskExecutor.setQueueCapacity(100);
            taskExecutor.initialize();
            return taskExecutor;
        }
     
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return null;
        }
    }
     
     
  • 相关阅读:
    Java并发编程:如何创建线程?
    Java并发编程:volatile关键字解析
    Java并发编程:深入剖析ThreadLocal
    Java并发编程:同步容器
    Java ConcurrentModificationException异常原因和解决方法
    Java并发编程:并发容器之ConcurrentHashMap
    Java并发编程:并发容器之CopyOnWriteArrayList
    Java并发编程:Callable、Future和FutureTask
    rest-framework频率组件
    rest-framework的权限组件
  • 原文地址:https://www.cnblogs.com/kelelipeng/p/13439130.html
Copyright © 2011-2022 走看看