@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"); } }
该文章来自:蚂蚁课堂学习