@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);
そしてそれはうまくいきます。
私は何を間違っているのですか?
SettingsService
には2つのメソッドがあります。1つはキャッシュされます(getGlobalEnumValue(...)
)、もう1つはキャッシュされないが、もう1つのメソッドを呼び出します(getGlobalSettingsValue(...)
)。
ただし、Springキャッシュの抽象化が機能する方法は、クラスをプロキシすることです( Spring AOP を使用)。ただし、同じクラス内のメソッドを呼び出すと、プロキシされたロジックは呼び出されず、その下にある直接のビジネスロジックが呼び出されます。これは、同じBeanでメソッドを呼び出している場合、キャッシュが機能しないことを意味します。
したがって、getGlobalSettingsValue()
を呼び出している場合、そのメソッドがgetGlobalEnumValue(...)
を呼び出したときに、データが入力されたり、キャッシュが使用されたりすることはありません。
考えられる解決策は次のとおりです。
@EnableCaching(mode = AdviceMode.ASPECTJ)
を設定することでモードを切り替えることができます。ただし、 ロード時間ウィービングを設定する も行う必要があります。問題は、キャッシュ可能なメソッドを呼び出す場所にあります。同じクラスから@Cacheable
メソッドを呼び出す場合は、this
参照から呼び出すだけです。つまり、Springのプロキシによってラップされないため、Springは呼び出しをキャッチして処理できません。
この問題を解決する方法の1つは、それ自体に@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
を含むメソッドを専用クラスに移動することです。