2016-08-22 2 views
2

У меня проблемы с сохранением некоторых значений в методе @Service. Мой код:Весеннее кэширование загрузки в классе @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, если существуют данные,
  • если не сохранить значение из ENUM.

но он не сохранил данные из БД или перечисления.

Мой кэш конфигурации:

@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; 
    } 
} 

У меня есть пример, чтобы убедиться, что кэш работает в моем проекте в методе отдыха:

@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); 

и это работает хорошо.

Что я делаю wwrong?

ответ

4

У вас есть два метода в вашем SettingsService, который кэшируется (getGlobalEnumValue(...)) и другой, который не кэшируется, но вызывает другой метод (getGlobalSettingsValue(...)).

Способ, которым работает абстрактная абстракция Spring, если вы проксируете свой класс (используя Spring AOP). Однако вызовы методов внутри одного класса не будут вызывать прокси-логику, а прямую бизнес-логику ниже. Это означает, что кеширование не работает, если вы вызываете методы в одном и том же компоненте.

Итак, если вы звоните getGlobalSettingsValue(), он не будет заполнять и не использовать кеш, когда этот метод вызывает getGlobalEnumValue(...).


Возможные решения:

  1. Не вызывая другой метод в том же классе при использовании прокси
  2. Кэширование другой метод, а
  3. Использование AspectJ, а не Spring AOP, который ткет код непосредственно в байтовый код во время компиляции, а не проксирование класса. Вы можете переключить режим, установив @EnableCaching(mode = AdviceMode.ASPECTJ). Однако вам также понадобится set up load time weaving.
  4. Autowire сервис в вашу службу и использовать эту услугу, а не напрямую обращаться к методу. Авторизовав службу, вы вводите прокси-сервер в свою службу.
+0

Отличный ответ, спасибо! –

2

Проблема в том месте, которое вы называете своим методом кэширования. Когда вы вызываете свой метод @Cacheable из того же класса, вы просто вызываете его из ссылки this, что означает, что он не завершен прокси-сервером Spring, поэтому Spring не может поймать ваш вызов для его обработки.

Один на пути решения этой проблемы заключается в @Autowired службы к себе и просто вызывать методы вы ожидали весной должны обращаться по этой ссылке:

@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 в выделенный класс.

+0

Неужели автоувеличивание не вызывает рекурсивной проблемы? –

Смежные вопросы