@Async注解的使用

@Async属于一部注解
@Async放在方法上标识该方法为异步方法

首先普及下同步与异步的区别

同步(synchronized):同步是指一个进程在执行某个请求的时候,如果该请求需要一段时间才能返回信息,那么这个进程会一直等待下去,直到收到返回信息才继续执行下去。

异步(Asynchronous):异步是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有信息返回的时候会通知进程进行处理,这样就可以提高执行的效率了,即异步是我们发出的一个请求,该请求会在后台自动发出并获取数据,然后对数据进行处理,在此过程中,我们可以继续做其他操作,不管它怎么发出请求,不关心它怎么处理数据。
解读

 1,在方法上使用该@Async注解,申明该方法是一个异步任务;

 2,在类上面使用该@Async注解,申明该类中的所有方法都是异步任务;

 3,使用此注解的方法的类对象,必须是spring管理下的bean对象; 

 4,要想使用异步任务,需要在主类上开启异步配置,即,配置上@EnableAsync注解;

1.使用:
springboot中的启动类中添加注解**@EnableAsync**来开启异步调用;
在需要异步执行的方法上添加**@Async(“taskExecutor”)**注解进行标注;
类或者方法中都可使用@Async注解,(类上标有该注解表示类中方法都是异步方法);
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {

    /**
     * A qualifier value for the specified asynchronous operation(s).
     * <p>May be used to determine the target executor to be used when executing this
     * method, matching the qualifier value (or the bean name) of a specific
     * {@link java.util.concurrent.Executor Executor} or
     * {@link org.springframework.core.task.TaskExecutor TaskExecutor}
     * bean definition.
     * <p>When specified on a class level {@code @Async} annotation, indicates that the
     * given executor should be used for all methods within the class. Method level use
     * of {@code Async#value} always overrides any value set at the class level.
     * @since 3.1.2
     */
    String value() default "";

}

一般会添加一个线程池的配置,不影响主线程,异步方法交给单独的线程完成。

配置一个自定义的执行器:

@Configuration
public class AsyncConfig {

    private static final int MAX_POOL_SIZE = 50;

    private static final int CORE_POOL_SIZE = 20;

    @Bean("taskExecutor")
    public AsyncTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        taskExecutor.setThreadNamePrefix("async-task-thread-pool");
        taskExecutor.initialize();
        return taskExecutor;
    }
}

2.带有返回值的Future
需要执行异步调用的方法示例(带有返回值的Future,需要用到AsyncResult)

@Service
public class DeviceProcessServiceImpl implements DeviceProcessService {

    @Autowired
    private DeviceRpcService deviceRpcService;

    @Async("taskExecutor")
    @Override
    public Future<Map<Long, List<ProcessDTO>>> queryDeviceProcessAbilities(List<BindDeviceDO> bindDevices) {
        if (CollectionUtils.isEmpty(bindDevices)) {
            return new AsyncResult<>(Maps.newHashMap());
        }
        List<Long> deviceIds = bindDevices.stream().map(BindDeviceDO::getDeviceId).collect(Collectors.toList());

        List<DeviceInstanceWithProcessResp> devices = deviceRpcService.getDeviceProcessAbility(deviceIds);
        Map<Long, List<ProcessDTO>> deviceAbilityMap = Maps.newHashMap();
        ...
        return new AsyncResult<>(deviceAbilityMap);
    }
}

对加了@async注解方法有返回值的调用

private ProcessAbilityData asyncCollectProcessAbilities(List<BindDeviceDO> bindDevices, List<BindStaffDO> bindStaffs, String dccId) {
        // 返回值
        Future<Map<Long, List<ProcessDTO>>> deviceProcessFutureResult = deviceProcessService.queryDeviceProcessAbilities(bindDevices);
        Future<Map<String, List<ProcessDTO>>> staffAbilityFutureResult = staffProcessService.queryStaffProcessAbilities(bindStaffs, dccId);
        Map<Long, List<ProcessDTO>> deviceAbilityMap;
        Map<String, List<ProcessDTO>> staffAbilityMap;
        try {
            // 此处获得返回值
            deviceAbilityMap = deviceProcessFutureResult.get();
            staffAbilityMap = staffAbilityFutureResult.get();
        } catch (InterruptedException | ExecutionException e) {
            // 捕获异常...
        }
        return new ProcessAbilityData(deviceAbilityMap, staffAbilityMap);
    }

3.异常处理
当方法返回值是Future的时候,异常捕获是没问题的 - Future.get()方法会抛出异常。

但是,如果返回类型是Void,那么异常在当前线程就捕获不到。因此,我们需要添加额外的配置来处理异常。

我们将通过实现AsyncUncaughtExceptionHandler接口创建一个定制的async异常处理程序。handleUncaughtException()方法在存在任何未捕获的异步异常时调用:

public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {

        System.out.println("Exception message - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
    }

}

像配置执行器一样配置异常处理程序:

@Configuration
public class SpringAsyncConfiguration implements AsyncConfigurer {

    /**
     * 覆盖默认的执行器
     *
     * @return
     */
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(16);
        executor.setQueueCapacity(64);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setThreadNamePrefix("SpringAsyncThread-");

        return executor;
    }

    /**
     * 覆盖此方法来配置异步异常处理程序
     *
     * @return
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SpringAsyncExceptionHandler();
    }

}


如果是微服务,可以在父项目中(start项目)中配置统一异常处理,在各个子项目中实现接口,来处理异常。

父项目:

@Configuration
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return this.new AsyncExceptionConfig();
    }

    private List<CustomAsyncExceptionHandler> exceptionHandlers = new ArrayList<>();

    @Autowired(required = false)
    public void setExceptionHandlers(List<CustomAsyncExceptionHandler> exceptionHandlers) {
        this.exceptionHandlers = exceptionHandlers;
    }

    /**
     * 统一错误处理类
     */
    class AsyncExceptionConfig implements AsyncUncaughtExceptionHandler {

        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            log.error("run method with annotation @Async exception! Method name - {}; Parameter value - {}; Exception message - {}", method.getName(), objects, throwable.getMessage());
            for (CustomAsyncExceptionHandler handler : exceptionHandlers) {
                try {
                    if (handler.ignore(throwable, method, objects)) {
                        continue;
                    }
                    handler.handler(throwable, method, objects);
                } catch (Exception e) {
                    log.error("run CustomAsyncExceptionHandler exception: {}", e.getMessage());
                }
            }
        }

    }

}


public interface CustomAsyncExceptionHandler {
    /**
     * 是否忽略此次异常处理
     *
     * @param throwable 错误信息
     * @param method    错误方法
     * @param objects   错误方法参数
     * @return true忽略;false不忽略
     */
    default boolean ignore(Throwable throwable, Method method, Object... objects) {
        return false;
    }

    /**
     * 错误处理方法
     *
     * @param throwable 错误信息
     * @param method    错误方法
     * @param objects   错误方法参数
     */
    default void handler(Throwable throwable, Method method, Object... objects) {
        //override and do something.
    }
}


各个子项目只需要实现接口CustomAsyncExceptionHandler既可以实现异常处理。

4.@EnableAsync启动报错问题
***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'xxxService' could not be injected as a 'com.xxx.xxxService' because it is a JDK dynamic proxy that implements:
    com.xxx.Xxx


Action:

Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.

错误的意思是:xxxService 类采用的是 JDK动态代理的方式实现的,需要我们考虑使用基于CGLib方式实现动态代理。

解决:

在@EnableAsync 或者 @EnableCaching上设置 proxyTargetClass=true,即:
@EnableAsync(proxyTargetClass = true)

@EnableAspectJAutoProxy单纯设置基于类的动态代理,其他的都是@EnableAsync(开启异步),@EnableCaching(开启缓存),@EnableTransactionManagement(开启事务管理)等注解顺便开启基于类的动态代理。
@EnableAspectJAutoProxy(proxyTargetClass = true)


5.@Async失效问题
启动类是否开启异步服务;

在定义异步方法的同一个类中,调用带有@Async注解方法,该方法则无法异步执行;

没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器管理。

注解的方法必须是public方法,不能是static;
 

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值