首頁 > 軟體

Java設計模式之代理模式與@Async非同步註解失效的解決

2022-07-27 14:01:54

自定義註解實現方式

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代理模式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com