Java Spring@Cacheable和@Async注释

Java Spring@Cacheable和@Async注释,java,spring,spring-cache,completable-future,spring-async,Java,Spring,Spring Cache,Completable Future,Spring Async,我需要缓存一些异步计算的结果。具体来说,为了克服这个问题,我尝试使用Spring4.3缓存和异步计算特性 作为一个示例,让我们以以下代码为例: @Service class AsyncService { @Async @Cacheable("users") CompletableFuture<User> findById(String usedId) { // Some code that retrieves the user relative

我需要缓存一些异步计算的结果。具体来说,为了克服这个问题,我尝试使用Spring4.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);
    }
}
@服务
类异步服务{
@异步的
@可缓存(“用户”)
CompletableFuture findById(字符串usedId){
//某些代码检索相对于userId userId的用户
返回CompletableFuture.completedFuture(用户);
}
}
可能吗?我的意思是,Spring的缓存抽象能否正确处理
CompletableFuture
类型的对象?我知道它有类似的功能,但如果配置正确,我无法理解Spring是否会使用它


编辑:我对
用户
对象本身不感兴趣,而是对表示计算的
CompletableFuture
感兴趣。

根据,
ListenableFuture
CompletableFuture
)不受支持。

理论上,只要

  • @Cacheable
    背后的CacheManager实现不是序列化缓存对象(如Hazelcast支持的缓存)

  • 由于
    CompletableFuture
    持有一种状态,可以通过调用
    cancel()
    方法来修改该状态,因此API的所有用户都不要乱动缓存的对象,这一点很重要。否则,
    未来
    中的缓存对象可能无法再被检索,因此有必要进行缓存逐出

  • 值得验证注释后面的代理的调用顺序。i、 e.
    @Cacheable
    代理是否总是在
    @Async
    代理之前调用?还是反过来?还是视情况而定?例如,如果之前调用了
    @Async
    ,它将在
    ForkJoinPool
    中触发一个
    可调用的
    ,然后从缓存中检索另一个对象


    • 社区要求我做一些实验,所以我做了。我发现我的问题的答案很简单:
      @Cacheable
      @Async
      如果放在同一个方法之上,它们就不能一起工作

      明确地说,我并不是在寻求一种直接使缓存返回
      CompletableFuture所拥有的对象的方法。这是不可能的,如果不是这样,它将打破
      CompletableFuture
      类异步计算的契约

      正如我所说的,这两个注释在同一个方法上不起作用。如果你仔细想想,这是显而易见的。用
      @Async
      标记也是
      @Cacheable
      的意思是将整个缓存管理委托给不同的异步线程。如果
      CompletableFuture
      值的计算需要很长时间才能完成,则Spring代理会将缓存中的值放置在该时间之后

      显然,有一个解决办法。解决方案使用了这样一个事实:
      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));
          }
      }
      
      无论如何,我分享了我的GitHub问题的完整解决方案

      最后,以上是对Jira所指内容的详细描述

      我希望有帮助。
      干杯。

      在一个类中的方法上添加
      @Async
      注释,并在另一个类中的方法级别添加
      @Cacheable
      注释

      然后从服务或任何不同的层调用
      @Async
      方法


      Redis cache和Async对我来说都很有效,这大大提高了性能。

      我尝试了下面的方法,似乎很有效

      • 使用
        @cacable
        创建一个方法,该方法执行实际的业务逻辑
      • 使用
        @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";
          }
      }
      
      公共类主{
      public void cachedAsyncData(){
      试一试{
      asyncFetcher.getData().get();
      }捕获(例外e){}
      }
      }
      公共类异步获取程序{
      @异步的
      公共CompletableFuture getData(){
      返回CompletableFuture.completedFuture(cacheFetcher.getData());
      }
      }
      公共类缓存获取程序{
      @可缓存
      公共字符串getData(){
      返回“数据”;
      }
      }
      
      感谢您的回复。在你链接的Jira中,有人问了一个不同的问题。据我所知,在Jira中,用户要求缓存
      ListenableFuture
      的内部值。我认为只要
      CompletableFuture
      遵守
      Object
      的契约,它就可以成功缓存,不是吗?你试过了吗?我这几天不得不试一下@Kayaman,我会让你知道我的测试结果的。很好。我一直在等着看是否有人在这里接球。我自己在谷歌上搜索了一下,但只找到了
      @Async
      任务中的
      未来与
      兼容未来
      ,这些任务在
      4.2
      @Kayaman中得到了修复。我用我的结果给出了答案。希望有帮助。不,它不起作用。今天我做了一些实验。明天我会发布这些实验的结果。我投票支持你尝试,但你所做的看起来像是一个黑客。放弃
      Cacheable
      注释,直接使用缓存管理器会更清晰。或者注册一个这样的回调。
      @Cacheable
      注释下的机制非常有用。我使用的是Spring,所以我尝试按照它的理念来做事情。嗨@Rohit,欢迎来到Stack Overflow。据我所知,这类似于我们在项目中实施的解决方案。不过,我认为最好添加一些代码来说明您的解决方案,并在描述中更加具体。
      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";
          }
      }