• 设计模式之代理模式-@Async异步注解失效


    自定义注解实现方式

    关键字:@interface
    参考以前博客:https://blog.csdn.net/weixin_44044929/article/details/117108409

    JDK动态代理实现自定义异步注解(@Async)

    实现思路:

    1. 首先自定义一个注解,命名为:ExtAsync
    2. 实现一个接口,这个接口的实现类就是被代理类
    3. 实现jdk的InvocationHandler接口,根据反射获取目标方法的信息,判断是否有异步注解,如果有则另起一个线程异步执行去。

    1、异步注解

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ExtAsync {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、接口和实现类

    //接口
    public interface OrderService {
        String addOrder();
        void addOrderLog();
    }
    
    //实现类
    public class OrderServiceImpl implements OrderService {
        private OrderService orderServiceProxy;
        public String addOrder() {
            System.out.println(Thread.currentThread().getName() + ">>>流程1");
            orderServiceProxy.addOrderLog();
            System.out.println(Thread.currentThread().getName() + ">>>流程3");
            return "addOrder";
        }
        @ExtAsync
        public void addOrderLog() {
            System.out.println(Thread.currentThread().getName() + ">>>流程2");
        }
    
        public void setOrderServiceProxy(OrderService orderServiceProxy) {
            this.orderServiceProxy = orderServiceProxy;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3、JDK动态代理需要实现的InvocationHandler接口类

    public class MayiktInvocationHandler implements InvocationHandler {
        /**
         * 目标对象
         */
        private Object target;
    
        /**
         * 定义线程池
         */
        private ExecutorService executorService;
    
        public MayiktInvocationHandler(Object target) {
            this.target = target;
            executorService = Executors.newFixedThreadPool(10);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //使用反射技术执行目标方法
    //        ExtAsync extAsync = method.getDeclaredAnnotation(ExtAsync.class);
            //根据接口的信息查找到目标对象的的方法
            Method methodImpl = target.getClass().getMethod(method.getName(), method.getParameterTypes());
            ExtAsync extAsync = methodImpl.getDeclaredAnnotation(ExtAsync.class);
            if (extAsync == null) {
                // 该方法上没有加上异步注解,则直接调用目标方法
                return method.invoke(target, args);
            }
            // 单独开启一个线程异步处理目标方法
            executorService.execute(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        method.invoke(target, args);
                    } catch (Exception e) {
    
                    }
    
                }
            });
            return null;
        }
    
        /**
         * 生成代理类
         *
         * @param <T>
         * @return
         */
        public <T> T getProxy() {
            return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    4、测试类

    public class Test001 {
        public static void main(String[] args) {
            OrderServiceImpl orderServiceImpl = new OrderServiceImpl();
            MayiktInvocationHandler mayiktInvocationHandler =
                    new MayiktInvocationHandler(orderServiceImpl);
            // 使用Jdk生成代理对象
            OrderService orderServiceProxy = mayiktInvocationHandler.getProxy();
    
            // 将代理设置给目标对象
            orderServiceImpl.setOrderServiceProxy(orderServiceProxy);
            orderServiceProxy.addOrder();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    总结分析:加上自定义的异步注解,查看输出的日志顺序,然后注释掉异步注解,再看输出的日志顺序。

    SpringAOP实现自定义异步注解

    核心在于AOP切面类上:拦截加了自定义异步注解的方法,看起一个线程执行目标方法。

    @Component
    @Aspect
    @Slf4j
    public class ExtAsyncAop {
    
        private ExecutorService executorService;
    
        public ExtAsyncAop() {
            executorService = Executors.newFixedThreadPool(10);
        }
        @Around(value = "@annotation(com.kaico.designMode.proxy.aopAsync.ext.ExtAsync)")
        public void doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
            // 直接获取到方法上有加上ExtAsync
            log.info(">>>拦截到我们方法上有加上ExtAsync");
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    // 执行我们的目标方法
                    try {
                        joinPoint.proceed();
                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                    }
                }
            });
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    Spring的异步注解@Async失效分析

    注解原理:AOP技术–》动态代理技术

    Spring中是如何综合使用Cglib和Jdk动态代理呢?

    1. 如果被代理类有实现接口的情况下默认采用 Jdk动态代理 可以转换为Cglib
    2. 如果被代理类没有实现接口的情况下采用Cglib

    异步注解失效:
    1、如果控制类(加了@RestController的类)中的有方法加上了异步注解,并且有实现接口的情况下,则采用JDK动态代理,控制类没有注册到SpringMVC容器中。
    2、如果控制类(加了@RestController的类)中有的方法加上了异步注解,但是没有实现接口的情况下,则采用CGLIB动态代理,控制类可以注册到SpringMVC容器,但是异步注解会失效。

    为什么失效?
    底层使用动态代理模式,在代理类创建线程,如果没有经过代理类就不会创建线程,所以必须从Sping中获取代理对象,通过代理对象.方法,才会经过代理类实现创建线程异步操作。因为在控制类的方法增加异步注解的时候,调用该方法时调用的不是代理对象的方法,而是当前对象的方法。
    1、不要在当前类直接使用异步注解,因为没有经历过代理类。
    2、官方建议新建一个类来进行异步操作。

    原理: 如果在控制类(实现接口的类)上的方法上加了异步注解,采用JDK动态代理技术,代理基于接口实现,而接口中没有加上@RestController注解,所以代理对象无法注册到SpringMVC容器中。反之,如果控制类没有实现接口,则采用CGLIB动态代理,生成的代理对象是采用继承目标对象(@RestController也会继承过来),这时代理对象可以注入带SpringMVC容器中。

  • 相关阅读:
    6-4代码块
    【逆向】导出表:1.编写程序打印所有的导出表信息 2.编写GetFunctionAddrByName 3.编写GetFunctionAddrByOrdinal
    解释一下React中的钩子(hooks),例如useState和useEffect。
    VirtualLab Fusion中的参数耦合
    基于plc的柔性制造系统供料检测单元的设计(论文+源码)
    无代码开发平台全部应用设置入门教程
    一文带你了解MySQL之redo日志
    kafka详解
    【Midjourney】Midjourney 连续性人物创作 ④ ( 使用 URL + Seed 随机种子生成连续性的人物 )
    python 基础知识(面向对象)
  • 原文地址:https://blog.csdn.net/weixin_44044929/article/details/125529961