web-dev-qa-db-ja.com

同じクラス内から呼び出されたときに、Springキャッシュの@Cacheableメソッドが無視される

同じクラス内から@Cacheableメソッドを呼び出そうとしています:

@Cacheable(value = "defaultCache", key = "#id")
public Person findPerson(int id) {
   return getSession().getPerson(id);
} 

public List<Person> findPersons(int[] ids) {
   List<Person> list = new ArrayList<Person>();
   for (int id : ids) {
      list.add(findPerson(id));
   }
   return list;
}

findPersonsからの結果もキャッシュされることを期待していますが、@Cacheableアノテーションは無視され、findPersonメソッドは毎回実行されます。

私はここで何か悪いことをしていますか、これは意図されていますか?

41
David Zhao

これは、Springでキャッシング、トランザクション関連の機能を処理するためにプロキシが作成される方法が原因です。これは、Springがそれを処理する方法の非常に優れたリファレンスです- トランザクション、キャッシュ、およびAOP:Springでのプロキシの使用法を理解する

つまり、セルフコールは動的プロキシをバイパスし、動的プロキシロジックの一部であるキャッシング、トランザクションなどの横断的な懸念もバイパスされます。

修正は、AspectJコンパイル時間またはロード時間ウィービングを使用することです。

45
Biju Kunjummen

これは、同じクラス内でメソッド呼び出しをほとんど使用しない小規模なプロジェクトで私が行うことです。コード内のドキュメントは、同僚にとっては苦痛に見えるかもしれないので、強く提供されています。しかし、そのテストは簡単で、簡単で、すぐに達成でき、本格的なAspectJインストゥルメンテーションを節約できます。ただし、より頻繁に使用する場合は、AspectJソリューションをお勧めします。

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {

    private final PersonDao _personDao;

    @Autowired
    public PersonDao(PersonDao personDao) {
        _personDao = personDao;
    }

    @Cacheable(value = "defaultCache", key = "#id")
    public Person findPerson(int id) {
        return getSession().getPerson(id);
    }

    public List<Person> findPersons(int[] ids) {
        List<Person> list = new ArrayList<Person>();
        for (int id : ids) {
            list.add(_personDao.findPerson(id));
        }
        return list;
    }
}
18
Mario Eis

Grails Spring Cacheプラグインを使用している人は、 回避策はドキュメントに記載されています 。 Grailsアプリでこの問題が発生しましたが、残念ながら、受け入れられた回答はGrailsでは使用できないようです。解決策は醜いですが、私見ですが、うまくいきます。

サンプルコードはそれをうまく示しています:

class ExampleService {
    def grailsApplication

    def nonCachedMethod() {
        grailsApplication.mainContext.exampleService.cachedMethod()
    }

    @Cacheable('cachedMethodCache')
    def cachedMethod() {
        // do some expensive stuff
    }
}

exampleService.cachedMethod()を独自のサービスとメソッドに置き換えるだけです。

2
Dónal Boyle