Java设计模式之代理模式与@Async异步注解失效的解决

 更新时间:2022年07月27日 09:34:43   作者:kaico2018  
代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象

自定义注解实现方式

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

实现思路:

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

1、异步注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtAsync {
}

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;
    }
}

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

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();
    }
}

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

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();
                }
            }
        });
    }
}

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

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

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

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

异步注解失效:

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

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

为什么失效?

底层使用动态代理模式,在代理类创建线程,如果没有经过代理类就不会创建线程,所以必须从Sping中获取代理对象,通过代理对象.方法,才会经过代理类实现创建线程异步操作。因为在控制类的方法增加异步注解的时候,调用该方法时调用的不是代理对象的方法,而是当前对象的方法。

1、不要在当前类直接使用异步注解,因为没有经历过代理类。

2、官方建议新建一个类来进行异步操作。

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

到此这篇关于Java设计模式之代理模式与@Async异步注解失效的解决的文章就介绍到这了,更多相关Java代理模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot Admin 监控指标接入Grafana可视化的实例详解

    Spring Boot Admin 监控指标接入Grafana可视化的实例详解

    Spring Boot Admin2 自带有部分监控图表,如图,有线程、内存Heap和内存Non Heap,这篇文章主要介绍了Spring Boot Admin 监控指标接入Grafana可视化,需要的朋友可以参考下
    2022-11-11
  • MP(MyBatis-Plus)实现乐观锁更新功能的示例代码

    MP(MyBatis-Plus)实现乐观锁更新功能的示例代码

    这篇文章主要介绍了MP(MyBatis-Plus)实现乐观锁更新功能的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • SpringBoot使用JavaMailSender实现发送邮件

    SpringBoot使用JavaMailSender实现发送邮件

    JavaMailSender是Spring Framework中的一个接口,用于发送电子邮件,本文主要为大家详细介绍了SpringBoot如何使用JavaMailSender实现发送邮件,需要的可以参考下
    2023-12-12
  • Mybatis自定义拦截器和插件开发详解

    Mybatis自定义拦截器和插件开发详解

    这篇文章主要给大家介绍了关于Mybatis自定义拦截器和插件开发的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Java Swing组件下拉菜单控件JComboBox用法示例

    Java Swing组件下拉菜单控件JComboBox用法示例

    这篇文章主要介绍了Java Swing组件下拉菜单控件JComboBox用法,结合具体实例形式分析了Swing组件下拉菜单控件JComboBox的具体定义、使用方法及相关使用注意事项,需要的朋友可以参考下
    2017-11-11
  • springboot对压缩请求的处理方法

    springboot对压缩请求的处理方法

    这篇文章主要介绍了springboot对压缩请求的处理,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Java中抽象类和接口的用法详解

    Java中抽象类和接口的用法详解

    这篇文章主要为大家详细介绍了Java中抽象类和接口的使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-08-08
  • 详解IDEA中SpringBoot整合Servlet三大组件的过程

    详解IDEA中SpringBoot整合Servlet三大组件的过程

    这篇文章主要介绍了详解IDEA中SpringBoot整合Servlet三大组件的过程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • 一文带你入门JDK8新特性——Lambda表达式

    一文带你入门JDK8新特性——Lambda表达式

    这篇文章主要介绍了JDK8新特性——Lambda表达式的相关资料,帮助大家更好的理解和学习JAVA开发,感兴趣的朋友可以了解下
    2020-08-08
  • java如何连接数据库executeUpdate()和executeQuery()

    java如何连接数据库executeUpdate()和executeQuery()

    这篇文章主要介绍了java如何连接数据库executeUpdate()和executeQuery(),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03

最新评论