在Java中,如果在同一個類中直接呼叫另一個帶有註解的方法,比如@Async或@Transactional,註解通常不會生效。這是因為這些註解通常是透過Spring的AOP(面向方面程式設計)來實現的,而AOP代理物件只能攔截外部對目標物件的方法呼叫,而不能攔截內部方法呼叫。
原因
Spring在處理這些註解時,會生成一個代理物件來管理事務、非同步操作等。如果你在同一個類中直接呼叫帶有這些註解的方法,本質上是在呼叫同一個物件的方法,而不是透過代理物件呼叫的,因此這些註解不會生效。
解決方案
1. 使用AOP註解的方法放到另一個Bean中
一種方法是將帶有這些註解的方法移到另一個Spring管理的Bean中,然後透過依賴注入呼叫該Bean的方法。
例如:
@Service public class MyService { @Autowired private AnotherService anotherService; public void myMethod() { anotherService.asyncMethod(); } } @Service public class AnotherService { @Async public void asyncMethod() { // 你的非同步程式碼 } }
2. 使用AOP自呼叫
如果不想將方法移到另一個Bean中,可以透過Spring應用上下文來獲取自身的代理物件並呼叫方法。
例如:
@Service public class MyService { @Autowired private ApplicationContext applicationContext; public void myMethod() { MyService self = applicationContext.getBean(MyService.class); self.asyncMethod(); } @Async public void asyncMethod() { // 你的非同步程式碼 } }
3. 使用@Configurable
和Spring AOP
這種方法相對複雜一些,可以在類上使用@Configurable
註解,然後在Spring配置中啟用AspectJ代理。
@Configurable public class MyService { public void myMethod() { asyncMethod(); } @Async public void asyncMethod() { // 你的非同步程式碼 } }
在Spring配置中啟用AspectJ代理:
xml複製程式碼 <bean id="configurableAnnotationProcessor" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/> <context:load-time-weaver aspectj-weaving="on"/>
總結
在同一個類中直接呼叫帶有AOP註解的方法時,這些註解通常不會生效。可以透過將方法移到另一個Bean、透過Spring上下文獲取自身代理物件或使用@Configurable
註解等方式解決這個問題。
完整的測試程式碼:
package com.minp.admin.task.standingbook; import lombok.extern.log4j.Log4j2; import org.springframework.context.ApplicationContext; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * @功能描寫: 測試非同步註解任務 * @Author **** * @Versions 1.0 * @Data: 2024/6/4 10:32 * @專案名稱: **** */ @Component @Log4j2 public class TestAsyncAnnotationTask { private final ApplicationContext applicationContext; public TestAsyncAnnotationTask(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void testAsyncAnnotationTask() { log.info("測試非同步註解任務--開始"); log.info("當前執行緒名稱-{}", Thread.currentThread().getName()); test1(); applicationContext.getBean(TestAsyncAnnotationTask.class).test2(); } @Async public void test1() { log.info("呼叫了test1"); log.info("當前執行緒名稱-{}", Thread.currentThread().getName()); } @Async public void test2() { log.info("呼叫了test2"); log.info("當前執行緒名稱-{}", Thread.currentThread().getName()); } }