@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");
    }
}

 




該文章來自:螞蟻課堂學習


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM