2016-08-22 9 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を取得列挙型。

ただし、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; 
    } 
} 

私はキャッシュが残り方法の私のプロジェクトで作業していることを確認するためにいくつかの例があります。

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

をし、それがうまく機能しています。

私は何をしていますか?

答えて

4

SettingsServiceには、キャッシュされたもの(getGlobalEnumValue(...))とキャッシュされていないものの2つの方法がありますが、他の方法(getGlobalSettingsValue(...))を呼び出します。

ただし、クラスをプロキシすることによって(Spring AOPを使用して)、Springキャッシュ抽象化が機能します。しかし、同じクラス内のメソッドへの呼び出しは、プロキシロジックを呼び出すのではなく、直接のビジネスロジックを呼び出します。これは、同じBean内のメソッドを呼び出す場合、キャッシュが機能しないことを意味します。

したがって、getGlobalSettingsValue()を呼び出すと、そのメソッドがgetGlobalEnumValue(...)を呼び出すときにキャッシュが使用されず、キャッシュも使用されません。


可能な解決策は以下のとおりです。AspectJのではなく、編む春AOPを使用して

  • を、同様に他の方法をキャッシュ
  • プロキシ
  • を使用した場合

    1. は、同じクラスの別のメソッドを呼び出すことはありませんクラスをプロキシ処理するのではなく、コンパイル時にコードを直接バイトコードに挿入します。 @EnableCaching(mode = AdviceMode.ASPECTJ)を設定すると、モードを切り替えることができます。ただし、set up load time weavingにする必要があります。
    2. サービスをサービスに自動転送し、メソッドを直接呼び出すのではなく、そのサービスを使用します。サービスをオートワイヤリングすることにより、サービスにプロキシを挿入します。
  • +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

    再帰的な問題は発生しませんか? –

    関連する問題