私もしばらくの間、これを行うことに興味がありました。申し訳ありませんが、これを行う簡単な方法は見つかりませんでした。Spring はこれを行いません。それは、Spring がラップしているキャッシュ実装がそれを実行できるかどうかという問題です。EhCache 実装を使用していると思います。残念ながら、私が知る限り、この機能はすぐには利用できません。
問題に応じて、同様のことを達成できるさまざまな方法があります
1)永遠のキャッシュ時間を使用し、キャッシュされたデータを定期的にループしてリフレッシュする第2クラスのスレッドを用意します。私はこれを正確には行っていませんが、Thread クラスは次のようにする必要があります。
@Autowired
EhCacheCacheManager ehCacheCacheManager;
...
//in the infinite loop
List keys = ((Ehcache) ehCacheCacheManager.getCache("test").getNative Cache()).getKeys();
for (int i = 0; i < keys.size(); i++) {
Object o = keys.get(i);
Ehcache ehcache = (Ehcache)ehCacheCacheManager.getCache("test").getNativeCache()
Element item = (ehcache).get(o);
//get the data based on some info in the value, and if no exceptions
ehcache.put(new Element(element.getKey(), newValue));
}
- 利点は、これが @Cacheable 呼び出し元にとって非常に高速であることです。欠点は、サーバーが必要以上のヒットを取得する可能性があることです。
2) CacheListener を作成してエビクション イベントをリッスンし、データを一時的に保存することができます。サーバー呼び出しが失敗した場合は、そのデータを使用してメソッドから戻ります。
ehcache.xml
<cacheEventListenerFactory class="caching.MyCacheEventListenerFactory"/>
</cache>
</ehcache>
ファクトリー: import net.sf.ehcache.event.CacheEventListener; net.sf.ehcache.event.CacheEventListenerFactory をインポートします。java.util.Properties をインポートします。
public class MyCacheEventListenerFactory extends CacheEventListenerFactory {
@Override
public CacheEventListener createCacheEventListener(Properties properties) {
return new CacheListener();
}
}
疑似実装 import net.sf.ehcache.CacheException; net.sf.ehcache.Ehcache をインポートします。net.sf.ehcache.Element をインポートします。net.sf.ehcache.event.CacheEventListener をインポートします。
import java.util.concurrent.ConcurrentHashMap;
public class CacheListener implements CacheEventListener {
//prob bad practice to use a global static here - but its just for demo purposes
public static ConcurrentHashMap myMap = new ConcurrentHashMap();
@Override
public void notifyElementPut(Ehcache ehcache, Element element) throws CacheException {
//we can remove it since the put happens after a method return
myMap.remove(element.getKey());
}
@Override
public void notifyElementExpired(Ehcache ehcache, Element element) {
//expired item, we should store this
myMap.put(element.getKey(), element.getValue());
}
//....
}
- ここでの課題は、キーがあまり役に立たないことです。サーバー呼び出しが失敗した場合に取得できるように、戻り値にキーに関する何かを格納する必要がある場合があります。これは少しハッキリしているように感じます。これが完全に防弾かどうかはわかりません。いくつかのテストが必要な場合があります。
3)多くの努力が必要ですが、うまくいきます:
@Cacheable("test")
public MyObject getValue(String data) {
try {
MyObject result = callServer(data);
storeResultSomewhereLikeADatabase(result);
} catch (Exception ex) {
return getStoredResult(data);
}
}
ここでの長所は、サーバーの再起動間で機能することであり、クラスター化されたサーバー間で共有キャッシュを許可するように単純に拡張できます. 12 のクラスター化された環境で、それぞれが最初にデータベースをチェックして、他のクラスターが最初に「高価な」データを取得したかどうかを確認し、サーバーを呼び出すのではなくそれを再利用するバージョンを持っていました。
わずかな変形として、DB ではなく @CachePut と一緒に 2 番目の @Cacheable メソッドを使用してデータを保存することもできます。しかし、これはメモリ使用量が 2 倍になることを意味します。結果のサイズによっては、それが許容される場合があります。