关键字:@interface
参考以前博客:https://blog.csdn.net/weixin_44044929/article/details/117108409
实现思路:
ExtAsync
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();
}
}
总结分析:加上自定义的异步注解,查看输出的日志顺序,然后注释掉异步注解,再看输出的日志顺序。
核心在于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();
}
}
});
}
}
注解原理:AOP技术–》动态代理技术
Spring中是如何综合使用Cglib和Jdk动态代理呢?
异步注解失效:
1、如果控制类(加了@RestController的类)中的有方法加上了异步注解,并且有实现接口的情况下,则采用JDK动态代理,控制类没有注册到SpringMVC容器中。
2、如果控制类(加了@RestController的类)中有的方法加上了异步注解,但是没有实现接口的情况下,则采用CGLIB动态代理,控制类可以注册到SpringMVC容器,但是异步注解会失效。
为什么失效?
底层使用动态代理模式,在代理类创建线程,如果没有经过代理类就不会创建线程,所以必须从Sping中获取代理对象,通过代理对象.方法,才会经过代理类实现创建线程异步操作。因为在控制类的方法增加异步注解的时候,调用该方法时调用的不是代理对象的方法,而是当前对象的方法。
1、不要在当前类直接使用异步注解,因为没有经历过代理类。
2、官方建议新建一个类来进行异步操作。
原理: 如果在控制类(实现接口的类)上的方法上加了异步注解,采用JDK动态代理技术,代理基于接口实现,而接口中没有加上@RestController
注解,所以代理对象无法注册到SpringMVC容器中。反之,如果控制类没有实现接口,则采用CGLIB动态代理,生成的代理对象是采用继承目标对象(@RestController
也会继承过来),这时代理对象可以注入带SpringMVC容器中。