Java設(shè)計(jì)模式之代理模式與@Async異步注解失效的解決
JDK動(dòng)態(tài)代理實(shí)現(xiàn)自定義異步注解(@Async)
實(shí)現(xiàn)思路:
- 首先自定義一個(gè)注解,命名為:
ExtAsync - 實(shí)現(xiàn)一個(gè)接口,這個(gè)接口的實(shí)現(xiàn)類就是被代理類
- 實(shí)現(xiàn)jdk的
InvocationHandler接口,根據(jù)反射獲取目標(biāo)方法的信息,判斷是否有異步注解,如果有則另起一個(gè)線程異步執(zhí)行去。
1、異步注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtAsync {
}
2、接口和實(shí)現(xiàn)類
//接口
public interface OrderService {
String addOrder();
void addOrderLog();
}
//實(shí)現(xiàn)類
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動(dòng)態(tài)代理需要實(shí)現(xiàn)的InvocationHandler接口類
public class MayiktInvocationHandler implements InvocationHandler {
/**
* 目標(biāo)對(duì)象
*/
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 {
//使用反射技術(shù)執(zhí)行目標(biāo)方法
// ExtAsync extAsync = method.getDeclaredAnnotation(ExtAsync.class);
//根據(jù)接口的信息查找到目標(biāo)對(duì)象的的方法
Method methodImpl = target.getClass().getMethod(method.getName(), method.getParameterTypes());
ExtAsync extAsync = methodImpl.getDeclaredAnnotation(ExtAsync.class);
if (extAsync == null) {
// 該方法上沒有加上異步注解,則直接調(diào)用目標(biāo)方法
return method.invoke(target, args);
}
// 單獨(dú)開啟一個(gè)線程異步處理目標(biāo)方法
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、測(cè)試類
public class Test001 {
public static void main(String[] args) {
OrderServiceImpl orderServiceImpl = new OrderServiceImpl();
MayiktInvocationHandler mayiktInvocationHandler =
new MayiktInvocationHandler(orderServiceImpl);
// 使用Jdk生成代理對(duì)象
OrderService orderServiceProxy = mayiktInvocationHandler.getProxy();
// 將代理設(shè)置給目標(biāo)對(duì)象
orderServiceImpl.setOrderServiceProxy(orderServiceProxy);
orderServiceProxy.addOrder();
}
}總結(jié)分析:加上自定義的異步注解,查看輸出的日志順序,然后注釋掉異步注解,再看輸出的日志順序。
SpringAOP實(shí)現(xiàn)自定義異步注解
核心在于AOP切面類上:攔截加了自定義異步注解的方法,看起一個(gè)線程執(zhí)行目標(biāo)方法。
@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() {
// 執(zhí)行我們的目標(biāo)方法
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
});
}
}Spring的異步注解@Async失效分析
注解原理:AOP技術(shù)–》動(dòng)態(tài)代理技術(shù)
Spring中是如何綜合使用Cglib和Jdk動(dòng)態(tài)代理呢?
- 如果被代理類有實(shí)現(xiàn)接口的情況下默認(rèn)采用 Jdk動(dòng)態(tài)代理 可以轉(zhuǎn)換為Cglib
- 如果被代理類沒有實(shí)現(xiàn)接口的情況下采用Cglib
異步注解失效:
1、如果控制類(加了@RestController的類)中的有方法加上了異步注解,并且有實(shí)現(xiàn)接口的情況下,則采用JDK動(dòng)態(tài)代理,控制類沒有注冊(cè)到SpringMVC容器中。
2、如果控制類(加了@RestController的類)中有的方法加上了異步注解,但是沒有實(shí)現(xiàn)接口的情況下,則采用CGLIB動(dòng)態(tài)代理,控制類可以注冊(cè)到SpringMVC容器,但是異步注解會(huì)失效。
為什么失效?
底層使用動(dòng)態(tài)代理模式,在代理類創(chuàng)建線程,如果沒有經(jīng)過代理類就不會(huì)創(chuàng)建線程,所以必須從Sping中獲取代理對(duì)象,通過代理對(duì)象.方法,才會(huì)經(jīng)過代理類實(shí)現(xiàn)創(chuàng)建線程異步操作。因?yàn)樵诳刂祁惖姆椒ㄔ黾赢惒阶⒔獾臅r(shí)候,調(diào)用該方法時(shí)調(diào)用的不是代理對(duì)象的方法,而是當(dāng)前對(duì)象的方法。
1、不要在當(dāng)前類直接使用異步注解,因?yàn)闆]有經(jīng)歷過代理類。
2、官方建議新建一個(gè)類來進(jìn)行異步操作。
原理: 如果在控制類(實(shí)現(xiàn)接口的類)上的方法上加了異步注解,采用JDK動(dòng)態(tài)代理技術(shù),代理基于接口實(shí)現(xiàn),而接口中沒有加上@RestController注解,所以代理對(duì)象無法注冊(cè)到SpringMVC容器中。反之,如果控制類沒有實(shí)現(xiàn)接口,則采用CGLIB動(dòng)態(tài)代理,生成的代理對(duì)象是采用繼承目標(biāo)對(duì)象(@RestController也會(huì)繼承過來),這時(shí)代理對(duì)象可以注入帶SpringMVC容器中。
到此這篇關(guān)于Java設(shè)計(jì)模式之代理模式與@Async異步注解失效的解決的文章就介紹到這了,更多相關(guān)Java代理模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot?Admin?監(jiān)控指標(biāo)接入Grafana可視化的實(shí)例詳解
Spring Boot Admin2 自帶有部分監(jiān)控圖表,如圖,有線程、內(nèi)存Heap和內(nèi)存Non Heap,這篇文章主要介紹了Spring?Boot?Admin?監(jiān)控指標(biāo)接入Grafana可視化,需要的朋友可以參考下2022-11-11
MP(MyBatis-Plus)實(shí)現(xiàn)樂觀鎖更新功能的示例代碼
這篇文章主要介紹了MP(MyBatis-Plus)實(shí)現(xiàn)樂觀鎖更新功能的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
SpringBoot使用JavaMailSender實(shí)現(xiàn)發(fā)送郵件
JavaMailSender是Spring Framework中的一個(gè)接口,用于發(fā)送電子郵件,本文主要為大家詳細(xì)介紹了SpringBoot如何使用JavaMailSender實(shí)現(xiàn)發(fā)送郵件,需要的可以參考下2023-12-12
Java Swing組件下拉菜單控件JComboBox用法示例
這篇文章主要介紹了Java Swing組件下拉菜單控件JComboBox用法,結(jié)合具體實(shí)例形式分析了Swing組件下拉菜單控件JComboBox的具體定義、使用方法及相關(guān)使用注意事項(xiàng),需要的朋友可以參考下2017-11-11
springboot對(duì)壓縮請(qǐng)求的處理方法
這篇文章主要介紹了springboot對(duì)壓縮請(qǐng)求的處理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05
詳解IDEA中SpringBoot整合Servlet三大組件的過程
這篇文章主要介紹了詳解IDEA中SpringBoot整合Servlet三大組件的過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
java如何連接數(shù)據(jù)庫(kù)executeUpdate()和executeQuery()
這篇文章主要介紹了java如何連接數(shù)據(jù)庫(kù)executeUpdate()和executeQuery(),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

