具有用于异步方法的Spring配置类,如下所示:

@Configuration
@EnableAsync(proxyTargetClass = true)
@EnableScheduling
public class AsyncConfiguration {

@Autowired
private ApplicationContext applicationContext;

@Bean
public ActivityMessageListener activityMessageListener() {
    return new ActivityMessageListener();
}
@Bean
public TaskExecutor defaultExecutor()
{
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
    threadPoolTaskExecutor.setCorePoolSize(10);
    threadPoolTaskExecutor.setMaxPoolSize(10);
    threadPoolTaskExecutor.setQueueCapacity(Integer.MAX_VALUE);

    return threadPoolTaskExecutor;
}


我所有的@Async方法都能按预期工作,但是如果我将AsyncConfigurer转换为AsyncConfiguration以捕获实现getAsyncUncaughtExceptionHandler()方法的异常,则不会代理我的bean,因此@Async方法不会在池执行器中运行。

这是无效的配置:

@Configuration
@EnableAsync(proxyTargetClass = true)
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {

@Autowired
private ApplicationContext applicationContext;

@Bean
public ActivityMessageListener activityMessageListener() {
    return new ActivityMessageListener();
}

@Override
public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
    threadPoolTaskExecutor.setCorePoolSize(10);
    threadPoolTaskExecutor.setMaxPoolSize(10);
    threadPoolTaskExecutor.setQueueCapacity(Integer.MAX_VALUE);

    return threadPoolTaskExecutor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler()        {
    return new SimpleAsyncUncaughtExceptionHandler();
}


可能会发生什么?

我们这样使用@Async

public class ActivityMessageListener extends BaseMessageListener {

public static final String PARAM_USER_ID                = "userId";
public static final String PARAM_COMPANY_ID             = "companyId";
public static final String PARAM_CREATE_DATE            = "createDate";
public static final String PARAM_CLASS_NAME             = "className";
public static final String PARAM_CLASS_PK               = "classPK";
public static final String PARAM_TYPE                   = "type";
public static final String PARAM_EXTRA_DATA             = "extraData";
public static final String PARAM_RECEIVED_USER_ID       = "receiverUserId";

@Override @Async(value = "defaultExecutor")
public Future<String> doReceive(Message message) throws Exception {

    String name = Thread.currentThread().getName();
    Map<String, Object> parameters  = message.getValues();
    Long userId                     = (Long)parameters.get(ActivityMessageListener.PARAM_USER_ID);
    Long companyId                  = (Long)parameters.get(ActivityMessageListener.PARAM_COMPANY_ID);
    Date createDate                 = (Date)parameters.get(ActivityMessageListener.PARAM_CREATE_DATE);
    String className                = (String)parameters.get(ActivityMessageListener.PARAM_CLASS_NAME);
    Long classPK                    = (Long)parameters.get(ActivityMessageListener.PARAM_CLASS_PK);
    Integer type                    = (Integer)parameters.get(ActivityMessageListener.PARAM_TYPE);
    String extraData                = (String)parameters.get(ActivityMessageListener.PARAM_EXTRA_DATA);
    Long receiverUserId             = (Long)parameters.get(ActivityMessageListener.PARAM_RECEIVED_USER_ID);
    ActivityLocalServiceUtil.addActivity(userId, companyId, createDate, className, classPK, type, extraData, receiverUserId);

    return new AsyncResult<String>(name);
}
}

最佳答案

编辑:我已经提交了bug report (SPR-14630)



我几乎要向Spring的issue tracker提交错误报告,但是当我准备一个用于再现该错误的小型应用程序时,我发现并解决了该问题。

首先,使用ThreadPoolTaskExecutor时,应在返回之前调用其initialize()方法:

@Override
public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setMaxPoolSize(1);
    executor.setCorePoolSize(1);
    executor.setThreadNamePrefix("CUSTOM-");

    // Initialize the executor
    executor.initialize();

    return executor;
}


同样由于某种原因,如果我在同一配置类中定义的@PostConstruct方法中使用bean,它将不会异步运行。原因是在执行@PostConstructgetAsyncExecutor()之前先执行getAsyncUncaughtExceptionHandler()方法:

AsyncBean.java

@Component
public class AsyncBean implements IAsyncBean {

    @Override
    @Async
    public void whoAmI() {
        final String message =
                String.format("My name is %s and I am running in %s", getClass().getSimpleName(), Thread.currentThread());

        System.out.println(message);
    }
}


AsyncDemoApp.java

@SpringBootApplication
@EnableAsync
public class AsyncDemoApp implements AsyncConfigurer {

    @Autowired
    private IAsyncBean asyncBean;

    public static void main(String[] args) {
        SpringApplication.run(AsyncDemoApp.class, args);
    }

    @Override
    public Executor getAsyncExecutor() {
        System.out.println("AsyncDemoApp.getAsyncExecutor");

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("CUSTOM-");
        executor.initialize();

        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        System.out.println("AsyncDemoApp.getAsyncUncaughtExceptionHandler");
        return (throwable, method, objects)
                -> throwable.printStackTrace();
    }

    @PostConstruct
    public void start() {
        System.out.println("AsyncDemoApp.start");
        asyncBean.whoAmI();
    }
}


输出:

AsyncDemoApp.start
My name is AsyncBean and I am running in Thread[main,5,main]
AsyncDemoApp.getAsyncExecutor
AsyncDemoApp.getAsyncUncaughtExceptionHandler


但是,如果在准备使用应用程序上下文之后使用Bean,则它应该都能按预期工作:

@SpringBootApplication
@EnableAsync
public class AsyncDemoApp implements AsyncConfigurer {

    public static void main(String[] args) {
        final ConfigurableApplicationContext context = SpringApplication.run(AsyncDemoApp.class, args);

        final IAsyncBean asyncBean = context.getBean(IAsyncBean.class);

        asyncBean.whoAmI();
    }

    @Override
    public Executor getAsyncExecutor() {
        System.out.println("AsyncDemoApp.getAsyncExecutor");

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("CUSTOM-");
        executor.initialize();

        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        System.out.println("AsyncDemoApp.getAsyncUncaughtExceptionHandler");
        return (throwable, method, objects)
                -> throwable.printStackTrace();
    }
}


另一个怪异的行为是,如果您在同一配置类中自动装配异步bean,则在配置自定义异步执行程序之前会发生自动装配,因此bean不会异步运行,而是在主线程中运行。可以通过在@PostConstruct中添加AsyncBean并使用CommandLineRunner来运行应用程序来验证这一点(我个人认为这是一个错误。至少可以这样说,这非常令人惊讶):

AsyncBean@PostConstruct

@Component
public class AsyncBean implements IAsyncBean {

    @Override
    @Async
    public void whoAmI() {
        final String message =
                String.format("My name is %s and I am running in %s", getClass().getSimpleName(), Thread.currentThread());

        System.out.println(message);
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("AsyncBean is constructed");
    }
}


AsyncDemoApp实施CommandLineRunner

@SpringBootApplication
@EnableAsync
public class AsyncDemoApp implements AsyncConfigurer, CommandLineRunner {

    @Autowired
    private IAsyncBean asyncBean;

    public static void main(String[] args) {
        SpringApplication.run(AsyncDemoApp.class, args);
    }

    @Override
    public Executor getAsyncExecutor() {
        System.out.println("AsyncDemoApp.getAsyncExecutor");

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("CUSTOM-");
        executor.initialize();

        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        System.out.println("AsyncDemoApp.getAsyncUncaughtExceptionHandler");
        return (throwable, method, objects)
                -> throwable.printStackTrace();
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("AsyncDemoApp.run");
        asyncBean.whoAmI();
    }
}


输出:

AsyncBean is constructed
AsyncDemoApp.getAsyncExecutor
AsyncDemoApp.getAsyncUncaughtExceptionHandler
AsyncDemoApp.run
My name is AsyncBean and I am running in Thread[main,5,main]




还有一件事! :)如果您使用ThreadPoolTaskExecutor,则根据您的要求,可能需要将其daemon属性设置为true,否则您的应用程序将永远运行(对于Web / Worker应用程序来说不是大问题)。这是setDaemon(boolean)的JavaDoc所说的:


设置该工厂是否应该创建守护线程
只要应用程序本身正在运行,它就会执行。默认为
“ false”:具体工厂通常支持显式取消。
因此,如果应用程序关闭,Runnables将默认完成
他们的执行。为紧急关闭线程指定“ true”
仍在应用程序运行时主动执行Runnable
本身会关闭。

09-27 03:26
查看更多