@Async

来自ling
跳转至: 导航搜索

https://segmentfault.com/a/1190000008981884

https://baijiahao.baidu.com/s?id=1700394969228178875&wfr=spider&for=pc

失效原因

  • 没有在@SpringBootApplication启动类当中添加注解@EnableAsync注解。
  • 异步方法使用注解@Async的返回值只能为void或者Future。
  • 没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。
  • 同一个类中的方法调用任然需要走proxy

PROPAGATION_NOT_SUPPORTED不生效的原因 同一个类中的方法调用任然需要走proxy 直接调用是不会有效果的

第二点和第三点容易犯......


解决方法: 这里具体说一下第三种情况的解决方法。

  • 注解的方法必须是public方法。
  • 方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的。
  • 如果需要从类的内部调用,需要先获取其代理类,下面上代码
@Service
public class XxxService{
  public void methodA(){
    ...
    XxxService xxxServiceProxy = SpringUtil.getBean(XxxService.class);
    xxxServiceProxy.methodB();
    ...
  }
 
  @Async
  public void methodB() {
    ...
  }


  • @Transactional 加于private方法, 无效
  • @Transactional 加于未加入接口的public方法, 再通过普通接口方法调用, 无效
  • @Transactional 加于接口方法, 无论下面调用的是private或public方法, 都有效
  • @Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效
  • @Transactional 加于接口方法后, 被本类普通接口方法通过接口调用, 有效
  • @Transactional 加于接口方法后, 被它类的接口方法调用, 有效
  • @Transactional 加于接口方法后, 被它类的私有方法调用后, 有效
PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。 
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。 
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。 
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。 
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。 


xml方式启用

component-scan.xml


<?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:p="http://www.springframework.org/schema/p"
	xmlns:security="http://www.springframework.org/schema/security"
	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-2.0.xsd 
	http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd  
	http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
	default-autowire="byName" default-lazy-init="false">

	<!-- <context:component-scan base-package="com.ling2.core.security.login" 
		/> <context:component-scan base-package="com.ling2.springbatch" /> -->
	<context:annotation-config />
	<context:component-scan base-package="com.deloitte.*" />
	<!--使用spring的异步@Async简单定义方式 -->
	<task:annotation-driven executor="asyncExecutor" />
	<task:executor id="asyncExecutor" pool-size="100-10000"
		queue-capacity="10" />

</beans>

@Async中的事务No Session found for current thread

注意ling框架中,代码必须放在server下才有事务否则加了@Transactional也没有用 参考MailSendService

http://blog.csdn.net/blueheart20/article/details/44648667

  • @Async调用中的事务处理机制

在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。

那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional.

例如:

  • 方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。
  • 方法B,使用了@Async来标注, B中调用了C、D,C/D分别使用@Transactional做了标注,则可实现事务控制的目的。


参考代码

IitDeuctionClaimAsyncServiceImpl

    /**
     * 一定要批量读取结果, 否则不能达到异步的效果!!
     * 异步方法和调用类不要在同一个类中
     * 注解扫描时,要注意过滤,避免重复实例化,因为存在覆盖问题,@Async就失效了
     * @param params
     * @return
     * @throws Exception
     */
    public PageResult<DeductionClaimDetailHVo> queryValidateIitDeductionClaimHVoAsync(Map params) throws Exception {
        PageResult<IitDeductionClaimH> claimHs = findIitDeductionClaimH(params, true);
        List<Future<DeductionClaimDetailHVo>> futures = new ArrayList<>();
        long start = System.currentTimeMillis();
        for (IitDeductionClaimH claimH : claimHs.getList()) {
            Future<DeductionClaimDetailHVo> future = iitDeductionClaimAsyncService.convertIitDeductionClaimHToDeductionClaimDetailHVoAsync(claimH);
            futures.add(future);
        }
        List<DeductionClaimDetailHVo> hVos = new ArrayList<>();
        for (Future future : futures) {
            DeductionClaimDetailHVo vo = (DeductionClaimDetailHVo) future.get();
            hVos.add(vo);
        }
        logger.info("queryValidateIitDeductionClaimHVoAsync合计消耗时间:"+String.format("任务执行成功,耗时{%s}毫秒", System.currentTimeMillis() - start));

        PageResult<DeductionClaimDetailHVo> results = new PageResult<>();
        results.setList(hVos);
        results.setPageIndex(claimHs.getPageIndex());
        results.setPageSize(claimHs.getPageSize());
        results.setPageIndexString(claimHs.getPageIndexString());
        results.setTotal(claimHs.getTotal());
        return results;
    }


    private void buildDetail_lvo(IitDeductionClaimH iitDeductionClaimH, DeductionClaimDetailHVo result,
                                 boolean withAttachment) throws Exception {
        long prestart = System.currentTimeMillis();

..............................................................................


        logger.info("buildDetail_lvo 前序任务消耗时间:"+String.format("任务执行成功,耗时{%s}毫秒", System.currentTimeMillis() - prestart));

        List<Future<DeductionClaimDetailHVo>> futures = new ArrayList<>();
        long endstart = System.currentTimeMillis();
        Future<DeductionClaimDetailHVo> futureMonthAmount = iitDeductionClaimAsyncService.processMonthAmount(result);
        futures.add(futureMonthAmount);
        Future<DeductionClaimDetailHVo> futureYearSum = iitDeductionClaimAsyncService.processYearSum(result);
        futures.add(futureYearSum);
        Future<DeductionClaimDetailHVo> futurePersionInfo = iitDeductionClaimAsyncService.processPersionInfo(iitDeductionClaimH,result);
        futures.add(futurePersionInfo);
        for (Future future : futures) {
            future.get();
        }

        logger.info("buildDetail_lvo 后续任务消耗时间:"+String.format("任务执行成功,耗时{%s}毫秒", System.currentTimeMillis() - endstart));
    }

private Future<DxlUserVO> buildDxlUserVO(RemoteUserService remoteUserService, SysUserVO userVO) {
		.....
		return new AsyncResult<>(dxlUserVO);
	}

@EnableAsync

让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync