最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring @Async 注解異步調(diào)用失效的五種解決方案

 更新時(shí)間:2025年05月02日 11:24:01   投稿:yin  
加上了@Async 注解,期待它能異步執(zhí)行,結(jié)果發(fā)現(xiàn)它還是同步執(zhí)行的?更困惑的是,同樣的注解在其他地方卻能正常工作,這個(gè)問題困擾了很多 Java 開發(fā)者,尤其是當(dāng)你在同一個(gè)類中調(diào)用帶有@Async 注解的方法時(shí),今天,我們就來深入解析這個(gè)問題的原因,并提供多種實(shí)用的解決方案

給一個(gè)方法加上了@Async 注解,期待它能異步執(zhí)行,結(jié)果發(fā)現(xiàn)它還是同步執(zhí)行的?更困惑的是,同樣的注解在其他地方卻能正常工作。這個(gè)問題困擾了很多 Java 開發(fā)者,尤其是當(dāng)你在同一個(gè)類中調(diào)用帶有@Async 注解的方法時(shí)。今天,我們就來深入解析這個(gè)問題的原因,并提供多種實(shí)用的解決方案。

Spring @Async 的正常工作原理

在討論內(nèi)部調(diào)用問題前,我們先了解一下@Async 注解的基本工作原理。

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;

// 簡單的用戶類
class User {
    private String email;
    private String name;

    // 默認(rèn)構(gòu)造器(Spring Bean實(shí)例化需要)
    public User() {}

    public User(String email, String name) {
        this.email = email;
        this.name = name;
    }

    public String getEmail() { return email; }
    public String getName() { return name; }
    public void setEmail(String email) { this.email = email; }
    public void setName(String name) { this.name = name; }
}

@Service
public class EmailService {

    @Async
    public void sendEmail(String to, String content) {
        // 耗時(shí)的郵件發(fā)送邏輯
        System.out.println("發(fā)送郵件中... 當(dāng)前線程: " + Thread.currentThread().getName());
    }
}

@Service
public class UserService {
    @Autowired
    private EmailService emailService;

    public void registerUser(User user) {
        // 用戶注冊邏輯
        System.out.println("注冊用戶中... 當(dāng)前線程: " + Thread.currentThread().getName());

        // 異步發(fā)送歡迎郵件
        emailService.sendEmail(user.getEmail(), "歡迎注冊!");

        // 注冊完成,立即返回
        System.out.println("注冊完成!");
    }
}

Spring @Async 的工作原理如下:

Spring @Async 內(nèi)部調(diào)用失效問題:五種解決方案實(shí)戰(zhàn)分析_TaskExecutor

Spring 通過 AOP 代理實(shí)現(xiàn)@Async 功能。當(dāng)一個(gè)方法被@Async 注解標(biāo)記時(shí),Spring 會(huì)創(chuàng)建一個(gè)代理對象。當(dāng)外部代碼調(diào)用該方法時(shí),調(diào)用實(shí)際上首先被代理對象攔截,然后代理將任務(wù)提交到線程池異步執(zhí)行。

Spring 默認(rèn)對實(shí)現(xiàn)接口的類使用 JDK 動(dòng)態(tài)代理,對非接口類使用 CGLIB 代理。但無論哪種代理,重要的是調(diào)用必須經(jīng)過代理對象,才能觸發(fā)@Async 的處理邏輯。

內(nèi)部調(diào)用問題

問題出現(xiàn)在同一個(gè)類中調(diào)用自己的@Async 方法時(shí):

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class NotificationService {

    public void notifyAll(List<User> users, String message) {
        System.out.println("開始通知所有用戶... 當(dāng)前線程: " + Thread.currentThread().getName());

        for (User user : users) {
            // 調(diào)用同一個(gè)類中的@Async方法
            sendNotification(user, message);  // 問題:這里變成了同步調(diào)用!
        }

        System.out.println("通知流程初始化完成!");  // 實(shí)際要等所有通知發(fā)送完才會(huì)執(zhí)行到這里
    }

    @Async
    public void sendNotification(User user, String message) {
        // 模擬耗時(shí)操作
        try {
            System.out.println("正在發(fā)送通知給" + user.getName() +
                    "... 當(dāng)前線程: " + Thread.currentThread().getName());
            Thread.sleep(1000); // 模擬耗時(shí)操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

上面的代碼中,雖然sendNotification方法標(biāo)記了@Async,但當(dāng)在notifyAll方法中調(diào)用它時(shí),它還是會(huì)同步執(zhí)行,這不是我們預(yù)期的行為。

為什么內(nèi)部調(diào)用會(huì)失效?

Spring @Async 內(nèi)部調(diào)用失效問題:五種解決方案實(shí)戰(zhàn)分析_TaskExecutor_02

內(nèi)部調(diào)用失效的核心原因是:Spring 的 AOP 是基于代理實(shí)現(xiàn)的,而內(nèi)部方法調(diào)用會(huì)繞過代理機(jī)制。

當(dāng)你在一個(gè)類中直接調(diào)用同一個(gè)類的方法時(shí)(即使用this.method()或簡單的method()),這種調(diào)用是通過 Java 的常規(guī)方法調(diào)用機(jī)制直接執(zhí)行的,完全繞過了 Spring 創(chuàng)建的代理對象。沒有經(jīng)過代理,@Async 注解就無法被識別和處理,因此方法會(huì)按普通方法同步執(zhí)行。

從源碼角度看,Spring 通過AsyncAnnotationBeanPostProcessor處理帶有@Async 注解的方法,創(chuàng)建代理對象。當(dāng)方法調(diào)用經(jīng)過代理時(shí),代理會(huì)檢測注解并將任務(wù)提交給配置的TaskExecutor(Spring 用于執(zhí)行異步任務(wù)的核心接口,提供線程池管理等功能)。內(nèi)部調(diào)用直接執(zhí)行原始方法,根本不經(jīng)過這個(gè)處理流程。

五種解決方案

方案 1:自我注入(Self-Injection)

最簡單的方法是在類中注入自己:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

@Service
public class NotificationService {

    @Autowired
    private NotificationService self;  // 注入自己的代理對象

    public void notifyAll(List<User> users, String message) {
        System.out.println("開始通知所有用戶... 當(dāng)前線程: " + Thread.currentThread().getName());

        for (User user : users) {
            // 通過自注入的引用調(diào)用@Async方法
            self.sendNotification(user, message);  // 現(xiàn)在是異步調(diào)用!
        }

        System.out.println("通知流程初始化完成!");  // 立即執(zhí)行,不等待通知完成
    }

    @Async
    public void sendNotification(User user, String message) {
        // 實(shí)現(xiàn)同前...
    }
}

工作原理:當(dāng) Spring 注入self字段時(shí),它實(shí)際上注入的是一個(gè)代理對象,而不是原始對象。通過代理調(diào)用方法,確保@Async 注解能被正確處理。

優(yōu)點(diǎn)

  • 實(shí)現(xiàn)簡單,僅需添加一個(gè)自引用字段,無需修改方法邏輯
  • 不改變原有的類結(jié)構(gòu)

缺點(diǎn)

  • 可能導(dǎo)致循環(huán)依賴問題(不過 Spring 通常能處理這類循環(huán)依賴)
  • 代碼看起來可能有點(diǎn)奇怪,自注入不是一種常見模式
  • 如果服務(wù)類需要序列化,代理對象可能導(dǎo)致序列化問題

方案 2:使用 ApplicationContext 獲取代理對象

通過 Spring 的 ApplicationContext 手動(dòng)獲取代理對象:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import java.util.List;

@Service
public class NotificationService {

    @Autowired
    private ApplicationContext applicationContext;

    public void notifyAll(List<User> users, String message) {
        System.out.println("開始通知所有用戶... 當(dāng)前線程: " + Thread.currentThread().getName());

        // 獲取代理對象
        NotificationService proxy = applicationContext.getBean(NotificationService.class);

        for (User user : users) {
            // 通過代理對象調(diào)用@Async方法
            proxy.sendNotification(user, message);  // 異步調(diào)用成功
        }

        System.out.println("通知流程初始化完成!");
    }

    @Async
    public void sendNotification(User user, String message) {
        // 實(shí)現(xiàn)同前...
    }
}

工作原理:從 ApplicationContext 獲取的 bean 總是代理對象(如果應(yīng)該被代理的話)。通過這個(gè)代理調(diào)用方法會(huì)觸發(fā)所有 AOP 切面,包括@Async。

優(yōu)點(diǎn)

  • 清晰明了,顯式獲取代理對象
  • 不需要添加額外的字段

缺點(diǎn)

  • 增加了對 ApplicationContext 的依賴
  • 每次調(diào)用前都需要獲取 bean,略顯冗余

方案 3:使用 AopContext 獲取代理對象

利用 Spring AOP 提供的工具類獲取當(dāng)前代理:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.aop.framework.AopContext;
import java.util.List;

@Configuration
@EnableAsync
@EnableAspectJAutoProxy(exposeProxy = true)  // 重要:暴露代理對象
public class AsyncConfig {
    // 異步配置...
}

@Service
public class NotificationService {

    public void notifyAll(List<User> users, String message) {
        System.out.println("開始通知所有用戶... 當(dāng)前線程: " + Thread.currentThread().getName());

        // 獲取當(dāng)前代理對象
        NotificationService proxy = (NotificationService) AopContext.currentProxy();

        for (User user : users) {
            // 通過代理對象調(diào)用@Async方法
            proxy.sendNotification(user, message);  // 異步調(diào)用成功
        }

        System.out.println("通知流程初始化完成!");
    }

    @Async
    public void sendNotification(User user, String message) {
        // 實(shí)現(xiàn)同前...
    }
}

工作原理:Spring AOP 提供了AopContext.currentProxy()方法來獲取當(dāng)前的代理對象。調(diào)用方法時(shí),使用這個(gè)代理對象而不是this。

注意事項(xiàng):必須在配置中設(shè)置@EnableAspectJAutoProxy(exposeProxy = true)來暴露代理對象,否則會(huì)拋出異常。

優(yōu)點(diǎn)

  • 無需注入其他對象
  • 代碼清晰,直接使用 AOP 上下文

缺點(diǎn)

  • 需要顯式配置exposeProxy = true
  • 依賴 Spring AOP 的特定 API

方案 4:拆分為單獨(dú)的服務(wù)類

將異步方法拆分到單獨(dú)的服務(wù)類中:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

@Service
public class AsyncNotificationService {

    @Async
    public void sendNotification(User user, String message) {
        // 模擬耗時(shí)操作
        try {
            System.out.println("正在發(fā)送通知給" + user.getName() +
                    "... 當(dāng)前線程: " + Thread.currentThread().getName());
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

@Service
public class NotificationService {

    @Autowired
    private AsyncNotificationService asyncService;

    public void notifyAll(List<User> users, String message) {
        System.out.println("開始通知所有用戶... 當(dāng)前線程: " + Thread.currentThread().getName());

        for (User user : users) {
            // 調(diào)用專門的異步服務(wù)
            asyncService.sendNotification(user, message);  // 正常異步調(diào)用
        }

        System.out.println("通知流程初始化完成!");
    }
}

工作原理:將需要異步執(zhí)行的方法移動(dòng)到專門的服務(wù)類中,然后通過依賴注入使用這個(gè)服務(wù)。這樣,調(diào)用總是通過 Spring 代理對象進(jìn)行的。

優(yōu)點(diǎn)

  • 符合單一職責(zé)原則,代碼組織更清晰
  • 避免了所有與代理相關(guān)的問題
  • 可以更好地對異步操作進(jìn)行組織和管理
  • 更符合依賴倒置原則,便于單元測試和模擬測試

缺點(diǎn)

  • 需要?jiǎng)?chuàng)建額外的類
  • 可能導(dǎo)致類的數(shù)量增加

方案 5:手動(dòng)使用 TaskExecutor

完全放棄@Async 注解,手動(dòng)使用 Spring 的 TaskExecutor:

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Service
public class NotificationService {

    @Autowired
    private TaskExecutor taskExecutor;  // Spring提供的任務(wù)執(zhí)行器接口

    public void notifyAll(List<User> users, String message) {
        System.out.println("開始通知所有用戶... 當(dāng)前線程: " + Thread.currentThread().getName());

        for (User user : users) {
            // 手動(dòng)提交任務(wù)到執(zhí)行器
            taskExecutor.execute(() -> {
                sendNotification(user, message);  // 異步執(zhí)行
            });

            // 如需獲取返回值,可以使用CompletableFuture
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                return sendNotificationWithResult(user, message);
            }, taskExecutor);

            // 非阻塞處理結(jié)果
            future.thenAccept(result -> {
                System.out.println("通知結(jié)果: " + result);
            });

            // 鏈?zhǔn)讲僮魇纠恨D(zhuǎn)換結(jié)果并組合多個(gè)異步操作
            CompletableFuture<Integer> processedFuture = future
                .thenApply(result -> result.length())  // 轉(zhuǎn)換結(jié)果
                .thenCombine(  // 組合另一個(gè)異步操作
                    CompletableFuture.supplyAsync(() -> user.getName().length()),
                    (len1, len2) -> len1 + len2
                );

            // 非阻塞異常處理
            processedFuture.exceptionally(ex -> {
                System.err.println("處理失敗: " + ex.getMessage());
                return -1;
            });
        }

        System.out.println("通知流程初始化完成!");
    }

    // 注意:不再需要@Async注解
    public void sendNotification(User user, String message) {
        // 實(shí)現(xiàn)同前...
    }

    public String sendNotificationWithResult(User user, String message) {
        // 返回通知結(jié)果
        return "已通知" + user.getName();
    }
}

工作原理:直接使用 Spring 的 TaskExecutor 提交任務(wù),完全繞過 AOP 代理機(jī)制。

優(yōu)點(diǎn)

  • 完全控制異步執(zhí)行的方式和時(shí)機(jī)
  • 不依賴 AOP 代理,更直接和透明
  • 可以更細(xì)粒度地控制任務(wù)執(zhí)行(如添加超時(shí)、錯(cuò)誤處理等)
  • 支持靈活的返回值處理,結(jié)合 CompletableFuture 實(shí)現(xiàn)非阻塞編程
  • 支持復(fù)雜的異步編排(如鏈?zhǔn)讲僮?、組合多個(gè)異步任務(wù))

缺點(diǎn)

  • 失去了@Async 的聲明式便利性
  • 需要更多的手動(dòng)編碼
  • 需要移除@Async 注解,修改方法簽名和調(diào)用邏輯,代碼侵入性高

針對返回值的異步方法

如果你的@Async 方法有返回值,它應(yīng)該返回FutureCompletableFuture。在處理內(nèi)部調(diào)用時(shí),上述解決方案同樣適用:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;

// 示例業(yè)務(wù)類
class ReportRequest {
    private String id;

    // 默認(rèn)構(gòu)造器
    public ReportRequest() {}

    public ReportRequest(String id) { this.id = id; }
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
}

class Report {
    private String id;
    private String content;

    // 默認(rèn)構(gòu)造器
    public Report() {}

    public Report(String id, String content) {
        this.id = id;
        this.content = content;
    }
}

@Service
public class ReportService {

    @Autowired
    private ReportService self;  // 使用方案1:自我注入

    public void generateReports(List<ReportRequest> requests) {
        List<CompletableFuture<Report>> futures = new ArrayList<>();

        for (ReportRequest request : requests) {
            // 通過代理調(diào)用返回CompletableFuture的異步方法
            CompletableFuture<Report> future = self.generateReport(request);
            futures.add(future);
        }

        // 等待所有報(bào)告生成完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        // 處理結(jié)果
        for (CompletableFuture<Report> future : futures) {
            Report report = future.join();
            // 處理報(bào)告...
        }
    }

    @Async
    public CompletableFuture<Report> generateReport(ReportRequest request) {
        // 模擬耗時(shí)的報(bào)告生成
        try {
            System.out.println("生成報(bào)告中... 當(dāng)前線程: " + Thread.currentThread().getName());
            Thread.sleep(2000);
            Report report = new Report(request.getId(), "報(bào)告內(nèi)容...");
            return CompletableFuture.completedFuture(report);
        } catch (Exception e) {
            CompletableFuture<Report> future = new CompletableFuture<>();
            future.completeExceptionally(e);
            return future;
        }
    }
}

異常處理與實(shí)踐建議

異步方法的異常處理需要特別注意:異步執(zhí)行的方法拋出的異常不會(huì)傳播到調(diào)用方,因?yàn)楫惓0l(fā)生在不同的線程中。

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.AsyncResult;

@Service
public class RobustNotificationService {

    @Autowired
    private RobustNotificationService self;
    private static final Logger logger = LoggerFactory.getLogger(RobustNotificationService.class);

    public void notifyAll(List<User> users, String message) {
        for (User user : users) {
            // 錯(cuò)誤:無法捕獲異步方法的異常,因?yàn)楫惓0l(fā)生在另一個(gè)線程
            // try {
            //     self.sendNotification(user, message);
            // } catch (Exception e) {
            //     logger.error("Failed to send notification to user: " + user.getId(), e);
            // }

            // 正確方式1:使用全局異常處理器(在AsyncConfigurer中配置)
            self.sendNotification(user, message);

            // 正確方式2:如果方法返回Future,可以通過future捕獲異常
            Future<"通知發(fā)送失敗: " + user.getName(), e);
                // 處理失敗情況
            }

            // 正確方式3:使用CompletableFuture的異常處理
            CompletableFuture<Void> cf = self.sendNotificationWithCompletableFuture(user, message);
            cf.exceptionally(ex -> {
                logger.error("通知發(fā)送失敗: " + user.getName(), ex);
                return null;
            });
        }
    }

    @Async
    public void sendNotification(User user, String message) {
        try {
            // 通知邏輯...
            if (user.getName() == null) {
                throw new RuntimeException("用戶名不能為空");
            }
        } catch (Exception e) {
            // 記錄詳細(xì)的異常信息,但異常不會(huì)傳播到調(diào)用方
            logger.error("通知失敗: " + user.getName(), e);
            // 異常會(huì)被AsyncUncaughtExceptionHandler處理(如果配置了)
            throw e;
        }
    }

    @Async
    public Future<Void> sendNotificationWithFuture(User user, String message) {
        // 實(shí)現(xiàn)邏輯...
        return new AsyncResult<>(null);
    }

    @Async
    public CompletableFuture<Void> sendNotificationWithCompletableFuture(User user, String message) {
        // 實(shí)現(xiàn)邏輯...
        return CompletableFuture.completedFuture(null);
    }
}

實(shí)踐建議

  1. 合理配置線程池:默認(rèn)情況下,Spring 使用SimpleAsyncTaskExecutor,每次調(diào)用都會(huì)創(chuàng)建新線程,這在生產(chǎn)環(huán)境中是不可接受的。應(yīng)配置適當(dāng)?shù)木€程池:
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);       // 核心線程數(shù)
        executor.setMaxPoolSize(10);       // 最大線程數(shù)
        executor.setQueueCapacity(25);     // 隊(duì)列容量
        executor.setThreadNamePrefix("MyAsync-");

        // 拒絕策略:當(dāng)隊(duì)列滿且線程數(shù)達(dá)到最大時(shí)的處理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 允許核心線程超時(shí),適用于負(fù)載波動(dòng)的場景
        executor.setAllowCoreThreadTimeOut(true);

        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}
  1. 適當(dāng)使用超時(shí)控制:對于需要獲取結(jié)果的異步方法,添加超時(shí)控制,但要注意阻塞問題:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

// 阻塞式超時(shí)控制(慎用,會(huì)阻塞當(dāng)前線程)
CompletableFuture<Report> future = reportService.generateReport(request);
try {
    Report report = future.get(30, TimeUnit.SECONDS); // 設(shè)置30秒超時(shí)
} catch (TimeoutException e) {
    logger.error("報(bào)告生成超時(shí)", e);
    // 處理超時(shí)情況
}

// 更好的非阻塞方式:
future.orTimeout(30, TimeUnit.SECONDS)
      .thenAccept(report -> processReport(report))
      .exceptionally(ex -> {
          if (ex instanceof TimeoutException) {
              logger.error("報(bào)告生成超時(shí)");
          } else {
              logger.error("報(bào)告生成失敗", ex);
          }
          return null;
      });
  1. 慎用方案選擇
  • 對于簡單場景,自我注入(方案 1)最簡單直接
  • 對于復(fù)雜業(yè)務(wù)邏輯,拆分服務(wù)(方案 4)是更好的架構(gòu)選擇
  • 如果需要細(xì)粒度控制,直接使用 TaskExecutor(方案 5)是最靈活的選擇
  1. 注意事務(wù)傳播: 異步方法執(zhí)行在單獨(dú)的線程中,會(huì)導(dǎo)致事務(wù)傳播行為失效。Spring 的事務(wù)上下文通過ThreadLocal與當(dāng)前線程綁定,異步方法在新線程中執(zhí)行時(shí),無法訪問調(diào)用方的ThreadLocal數(shù)據(jù),因此必須在異步方法上單獨(dú)聲明@Transactional以創(chuàng)建新事務(wù)。
@Service
public class TransactionService {

    @Autowired
    private TransactionService self;

    @Transactional
    public void saveWithTransaction(Entity entity) {
        // 事務(wù)操作...

        // 錯(cuò)誤:異步方法在新線程中執(zhí)行,當(dāng)前事務(wù)不會(huì)傳播
        self.asyncOperation(entity); // 不會(huì)共享當(dāng)前事務(wù)
    }

    @Async
    @Transactional // 必須單獨(dú)添加事務(wù)注解,會(huì)創(chuàng)建新的事務(wù)
    public void asyncOperation(Entity entity) {
        // 此方法將有自己的事務(wù),而非繼承調(diào)用方的事務(wù)
    }
}
  1. 驗(yàn)證異步執(zhí)行
// 在測試類中驗(yàn)證異步執(zhí)行
@SpringBootTest
public class AsyncServiceTest {

    @Autowired
    private NotificationService service;

    @Test
    public void testAsyncExecution() throws Exception {
        // 記錄主線程名稱
        String mainThread = Thread.currentThread().getName();

        // 保存異步線程名稱
        final String[] asyncThread = new String[1];
        CountDownLatch latch = new CountDownLatch(1);

        User user = new User();
        user.setName("TestUser");

        // 重寫異步方法以捕獲線程名稱
        service.sendNotificationWithCompletableFuture(user, "test")
               .thenAccept(v -> {
                   asyncThread[0] = Thread.currentThread().getName();
                   latch.countDown();
               });

        // 等待異步操作完成
        latch.await(5, TimeUnit.SECONDS);

        // 驗(yàn)證線程不同
        assertThat(mainThread).isNotEqualTo(asyncThread[0]);
        assertThat(asyncThread[0]).startsWith("MyAsync-");
    }
}

五種方案對比

Spring @Async 內(nèi)部調(diào)用失效問題:五種解決方案實(shí)戰(zhàn)分析_TaskExecutor_03

總結(jié)

解決方案

實(shí)現(xiàn)復(fù)雜度

代碼侵入性

額外依賴

架構(gòu)清晰度

適用場景

自我注入

(僅添加一個(gè)自注入字段,無方法邏輯修改)

簡單項(xiàng)目,快速解決

ApplicationContext

ApplicationContext

需要明確控制代理獲取

AopContext

需開啟 exposeProxy

不想增加依賴字段

拆分服務(wù)

大型項(xiàng)目,關(guān)注點(diǎn)分離

手動(dòng) TaskExecutor

(需修改方法注解和調(diào)用邏輯)

TaskExecutor

需要精細(xì)控制異步執(zhí)行

需靈活處理返回值

需要復(fù)雜異步編排

相關(guān)文章

  • 2024最新版Java?JDK安裝配置圖文詳解全攻略

    2024最新版Java?JDK安裝配置圖文詳解全攻略

    這篇文章主要介紹了2024最新版Java?JDK安裝配置圖文詳解的相關(guān)資料,包含準(zhǔn)備工作、下載步驟、安裝指南及環(huán)境變量配置驗(yàn)證,供用戶輕松搭建Java開發(fā)環(huán)境,需要的朋友可以參考下
    2024-09-09
  • springboot如何集成mysql

    springboot如何集成mysql

    這篇文章主要介紹了springboot如何集成mysql問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Java中用戶向系統(tǒng)傳遞參數(shù)的三種基本方式實(shí)例分享

    Java中用戶向系統(tǒng)傳遞參數(shù)的三種基本方式實(shí)例分享

    這篇文章主要介紹了Java中用戶向系統(tǒng)傳遞參數(shù)的三種基本方式實(shí)例,有需要的朋友可以參考一下
    2014-01-01
  • Java Map如何根據(jù)key取value以及不指定key取出所有的value

    Java Map如何根據(jù)key取value以及不指定key取出所有的value

    這篇文章主要介紹了Java Map如何根據(jù)key取value以及不指定key取出所有的value,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • CountDownLatch源碼解析之a(chǎn)wait()

    CountDownLatch源碼解析之a(chǎn)wait()

    這篇文章主要為大家詳細(xì)解析了CountDownLatch源碼之a(chǎn)wait方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • SpringMvc配置靜態(tài)資源訪問路徑的實(shí)現(xiàn)

    SpringMvc配置靜態(tài)資源訪問路徑的實(shí)現(xiàn)

    本文主要介紹了SpringMvc配置靜態(tài)資源訪問路徑的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java負(fù)載均衡算法實(shí)現(xiàn)之輪詢和加權(quán)輪詢

    Java負(fù)載均衡算法實(shí)現(xiàn)之輪詢和加權(quán)輪詢

    網(wǎng)上找了不少負(fù)載均衡算法的資源,都不夠全面,后來自己結(jié)合了網(wǎng)上的一些算法實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于Java負(fù)載均衡算法實(shí)現(xiàn)之輪詢和加權(quán)輪詢的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • Java獲取指定父節(jié)點(diǎn)、子節(jié)點(diǎn)的方法實(shí)現(xiàn)

    Java獲取指定父節(jié)點(diǎn)、子節(jié)點(diǎn)的方法實(shí)現(xiàn)

    在Java中,要獲取指定節(jié)點(diǎn)的父節(jié)點(diǎn)和子節(jié)點(diǎn),通常需要使用 DOM,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • SpringSecurity集成第三方登錄過程詳解(最新推薦)

    SpringSecurity集成第三方登錄過程詳解(最新推薦)

    在ThirdAuthenticationFilter 類的attemptAuthentication()方法中,我們通過authType類型,然后創(chuàng)建對應(yīng)的Authentication實(shí)現(xiàn)來實(shí)現(xiàn)不同方式的登錄,下面給大家分享SpringSecurity集成第三方登錄過程,感興趣的朋友一起看看吧
    2024-05-05
  • Spring 日志規(guī)范及作用

    Spring 日志規(guī)范及作用

    日志是在系統(tǒng)運(yùn)行過程中關(guān)鍵的節(jié)點(diǎn)的數(shù),這個(gè)些日志的記錄方便當(dāng)系統(tǒng)出現(xiàn)問題方便問題查找,這篇文章主要介紹了Spring 日志規(guī)范及作用,需要的朋友可以參考下
    2024-03-03

最新評論

大邑县| 金塔县| 楚雄市| 龙岩市| 古浪县| 资阳市| 禄劝| 五河县| 郴州市| 景泰县| 林口县| 彰武县| 东至县| 佳木斯市| 永德县| 合作市| 浮山县| 金坛市| 临沂市| 高唐县| 兴国县| 寻乌县| 张家川| 舒城县| 政和县| 马公市| 囊谦县| 泰宁县| 洛浦县| 陇川县| 屯昌县| 新安县| 高邮市| 江达县| 安阳市| 台东市| 玉树县| 中阳县| 盐池县| 额尔古纳市| 荣昌县|