Java设计模式之代理模式与@Async异步注解失效的解决
作者:kaico2018
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代理模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!