Java代理模式
kaico2018 人气:0JDK动态代理实现自定义异步注解(@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容器中。
加载全部内容