@Async 注解失效解析

1.问题描述:使用 @Async 注解导致访问  /addOrder  接口报 404 错误。代码如下
//接口代码
public interface OrderService {

    /**
     * 新增方法
     *
     * @return
     */
    String addOrder();


    /**
     * 日志方法
     *
     * @return
     */
    void addOrderLog();
}
//实现类代码
@Slf4j
@RestController
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderServiceManager orderServiceManager;

    @RequestMapping("/addOrder")
    @Override
    public String addOrder() {
        log.info(">>>流程1");
        // 代理对象.addOrderLog()
        this.addOrderLog();
        log.info(">>>流程3");
        return "addOrder";
    }

    @Async
    @Override
    public void addOrderLog() {
        log.info(">>>流程2");
    }
}
//启动类代码
@EnableAsync
@SpringBootApplication
public class CustomAnnotationApp {
    public static void main(String[] args) {
        SpringApplication.run(CustomAnnotationApp.class, args);
    }
}

访问接口如下图:

 2.问题描述:使用 @Async 注解导致访问 /addOrder 接口导致 异步注解不起作用。启动类代码同上。

//控制层代码
@Slf4j
@RestController
public class OrderServiceImpl {

    @Autowired
    private OrderServiceManager orderServiceManager;

    @RequestMapping("/addOrder")
    public String addOrder() {
        log.info(">>>流程1");
        // 代理对象.addOrderLog()
        this.addOrderLog();
        log.info(">>>流程3");
        return "addOrder";
    }

    @Async
    public void addOrderLog() {
        log.info(">>>流程2");
    }
}

页面访问接口,控制台打印日志如下:

 由上图可知@Async 注解未生效。


问题1:我们控制层实现了接口且使用了异步注解@Async,代码底层会走 JDK 动态代理,导致 OrderServiceImpl 控制类没有注入到 SpringMVC 容器中。
原理:是因为 JDK 动态代理技术是基于接口实现的,而接口中没有@RestController注解,所以导致代理对象无法注册到SpringMVC容器中。
OrderServiceImpl 做为代理类实现 OrderService 接口,代理类(OrderServiceImpl)发现 OrderService 接口上面没有 RestController 注解,所以导致了代理类(OrderServiceImpl)不会注入到SpringMVC容器中。
spring 源码如下:
    /**  初始化 bean 对象
     * Detects handler methods at initialization.
     * @see #initHandlerMethods
     */
    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }

    /**
     * Scan beans in the ApplicationContext, detect and register handler methods.
     * @see #getCandidateBeanNames()  获取到所有的 bean 对象
     * @see #processCandidateBean
     * @see #handlerMethodsInitialized
     */
    protected void initHandlerMethods() {
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }
    /**
     * Determine the type of the specified candidate bean and call
     * {@link #detectHandlerMethods} if identified as a handler type.
     * <p>This implementation avoids bean creation through checking
     * {@link org.springframework.beans.factory.BeanFactory#getType}
     * and calling {@link #detectHandlerMethods} with the bean name.
     * @param beanName the name of the candidate bean
     * @since 5.1
     * @see #isHandler
     * @see #detectHandlerMethods
     */
    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isTraceEnabled()) {
                logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
            }
        }
        if (beanType != null && isHandler(beanType)) {
            detectHandlerMethods(beanName);
        }
    }
    /**
     * {@inheritDoc} 判断类上面是否有 Controller、RequestMapping 注解。
     * <p>Expects a handler to have either a type-level @{@link Controller}
     * annotation or a type-level @{@link RequestMapping} annotation.
     */
    @Override
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
问题2:我们控制层使用了异步注解@Async 但是没有实现接口的时候,代码底层走 Cglib 动态代理,OrderServiceImpl 控制类注入到 SpringMVC 容器中。
原理:Cglib 动态代理生成的代理对象是采用继承目标对象(OrderServiceImpl)的模式生成代理类的,而目标对象(OrderServiceImpl)中有@RestController 注解,所以注解会被继承过来,这样Cglib 生成的代理对象就可以注入到SpringMVC 容器中。
失效原因:因为@Async注解方法与控制类在同一个类中导致没有走代理类,所以@Async 注解失效。
解决方案:在新建一个类,存放需要走异步的方法。如下所示:
//manager 层
@Slf4j
@Component
public class OrderServiceManager {

    @Async
    public void addOrderLog() {
        log.info(">>>流程2");
    }
}
@Slf4j
@RestController
public class OrderServiceImpl {

    @Autowired
    private OrderServiceManager orderServiceManager;

    @RequestMapping("/addOrder")
    public String addOrder() {
        log.info(">>>流程1");
        // 代理对象.addOrderLog()
        orderServiceManager.addOrderLog();
        log.info(">>>流程3");
        return "addOrder";
    }


    public void addOrderLog() {
        log.info(">>>流程2");
    }
}



该文章来自:蚂蚁课堂学习
原文地址:https://www.cnblogs.com/ming-blogs/p/12951861.html