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



    该文章来自:蚂蚁课堂学习
  • 相关阅读:
    计算机图形学--贝塞尔曲线2
    计算机图形学--贝塞尔曲线1
    Windows编程1
    winform里面的Form1.Designer.cs
    mfc添加自定义事件
    怎么精确控制solidworks里面的表格的位置
    solidworks的工程图模板文件和图纸格式文件
    给datagridview的下拉框添加valueChange事件
    php开启短标签与<?xml version="1.0" encoding="UTF-8"?>冲突
    PHP分行打印数组-php输出数组方法大全
  • 原文地址:https://www.cnblogs.com/ming-blogs/p/12951861.html
Copyright © 2020-2023  润新知