[英]Spring @Cacheable and @Async annotation
我需要緩存一些異步計算的結果。 具體來說,為了克服這個問題,我正在嘗試使用 Spring 4.3 緩存和異步計算功能。
例如,讓我們看下面的代碼:
@Service
class AsyncService {
@Async
@Cacheable("users")
CompletableFuture<User> findById(String usedId) {
// Some code that retrieves the user relative to id userId
return CompletableFuture.completedFuture(user);
}
}
是否可以? 我的意思是, Spring 的緩存抽象是否會正確處理CompletableFuture<User>
類型的對象? 我知道Caffeine Cache有類似的東西,但如果配置正確,我無法理解 Spring 是否使用它。
編輯:我對User
object 本身不感興趣,但對代表計算的CompletableFuture
感興趣。
社區要求我做一些實驗,所以我做了它們。 我發現我的問題的答案很簡單:如果@Cacheable
和@Async
放在同一個方法之上,它們就不能一起工作。
需要明確的是,我並不是在尋求一種方法來直接讓緩存返回CompletableFuture
擁有的對象。 這是不可能的,如果不是這樣,就會破壞CompletableFuture
類的異步計算契約。
正如我所說,這兩個注釋不能在同一個方法上一起工作。 如果你仔細想想,那就很明顯了。 用@Async
標記也是@Cacheable
手段,將整個緩存管理委托給不同的異步線程。 如果CompletableFuture
的值的計算需要很長時間才能完成,那么 Spring Proxy 將在該時間之后放置緩存中的值。
顯然,有一個解決方法。 解決方法使用CompletableFuture
是promise的事實。 讓我們看看下面的代碼。
@Component
public class CachedService {
/* Dependecies resolution code */
private final AsyncService service;
@Cacheable(cacheNames = "ints")
public CompletableFuture<Integer> randomIntUsingSpringAsync() throws InterruptedException {
final CompletableFuture<Integer> promise = new CompletableFuture<>();
// Letting an asynchronous method to complete the promise in the future
service.performTask(promise);
// Returning the promise immediately
return promise;
}
}
@Component
public class AsyncService {
@Async
void performTask(CompletableFuture<Integer> promise) throws InterruptedException {
Thread.sleep(2000);
// Completing the promise asynchronously
promise.complete(random.nextInt(1000));
}
}
訣竅是創建一個不完整的承諾並立即從標有@Cacheable
注釋的方法中返回它。 承諾將由另一個擁有用@Async
注釋標記的方法的 bean 異步完成。
作為獎勵,我還實現了一個不使用 Spring @Async
注釋的解決方案,但它直接使用CompletableFuture
類中可用的工廠方法。
@Cacheable(cacheNames = "ints1")
public CompletableFuture<Integer> randomIntNativelyAsync() throws
InterruptedException {
return CompletableFuture.supplyAsync(this::getAsyncInteger, executor);
}
private Integer getAsyncInteger() {
logger.info("Entering performTask");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return random.nextInt(1000);
}
無論如何,我分享了我的 GitHub 問題的完整解決方案spring-cacheable-async 。
最后,以上是對 Jira SPR-12967所指內容的詳細描述。
我希望它有幫助。 干杯。
根據SPR- ListenableFuture
, ListenableFuture
( CompletableFuture
) 不受支持。
添加@Async
在一類和方法的注釋@Cacheable
在方法級別批注在不同的類。
然后從服務或任何不同層調用@Async
方法。
它對我有用,Redis 緩存和異步,這極大地提高了性能。
理論上,只要
@Cacheable
后面的 CacheManager 實現不是序列化緩存對象(如 Hazelcast 支持的緩存)
由於CompletableFuture
持有一個狀態,它可以通過調用例如cancel()
方法進行修改,重要的是 API 的所有用戶都不會亂用緩存的對象。 否則,可能存在無法再檢索Future
中的緩存對象的風險,並且需要進行緩存驅逐
值得驗證調用注釋背后的代理的順序。 即@Cacheable
代理總是在@Async
之前@Async
嗎? 或者反過來? 還是看情況? 例如,如果之前調用了@Async
,它將在ForkJoinPool
觸發一個Callable
,然后從緩存中檢索另一個對象。
我嘗試了以下方法,它似乎有效。
@Cachable
創建一個方法,它執行實際的業務邏輯@Async
創建一個方法,它調用上面的@Cachable
方法並返回一個CompletableFuture
@Async
調用方法例子:
public class Main {
public void cachedAsyncData() {
try {
asyncFetcher.getData().get();
} catch(Exception e){}
}
}
public class AsyncFetcher {
@Async
public CompletableFuture<String> getData() {
return CompletableFuture.completedFuture(cacheFetcher.getData());
}
}
public class CacheFetcher {
@Cacheable
public String getData() {
return "DATA";
}
}
請在@Component 或@Serice class 級別添加注解@EnableAsync。 經驗值:
@Service
@Slf4j
@EnableAsync //Add it to here
public class CachingServiceImpl implements CachingService {
希望能幫到你!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.