Spring @Cacheable 和@Async 注解
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
对象本身不感兴趣,但对代表计算的 CompletableFuture
感兴趣。
根据 SPR-12967,不支持 ListenableFuture
(CompletableFuture
)。
理论上只要
就可以用
@Cacheable
背后的 CacheManager 实现没有序列化缓存的对象(就像 Hazelcast 支持的缓存)
因为 CompletableFuture
持有一个状态,可以通过调用例如修改cancel()
方法,重要的是 API 的所有用户都不会乱用缓存的对象。否则,可能存在无法再检索 Future
中的缓存对象的风险,并且需要缓存逐出
验证注释后面的代理调用的顺序是值得的。即 @Cacheable
代理总是在 @Async
代理之前被调用吗?或者反过来?或者这取决于?例如,如果之前调用了 @Async
,它会在 ForkJoinPool
中触发 Callable
,然后从缓存中检索另一个对象。
社区让我做一些实验,所以我做了。我发现我的问题的答案很简单:如果 @Cacheable
和 @Async
放在同一个方法之上,它们就不能一起工作。
明确地说,我并不是在寻求一种方法来直接使缓存 return 成为 CompletableFuture
拥有的对象。这是不可能的,否则会破坏classclass.
的异步计算的契约。
正如我所说,这两个注释不能在同一个方法上一起工作。如果你仔细想想,这是显而易见的。用@Async
标记也是@Cacheable
的意思是将整个缓存管理委托给不同的异步线程。如果 CompletableFuture
的值的计算需要很长时间才能完成,缓存中的值将在该时间之后由 Spring Proxy 放置。
显然,有一个解决方法。解决方法使用 CompletableFuture
是一个 承诺 这一事实。让我们看看下面的代码。
@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
注释的方法中 return 它。该承诺将由另一个拥有标有 @Async
注释的方法的 bean 异步完成。
作为奖励,我还实现了一个不使用 Spring @Async
注释的解决方案,但它使用 CompletableFuture
class 中可用的工厂方法直接。
@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。
最后,上面是对JiraSPR-12967所指内容的长篇描述
希望对您有所帮助。
干杯。
在一个 class 中的方法上添加 @Async
注释,在另一个 class 中的方法级别添加 @Cacheable
注释。
然后从服务或任何不同的层调用 @Async
方法。
它对我有用,包括 Redis 缓存和异步,极大地提高了性能。
我尝试了以下方法,它似乎有效。
- 使用
@Cachable
创建一个执行实际业务逻辑的方法
- 使用
@Async
创建一个方法,它调用上面的 @Cachable
方法和 returns 一个 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。
Exp:
@Service
@Slf4j
@EnableAsync //Add it to here
public class CachingServiceImpl implements CachingService {
希望能帮到你!
我需要缓存一些异步计算的结果。具体来说,为了克服这个问题,我正在尝试使用 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
对象本身不感兴趣,但对代表计算的 CompletableFuture
感兴趣。
根据 SPR-12967,不支持 ListenableFuture
(CompletableFuture
)。
理论上只要
就可以用@Cacheable
背后的 CacheManager 实现没有序列化缓存的对象(就像 Hazelcast 支持的缓存)因为
CompletableFuture
持有一个状态,可以通过调用例如修改cancel()
方法,重要的是 API 的所有用户都不会乱用缓存的对象。否则,可能存在无法再检索Future
中的缓存对象的风险,并且需要缓存逐出验证注释后面的代理调用的顺序是值得的。即
@Cacheable
代理总是在@Async
代理之前被调用吗?或者反过来?或者这取决于?例如,如果之前调用了@Async
,它会在ForkJoinPool
中触发Callable
,然后从缓存中检索另一个对象。
社区让我做一些实验,所以我做了。我发现我的问题的答案很简单:如果 @Cacheable
和 @Async
放在同一个方法之上,它们就不能一起工作。
明确地说,我并不是在寻求一种方法来直接使缓存 return 成为 CompletableFuture
拥有的对象。这是不可能的,否则会破坏classclass.
正如我所说,这两个注释不能在同一个方法上一起工作。如果你仔细想想,这是显而易见的。用@Async
标记也是@Cacheable
的意思是将整个缓存管理委托给不同的异步线程。如果 CompletableFuture
的值的计算需要很长时间才能完成,缓存中的值将在该时间之后由 Spring Proxy 放置。
显然,有一个解决方法。解决方法使用 CompletableFuture
是一个 承诺 这一事实。让我们看看下面的代码。
@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
注释的方法中 return 它。该承诺将由另一个拥有标有 @Async
注释的方法的 bean 异步完成。
作为奖励,我还实现了一个不使用 Spring @Async
注释的解决方案,但它使用 CompletableFuture
class 中可用的工厂方法直接。
@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。
最后,上面是对JiraSPR-12967所指内容的长篇描述
希望对您有所帮助。 干杯。
在一个 class 中的方法上添加 @Async
注释,在另一个 class 中的方法级别添加 @Cacheable
注释。
然后从服务或任何不同的层调用 @Async
方法。
它对我有用,包括 Redis 缓存和异步,极大地提高了性能。
我尝试了以下方法,它似乎有效。
- 使用
@Cachable
创建一个执行实际业务逻辑的方法 - 使用
@Async
创建一个方法,它调用上面的@Cachable
方法和 returns 一个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。 Exp:
@Service
@Slf4j
@EnableAsync //Add it to here
public class CachingServiceImpl implements CachingService {
希望能帮到你!