欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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;

    // 默認構(gòu)造器(Spring Bean實例化需要)
    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) {
        // 耗時的郵件發(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)用失效問題:五種解決方案實戰(zhàn)分析_TaskExecutor

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

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

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

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

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)用同一個類中的@Async方法
            sendNotification(user, message);  // 問題:這里變成了同步調(diào)用!
        }

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

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

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

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

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

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

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

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

五種解決方案

方案 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) {
        // 實現(xiàn)同前...
    }
}

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

優(yōu)點

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

缺點

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

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

通過 Spring 的 ApplicationContext 手動獲取代理對象:

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) {
        // 實現(xiàn)同前...
    }
}

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

優(yōu)點

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

缺點

  • 增加了對 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) {
        // 實現(xiàn)同前...
    }
}

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

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

優(yōu)點

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

缺點

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

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

將異步方法拆分到單獨的服務(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) {
        // 模擬耗時操作
        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í)行的方法移動到專門的服務(wù)類中,然后通過依賴注入使用這個服務(wù)。這樣,調(diào)用總是通過 Spring 代理對象進行的。

優(yōu)點

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

缺點

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

方案 5:手動使用 TaskExecutor

完全放棄@Async 注解,手動使用 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) {
            // 手動提交任務(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é)果并組合多個異步操作
            CompletableFuture<Integer> processedFuture = future
                .thenApply(result -> result.length())  // 轉(zhuǎn)換結(jié)果
                .thenCombine(  // 組合另一個異步操作
                    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) {
        // 實現(xiàn)同前...
    }

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

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

優(yōu)點

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

缺點

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

針對返回值的異步方法

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

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;

    // 默認構(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;

    // 默認構(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);
        }

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

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

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

異常處理與實踐建議

異步方法的異常處理需要特別注意:異步執(zhí)行的方法拋出的異常不會傳播到調(diào)用方,因為異常發(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) {
            // 錯誤:無法捕獲異步方法的異常,因為異常發(fā)生在另一個線程
            // 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) {
            // 記錄詳細的異常信息,但異常不會傳播到調(diào)用方
            logger.error("通知失敗: " + user.getName(), e);
            // 異常會被AsyncUncaughtExceptionHandler處理(如果配置了)
            throw e;
        }
    }

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

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

實踐建議

  1. 合理配置線程池:默認情況下,Spring 使用SimpleAsyncTaskExecutor,每次調(diào)用都會創(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);     // 隊列容量
        executor.setThreadNamePrefix("MyAsync-");

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

        // 允許核心線程超時,適用于負載波動的場景
        executor.setAllowCoreThreadTimeOut(true);

        executor.initialize();
        return executor;
    }

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

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

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

    @Autowired
    private TransactionService self;

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

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

    @Async
    @Transactional // 必須單獨添加事務(wù)注解,會創(chuàng)建新的事務(wù)
    public void asyncOperation(Entity entity) {
        // 此方法將有自己的事務(wù),而非繼承調(diào)用方的事務(wù)
    }
}
  1. 驗證異步執(zhí)行
// 在測試類中驗證異步執(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);

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

五種方案對比

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

總結(jié)

解決方案

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

代碼侵入性

額外依賴

架構(gòu)清晰度

適用場景

自我注入

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

簡單項目,快速解決

ApplicationContext

ApplicationContext

需要明確控制代理獲取

AopContext

需開啟 exposeProxy

不想增加依賴字段

拆分服務(wù)

大型項目,關(guān)注點分離

手動 TaskExecutor

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

TaskExecutor

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

需靈活處理返回值

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

相關(guān)文章

  • Java 實現(xiàn)完整功能的學(xué)生管理系統(tǒng)實例

    Java 實現(xiàn)完整功能的學(xué)生管理系統(tǒng)實例

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java實現(xiàn)一個完整版學(xué)生管理系統(tǒng),大家可以在過程中查缺補漏,提升水平
    2021-11-11
  • 登錄EasyConnect后無法通過jdbc訪問服務(wù)器數(shù)據(jù)庫問題的解決方法

    登錄EasyConnect后無法通過jdbc訪問服務(wù)器數(shù)據(jù)庫問題的解決方法

    描述一下近期使用EasyConnect遇到的問題,下面這篇文章主要給大家介紹了關(guān)于登錄EasyConnect后無法通過jdbc訪問服務(wù)器數(shù)據(jù)庫問題的解決方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-02-02
  • java常用工具類 XML工具類、數(shù)據(jù)驗證工具類

    java常用工具類 XML工具類、數(shù)據(jù)驗證工具類

    這篇文章主要為大家詳細介紹了java常用工具類,包括XML工具類、數(shù)據(jù)驗證工具類,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • java打jar包的幾種方式詳解

    java打jar包的幾種方式詳解

    這篇文章主要介紹了java打jar包的幾種方式,本文分步驟給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-11-11
  • SpringBoot中忽略實體類中的某個屬性不返回給前端的方法(示例詳解)

    SpringBoot中忽略實體類中的某個屬性不返回給前端的方法(示例詳解)

    本文介紹了在Spring Boot中使用Jackson和Fastjson忽略實體類屬性不返回給前端的方法,在Jackson中,同時使用@JsonProperty和@JsonIgnore時,@JsonIgnore可能失效,Fastjson中可以使用@JSONField(serialize=false)來實現(xiàn),本文結(jié)合實例代碼介紹的非常詳細,需要的朋友參考下吧
    2024-11-11
  • Java實現(xiàn)簡易五子棋小游戲

    Java實現(xiàn)簡易五子棋小游戲

    這篇文章主要為大家詳細介紹了Java實現(xiàn)簡易五子棋小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 在Spring中使用Knife4j進行API文檔生成與管理的操作方法

    在Spring中使用Knife4j進行API文檔生成與管理的操作方法

    Knife4j 是為Java MVC 框架(如Spring Boot、Spring MVC等)集成 Swagger 生成 API 文檔的增強解決方案,它基于 Swagger 的核心功能,通過定制化的前端界面和一些額外的特性,本文介紹了在Spring中使用Knife4j進行API文檔生成與管理的操作方法,需要的朋友可以參考下
    2024-12-12
  • spring如何解決循環(huán)依賴問題

    spring如何解決循環(huán)依賴問題

    Spring在單例模式下用三級緩存設(shè)計解決setter方法注入bean屬性循環(huán)依賴問題,但無法解決多例Bean和構(gòu)造方法注入?yún)?shù)的循環(huán)依賴,三級緩存通過A、B兩對象互相注入屬性的過程解決循環(huán)依賴,其中,構(gòu)造方法的循環(huán)依賴無法解決是因為創(chuàng)建對象會走構(gòu)造方法
    2024-10-10
  • java的Arrays工具類實戰(zhàn)

    java的Arrays工具類實戰(zhàn)

    java.util.Arrays類能方便地操作數(shù)組,它提供的所有方法都是靜態(tài)的。Arrays作為一個工具類,能很好的操作數(shù)組。下面介紹主要使用的幾個函數(shù)
    2016-12-12
  • 解析Hibernate + MySQL中文亂碼問題

    解析Hibernate + MySQL中文亂碼問題

    如果持久化的類中有包括了漢字的String對象,那么對應(yīng)到數(shù)據(jù)庫中漢字的部分就會是亂碼。這主要是由于MySQL數(shù)據(jù)表的字符集與我們當(dāng)前使用的本地字符集不相同造成的
    2013-07-07

最新評論