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"); } }
該文章來自:螞蟻課堂學習