【问题标题】:Spring boot caching in @Service class does not work@Service 类中的 Spring Boot 缓存不起作用
【发布时间】:2016-12-28 13:46:46
【问题描述】:

我在 @Service 方法中保存一些值时遇到问题。 我的代码:

@Service(value = "SettingsService")
public class SettingsService {
...

    public String getGlobalSettingsValue(Settings setting) {
        getTotalEhCacheSize();
        if(!setting.getGlobal()){
            throw new IllegalStateException(setting.name() + " is not global setting");
        }
        GlobalSettings globalSettings = globalSettingsRepository.findBySetting(setting);
        if(globalSettings != null)
            return globalSettings.getValue();
        else
            return getGlobalEnumValue(setting)
    }

@Cacheable(value = "noTimeCache", key = "#setting.name()")
    public String getGlobalEnumValue(Settings setting) {
        return Settings.valueOf(setting.name()).getDefaultValue();
    }

我的存储库类:

@Repository
public interface GlobalSettingsRepository extends CrudRepository<GlobalSettings, Settings> {

    @Cacheable(value = "noTimeCache", key = "#setting.name()", unless="#result == null")
    GlobalSettings findBySetting(Settings setting);

它应该像这样工作:

  • 如果数据存在,则从 DB 中获取值,
  • 如果不保存枚举中的值。

但它没有从数据库或枚举中保存任何数据。

我的缓存配置:

@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public EhCacheCacheManager cacheManager(CacheManager cm) {
        return new EhCacheCacheManager(cm);
    }
    @Bean
    public EhCacheManagerFactoryBean ehcache() {
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));

        return  ehCacheManagerFactoryBean;
    }
}

我有一些例子来确保缓存在我的项目中以 rest 方法工作:

    @RequestMapping(value = "/system/status", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> systemStatus() {
        Object[] list = userPuzzleRepository.getAverageResponseByDateBetween(startDate, endDate);
...
}

public interface UserPuzzleRepository extends CrudRepository<UserPuzzle, Long> {
    @Cacheable(value = "averageTimeAnswer", key = "#startDate")
    @Query("select AVG(case when up.status='SUCCESS' OR up.status='FAILURE' OR up.status='TO_CHECK' then up.solvedTime else null end) from UserPuzzle up where up.solvedDate BETWEEN ?1 AND ?2")
    Object[] getAverageResponseByDateBetween(Timestamp startDate, Timestamp endDate);

而且效果很好。

我做错了什么?

【问题讨论】:

    标签: caching spring-boot ehcache


    【解决方案1】:

    SettingsService 中有两种方法,一种是缓存的 (getGlobalEnumValue(...)),另一种是未缓存但调用另一种方法 (getGlobalSettingsValue(...))。

    Spring 缓存抽象的工作方式是代理你的类(使用Spring AOP)。但是,对同一类中的方法的调用不会调用代理逻辑,而是调用下面的直接业务逻辑。这意味着如果您在同一个 bean 中调用方法,缓存将不起作用。

    因此,如果您调用 getGlobalSettingsValue(),当该方法调用 getGlobalEnumValue(...) 时,它不会填充,也不会使用缓存。


    可能的解决方案是:

    1. 使用代理时不调用同一类中的其他方法
    2. 缓存其他方法
    3. 使用 AspectJ 而不是 Spring AOP,后者在编译时将代码直接编织到字节码中,而不是代理类。您可以通过设置@EnableCaching(mode = AdviceMode.ASPECTJ) 来切换模式。但是,您也必须set up load time weaving
    4. 将服务自动装配到您的服务中,并使用该服务而不是直接调用该方法。通过自动装配服务,您可以将代理注入到您的服务中。

    【讨论】:

    • @g00glen00b 拯救了我的一天!
    • 尊敬的先生,您在调试这个问题 10 小时后给了我涅槃。我不知道为什么春季文档中没有提到这一点。
    • 非常感谢!您能否提供您是如何/在哪里找到解决方案的?
    【解决方案2】:

    问题出在您调用可缓存方法的地方。当您从同一个类调用 @Cacheable 方法时,您只需从 this 引用中调用它,这意味着它没有被 Spring 的代理包装,因此 Spring 无法捕获您的调用来处理它。

    解决此问题的方法之一是@Autowired 为自身服务,然后调用您期望 spring 必须通过此引用处理的方法:

    @Service(value = "SettingsService")
    public class SettingsService {
    //...
    
        @Autowired
        private SettingsService settingsService;
    //...
        public String getGlobalSettingsValue(Settings setting) {
           // ...
            return settingsSerive.getGlobalEnumValue(setting)
    //-----------------------^Look Here
        }
    
        @Cacheable(value = "noTimeCache", key = "#setting.name()")
        public String getGlobalEnumValue(Settings setting) {
            return Settings.valueOf(setting.name()).getDefaultValue();
        }
    }
    

    但是如果你有这样的问题,那就说明你的课承担的太多了,不符合“单一班级-单一职责”的原则。更好的解决方案是将带有@Cacheable 的方法移动到专用类。

    【讨论】:

    • 自动装配不会导致递归问题吗?
    猜你喜欢
    • 2018-04-11
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 2017-02-23
    • 2019-01-29
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    相关资源
    最近更新 更多