[英]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.