Java設(shè)計模式之代理模式與@Async異步注解失效的解決
JDK動態(tài)代理實(shí)現(xiàn)自定義異步注解(@Async)
實(shí)現(xiàn)思路:
- 首先自定義一個注解,命名為:
ExtAsync
- 實(shí)現(xiàn)一個接口,這個接口的實(shí)現(xiàn)類就是被代理類
- 實(shí)現(xiàn)jdk的
InvocationHandler
接口,根據(jù)反射獲取目標(biāo)方法的信息,判斷是否有異步注解,如果有則另起一個線程異步執(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動態(tài)代理需要實(shí)現(xiàn)的InvocationHandler
接口類
public class MayiktInvocationHandler implements InvocationHandler { /** * 目標(biāo)對象 */ 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)對象的的方法 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ú)開啟一個線程異步處理目標(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、測試類
public class Test001 { public static void main(String[] args) { OrderServiceImpl orderServiceImpl = new OrderServiceImpl(); MayiktInvocationHandler mayiktInvocationHandler = new MayiktInvocationHandler(orderServiceImpl); // 使用Jdk生成代理對象 OrderService orderServiceProxy = mayiktInvocationHandler.getProxy(); // 將代理設(shè)置給目標(biāo)對象 orderServiceImpl.setOrderServiceProxy(orderServiceProxy); orderServiceProxy.addOrder(); } }
總結(jié)分析:加上自定義的異步注解,查看輸出的日志順序,然后注釋掉異步注解,再看輸出的日志順序。
SpringAOP實(shí)現(xiàn)自定義異步注解
核心在于AOP切面類上:攔截加了自定義異步注解的方法,看起一個線程執(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ù)–》動態(tài)代理技術(shù)
Spring中是如何綜合使用Cglib和Jdk動態(tài)代理呢?
- 如果被代理類有實(shí)現(xiàn)接口的情況下默認(rèn)采用 Jdk動態(tài)代理 可以轉(zhuǎn)換為Cglib
- 如果被代理類沒有實(shí)現(xiàn)接口的情況下采用Cglib
異步注解失效:
1、如果控制類(加了@RestController的類)中的有方法加上了異步注解,并且有實(shí)現(xiàn)接口的情況下,則采用JDK動態(tài)代理,控制類沒有注冊到SpringMVC容器中。
2、如果控制類(加了@RestController的類)中有的方法加上了異步注解,但是沒有實(shí)現(xiàn)接口的情況下,則采用CGLIB動態(tài)代理,控制類可以注冊到SpringMVC容器,但是異步注解會失效。
為什么失效?
底層使用動態(tài)代理模式,在代理類創(chuàng)建線程,如果沒有經(jīng)過代理類就不會創(chuàng)建線程,所以必須從Sping中獲取代理對象,通過代理對象.方法,才會經(jīng)過代理類實(shí)現(xiàn)創(chuàng)建線程異步操作。因?yàn)樵诳刂祁惖姆椒ㄔ黾赢惒阶⒔獾臅r候,調(diào)用該方法時調(diào)用的不是代理對象的方法,而是當(dāng)前對象的方法。
1、不要在當(dāng)前類直接使用異步注解,因?yàn)闆]有經(jīng)歷過代理類。
2、官方建議新建一個類來進(jìn)行異步操作。
原理: 如果在控制類(實(shí)現(xiàn)接口的類)上的方法上加了異步注解,采用JDK動態(tài)代理技術(shù),代理基于接口實(shí)現(xiàn),而接口中沒有加上@RestController
注解,所以代理對象無法注冊到SpringMVC容器中。反之,如果控制類沒有實(shí)現(xiàn)接口,則采用CGLIB動態(tài)代理,生成的代理對象是采用繼承目標(biāo)對象(@RestController
也會繼承過來),這時代理對象可以注入帶SpringMVC容器中。
到此這篇關(guān)于Java設(shè)計模式之代理模式與@Async異步注解失效的解決的文章就介紹到這了,更多相關(guān)Java代理模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC 重新定向redirect請求中攜帶數(shù)據(jù)方式
這篇文章主要介紹了SpringMVC 重新定向redirect請求中攜帶數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12一文詳解java如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
從?Java?8?開始,便引入了一種稱為“流式?API”的編程風(fēng)格,當(dāng)然也被稱為“鏈?zhǔn)皆O(shè)置”或“鏈?zhǔn)秸{(diào)用”,本文主要來和大家討論一下如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,感興趣的可以了解下2023-12-12如何基于SpringBoot實(shí)現(xiàn)人臉識別功能
人工智能時代的到來,相信大家已耳濡目染,虹軟免費(fèi),離線開放的人臉識別SDK,正推動著全行業(yè)進(jìn)入刷臉時代,下面這篇文章主要給大家介紹了關(guān)于如何基于SpringBoot實(shí)現(xiàn)人臉識別功能的相關(guān)資料,需要的朋友可以參考下2022-05-05Spring自動裝配之方法、構(gòu)造器位置的自動注入操作
這篇文章主要介紹了Spring自動裝配之方法、構(gòu)造器位置的自動注入操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08JavaWeb?使用DBUtils實(shí)現(xiàn)增刪改查方式
這篇文章主要介紹了JavaWeb?使用DBUtils實(shí)現(xiàn)增刪改查方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12