Я был тоже заинтересован в этом. Извините, что я не нашел тривиального способа сделать это. Весна не сделает этого для вас, это скорее вопрос о том, может ли эта реализация кэша использовать упаковку. Я предполагаю, что вы используете реализацию EhCache. К сожалению, эта функциональность не выходит из коробки, насколько я знаю.
Существуют различные способы, можно достичь что-то подобное в зависимости от вашей проблемы
1) использовать вечное время кэша и имеют второй класс тему, которая периодически перебирает кэшированных данных освежающих его. Я не сделал это точно, но класс 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>
Фабрика: импорт net.sf.ehcache.event.CacheEventListener; import net.sf.ehcache.event.CacheEventListenerFactory; импорт java.util.Properties;
public class MyCacheEventListenerFactory extends CacheEventListenerFactory {
@Override
public CacheEventListener createCacheEventListener(Properties properties) {
return new CacheListener();
}
}
Псевдо-реализация импорта net.sf.ehcache.CacheException; import net.sf.ehcache.Ehcache; import 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 кластерной среде, где каждый из них сначала проверял базу данных, чтобы узнать, получили ли какие-либо другие кластеры «дорогие» данные сначала , а затем повторно использовали это, а не вызывали вызов сервера.
Небольшой вариант также должен был бы использовать второй метод @Cacheable вместе с @CachePut, а не БД для хранения данных. Но это означало бы удвоение использования памяти. Это может быть приемлемо в зависимости от ваших размеров результатов.
это звучит так, как будто на целевой сервер требуется 2 вызова, один из которых определяет, находится ли сервер, а второй - для получения фактических данных. 2 вызова означает, что сервер может опуститься посередине. – zibi
, возможно, использование какой-то пользовательской логики внутри этого метода может многое помочь, но в конце это не большое улучшение, как вы предложили в качестве основной альтернативы в своем вопросе :( –