我目前正在为生产中的一些方法实现Spring缓存.它与使用@GetMapping的REST控制器方法一起工作得很好,该方法调用用@Cacheable注释的服务方法.

然而,当我try 在Groovy脚本中实现相同的缓存机制时,它似乎不起作用.ChatGPT认为,这可能是因为服务中用@Cacheable注释的方法调用发生在组件的构造函数中.

为了进一步研究,我创建了一个测试Spring Boot应用程序,并try 从用@PostConstruct注释的另一个Bean的方法中调用一个@Cacheable的测试方法,但不幸的是,它仍然不起作用.在阅读了几本手册和文档后,我怀疑没有创建代理或类似的东西.

以下是我的测试应用程序的代码:

package test.spring.springtest;

import jakarta.annotation.PostConstruct;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringTestApplication {
    private final JavaStarter javaStarter;

    public SpringTestApplication(JavaStarter javaStarter) {
        this.javaStarter = javaStarter;
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringTestApplication.class, args);
    }

    @PostConstruct
    public void invokeJavaStarterMethod() {
        javaStarter.invokeTestMethod("test");
    }
}
package test.spring.springtest.config;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("cache");
    }
}
package test.spring.springtest;

import org.springframework.stereotype.Component;
import test.spring.springtest.service.JavaService;

@Component
public class JavaStarter {

    private final JavaService javaService;

    JavaStarter(JavaService javaService) {
        this.javaService = javaService;
    }

    public void invokeTestMethod(String input) {
        for (int i = 0; i < 3; i++) {
            System.out.println("Output: " + javaService.testMethod(input));
            System.out.println();
        }
    }
}
package test.spring.springtest.service;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class JavaService {
    @Cacheable(cacheNames = "cache")
    public String testMethod(String input) {
        System.out.println("Cache is not being used");
        return input;
    }
}

您能解释一下为什么缓存行为没有像预期的那样工作吗?

在我的项目中,我决定在我的Spring Boot应用程序中使用@Cacheable注释实现缓存.当我将其应用于调用用@Cacheable注释的服务方法的REST控制器方法时,缓存工作得很好.

然而,当我试图在Groovy脚本中使用相同的缓存机制时,它没有达到预期的效果.我甚至try 用@PostConstruct从另一个bean的方法调用一个测试方法,但这也没有解决问题.

我花时间研究了各种手册和文档以找到解决方案,但我找不到问题的根本原因.似乎存在缓存行为不能像预期的那样工作的原因.这可能与我如何使用@Cacheable注释或它如何与其他组件交互有关.

推荐答案

它实际上确实像预期的那样工作.

@SpringBootApplication是(间接但最终)@Configuration级.这些都是在这个过程的非常早的时候处理的.当您将它注入到@Configuration类中时,没有发生过对Bean的代理(因为我们仍然处于读取配置阶段),因此不会发生缓存.

如果您添加一个调用该方法的ApplicationRunner,就可以看到这一点.

package biz.deinum.stackoverflow.caching;

import jakarta.annotation.PostConstruct;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableCaching
public class StackoverflowCachingApplication {

  private final JavaStarter javaStarter;

  public StackoverflowCachingApplication(JavaStarter javaStarter) {
    this.javaStarter = javaStarter;
  }

  public static void main(String[] args) {
    SpringApplication.run(StackoverflowCachingApplication.class, args);
  }

  @Bean
  public ApplicationRunner invoker(JavaStarter starter) {
    return (args) -> starter.invokeTestMethod("test");
  }

  @PostConstruct
  public void invokeJavaStarterMethod() {
    javaStarter.invokeTestMethod("test");
  }
}

如果运行此命令,输出将如下所示

2024-03-04T16:05:36.663+01:00  INFO 3105 --- [           main] b.d.s.c.StackoverflowCachingApplication  : Starting StackoverflowCachingApplication using Java 23-ea with PID 3105 (/Users/BV38BY/Downloads/stackoverflow-caching/target/classes started by BV38BY in /Users/BV38BY/Downloads/stackoverflow-caching)
2024-03-04T16:05:36.664+01:00  INFO 3105 --- [           main] b.d.s.c.StackoverflowCachingApplication  : No active profile set, falling back to 1 default profile: "default"
Cache is not being used
Output: test

Cache is not being used
Output: test

Cache is not being used
Output: test

2024-03-04T16:05:36.920+01:00  INFO 3105 --- [           main] b.d.s.c.StackoverflowCachingApplication  : Started StackoverflowCachingApplication in 0.385 seconds (process running for 0.618)
Cache is not being used
Output: test

Output: test

Output: test

如您所见,第一个输出来自@PostConstruct,也打印在Starter ... 行之前.在那之后调用来自ApplicationRunner的那个,并且已经正确地设置了缓存和代理.

Java相关问答推荐

我想了解Java中的模块化.编译我的应用程序时,我有一个ResolutionException

为什么一个java函数会返回一个作为参数传递给它的对象?

存根基类的受保护方法

在JavaFX项目中注册组合框的控件FX验证器时,模块系统出错

Spark上下文在向Spark提交数据集时具有内容,但Spark在实际构建它时发现它为空

测试期间未执行开放重写方法

无法使用Java&;TestContainers获取AWS SQS队列的属性

我如何知道MediaDiscoverer何时完成发现介质?

我可以在MacOS上使用什么Java函数来在适当的设备上以适当的音量播放适当的alert 声音?

%This内置函数示例

没有使用Lombok生成的参数

try 使用Spring集成和MySQL实现发件箱模式时,锁定等待超时

从Spring6中的JPMS模块读取类时出现问题

每次我需要时创建和关闭数据库连接会有什么效果吗?

Java集合:NPE,即使没有添加空值

当我将鼠标悬停在javafxTextArea上时,如何更改鼠标光标?

";重复键的值提示唯一约束«;livre_genre_pkey»";例外

@此处不能应用可为null的批注

java.exe如何执行java源代码?

如何使用 Java 替换位于特定标记内的 XML 标记的 CDATA 内的值