Слияние кода завершено, страница обновится автоматически
Множественные уровни кэширования.
redis ehcache
1) Maven зависимость
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Кэширование Ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.3.0</version>
</dependency>
2) Конфигурация класса
```3) Конфигурация xml
ehcache.xml
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">
<!-- Путь к папке для хранения данных -->
<persistence directory="D:/ehcacheData"/>
<!-- Шаблон кэша, для демонстрации его использования, можно не использовать шаблон и задать параметры напрямую в кэше -->
<cache-template name="template">
<key-type>java.lang.String</key-type>
<value-type>java.lang.String</value-type>
<expiry>
<!-- Единица измерения по умолчанию - секунды, если используется секунды, можно не указывать -->
<ttl unit="hours">1</ttl>
</expiry>
<resources>
<!-- Единица измерения по умолчанию - записи, если используется записи, можно не указывать -->
<heap>1</heap>
<offheap unit="MB">1</offheap>
<!-- persistent по умолчанию - false, можно не указывать -->
<disk unit="MB">20</disk>
</resources>
</cache-template>
<!-- Объект кэша, если используется шаблон, он перезапишет параметры шаблона, используя uses-template="" для ссылки на шаблон -->
<cache alias="defaultCache" uses-template="template">
<expiry>
<!-- Здесь перезапишет параметры шаблона (TTL) -->
<tti>60</tti>
</expiry>
<resources>
<disk unit="MB" persistent="true">500</disk>
</resources>
<!-- Не исследовал эту часть, пока оставлю без внимания
<eviction-advisor></eviction-advisor>
-->
</cache>
</config>
```Конфигурация класса для загрузки:
```java
System.out.println("[Инициализация конфигурации Ehcache<начало>]");
// Установка параметров по умолчанию для кэша
```cacheManager = CacheManagerBuilder.newCacheManager(new XmlConfiguration(getClass().getResource("/ehcache.xml")));
cacheManager.init();
System.out.println("[Инициализация конфигурации Ehcache завершена]");
Конфигурация в application.yml:
spring:
cache:
ehcache:
config: ehcache.xml
@Service
public class EhCacheService {
@Cacheable(value = "test")
public String readCache() {
System.out.println("Если это не выполнено, значит данные были кэшированы");
return "1234";
}
@CachePut(value = "test")
public String update() {
// Обновление кэша, что изменит содержимое
System.out.println("Кэш обновлен");
return "456";
}
}
``` @CacheEvict(value="test",allEntries = true)
public void deleteAll() {
System.out.println("Все кэши удалены");
}
}
6) Код контроллера
@RestController
@RequestMapping("/ehcache")
public class EhCacheController {
@Autowired
public EhCacheService ehCacheService;
@GetMapping("/read")
public String readEhCache(){
return ehCacheService.readCache();
}
@GetMapping("/deleteAll")
public String deleteAll(){
ehCacheService.deleteAll();
return "Удаление выполнено";
}```java
@GetMapping("/update")
public String update(){
ehCacheService.update();
return "Обновление выполнено";
}
}
1) Maven зависимости
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Необходимые зависимости для интеграции Redis в Spring 2.0 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
2) Конфигурационный класс
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Slf4j
public class RedisCacheConfiguration {
/**
* Кэш-шаблон
* @param lettuceConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(lettuceConnectionFactory);
return template;
}
}
``````markdown
/**
* Кэш-менеджер
* @param lettuceConnectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(lettuceConnectionFactory);
@SuppressWarnings("serial")
Set<String> cacheNames = new HashSet<String>() {
{
add("codeNameCache");
}
};
builder.initialCacheNames(cacheNames);
return builder.build();
}
}
3) Конфигурационный файл
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
# Время ожидания подключения (в миллисекундах)
spring.redis.timeout=10000
# По умолчанию Redis имеет 16 сегментов, здесь указывается конкретный сегмент
spring.redis.database=0
```# Максимальное количество подключений в пуле (отрицательное значение означает отсутствие ограничений) по умолчанию 8
spring.redis.lettuce.pool.max-active=8
# Максимальное время ожидания подключения в пуле (отрицательное значение означает отсутствие ограничений) по умолчанию -1
spring.redis.lettuce.pool.max-wait=-1
# Максимальное количество пустых подключений в пуле по умолчанию 8
spring.redis.lettuce.pool.max-idle=8
# Минимальное количество пустых подключений в пуле по умолчанию 0
spring.redis.lettuce.pool.min-idle=0
4) Тестовый класс
// TODO Проверка потоковой безопасности
ExecutorService executorService = Executors.newFixedThreadPool(1000);
IntStream.range(0, 1000).forEach(i ->
executorService.execute(() -> stringRedisTemplate.opsForValue().increment("kk", 1))
);
stringRedisTemplate.opsForValue().set("k1", "v1");
final String k1 = stringRedisTemplate.opsForValue().get("k1");
log.info("[Результат кэширования строк] - [{}]", k1);
// TODO Поддерживаемые Redis команды
String key = "battcn:user:1";
redisCacheTemplate.opsForValue().set(key, new User(1L, "u1", "pa"));
// TODO Соответствует String (строка)
final User user = (User) redisCacheTemplate.opsForValue().get(key);
log.info("[Результат кэширования объекта] - [{}]", user);
}
1) Класс сообщения
@Data
public class CacheMessage implements Serializable {
private static final long serialVersionUID = 5987219310442078193L;
private String cacheName;
private Object key;
private Integer sender;
public CacheMessage(String cacheName, Object key) {
super();
this.cacheName = cacheName;
this.key = key;
}
public CacheMessage(String cacheName, Object key, Integer sender) {
super();
this.cacheName = cacheName;
this.key = key;
this.sender = sender;
}
}
```2) Пользовательский класс кэширования
@Slf4j
public class RedisEhcacheCache extends AbstractValueAdaptingCache {
private String name;
private RedisTemplate<Object, Object> redisTemplate;
private Cache<Object, Object> ehcacheCache;
private String cachePrefix;
private long defaultExpiration = 0;
private Map<String, Long> expires;
private String topic = "cache:redis:ehcache:topic";
protected RedisEhcacheCache(boolean allowNullValues) {
super(allowNullValues);
} public RedisEhcacheCache(String name, RedisTemplate<Object, Object> redisTemplate, Cache<Object, Object> ehcacheCache, RedisEhcacheProperties redisEhcacheProperties) {
super(redisEhcacheProperties.isCacheNullValues());
this.name = name;
this.redisTemplate = redisTemplate;
this.ehcacheCache = ehcacheCache;
this.cachePrefix = redisEhcacheProperties.getCachePrefix();
this.defaultExpiration = redisEhcacheProperties.getRedis().getDefaultExpiration();
this.expires = redisEhcacheProperties.getRedis().getExpires();
this.topic = redisEhcacheProperties.getRedis().getTopic();
log.info("Пользовательский кэшировщик " + name);
}
@Override
public String getName() {
return this.name;
} @Override
public Object getNativeCache() {
return this;
}
}
```java
@SuppressWarnings("unchecked")
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
log.info("Вызов пользовательского метода GET для кэша");
Object value = lookup(key);
if (value != null) {
return (T) value;
}
ReentrantLock lock = new ReentrantLock();
try {
lock.lock();
value = lookup(key);
if (value != null) {
return (T) value;
}
value = valueLoader.call();
Object storeValue = toStoreValue(valueLoader.call());
put(key, storeValue);
return (T) value;
} catch (Exception e) {
try {
Class<?> c = Class.forName("org.springframework.cache.Cache$ValueRetrievalException");
Constructor<?> constructor = c.getConstructor(Object.class, Callable.class, Throwable.class);
RuntimeException exception = (RuntimeException) constructor.newInstance(key, valueLoader, e.getCause());
throw exception;
} catch (Exception e1) {
throw new IllegalStateException(e1);
}
} finally {
lock.unlock();
}
}
// Чтение значения value из устойчивого слоя и сохранение его в кэше. Разрешено значение value = null
@Override
public void put(Object key, Object value) {
log.info("Вызов пользовательского метода кэширования PUT для ключа " + getKey(key));
if (!super.isAllowNullValues() && value == null) {
this.evict(key);
return;
}
long expire = getExpire();
if (expire > 0) {
redisTemplate.opsForValue().set(getKey(key), toStoreValue(value), expire, TimeUnit.MILLISECONDS);
} else {
redisTemplate.opsForValue().set(getKey(key), toStoreValue(value));
}
}
``` // Отправка сообщения через redis для сброса кэша других служб.
// Недостаток оригинального метода: после помещения KV в кэш службой 1 и отправки сообщения через redis,
// служба 1 получает это сообщение и удаляет только что помещённое KV.
// Здесь передаётся хэш-код ehcacheCache, чтобы избежать этой проблемы.
push(new CacheMessage(this.name, key, this.ehcacheCache.hashCode()));
ehcacheCache.put(key, value);
}
// Генерация ключа name:cachePrefix:key
private Object getKey(Object key) {
log.info("Вызов пользовательского метода получения ключа");
return this.name.concat(":").concat(StringUtils.isEmpty(cachePrefix) ? key.toString() : cachePrefix.concat(":").concat(key.toString()));
} private long getExpire() {
log.info("Вызов пользовательского метода получения времени жизни");
long expire = defaultExpiration;
Long cacheNameExpire = expires.get(this.name);
return cacheNameExpire == null ? expire : cacheNameExpire.longValue();
} @Override
public ValueWrapper putIfAbsent(Object key, Object value) {
Object cacheKey = getKey(key);
Object prevValue = null;
// Рассмотреть использование распределенного блокирования или замену redis.setIfAbsent на атомарную операцию
synchronized (key) {
prevValue = redisTemplate.opsForValue().get(cacheKey);
if(prevValue == null) {
long expire = getExpire();
if(expire > 0) {
redisTemplate.opsForValue().set(getKey(key), toStoreValue(value), expire, TimeUnit.MILLISECONDS);
} else {
redisTemplate.opsForValue().set(getKey(key), toStoreValue(value));
}
//
push(new CacheMessage(this.name, key, this.ehcacheCache.hashCode()));
//
ehcacheCache.put(key, toStoreValue(value));
}
}
return toValueWrapper(prevValue);
} @Override
public void evict(Object key) {
log.info("Вызов метода evict для пользовательского кэша");
// Сначала удаляем данные из кэша Redis, затем из кэша Ehcache, чтобы избежать ситуации, когда в краткосрочной перспективе запросы будут перезагружать данные из Redis в Ehcache
redisTemplate.delete(getKey(key));
push(new CacheMessage(this.name, key, this.ehcacheCache.hashCode()));
//
ehcacheCache.remove(key);
} @Override
public void clear() {
// Сначала удаляем данные из кэша Redis, затем из кэша Ehcache, чтобы избежать ситуации, когда в краткосрочной перспективе запросы будут перезагружать данные из Redis в Ehcache
Set<Object> keys = redisTemplate.keys(this.name.concat(":"));
for (Object key : keys) {
redisTemplate.delete(key);
}
push(new CacheMessage(this.name, null));
ehcacheCache.clear();
}
// Получение значения из кэша по ключу, если значение отсутствует, то требуется чтение из постоянного хранилища
@Override
protected Object lookup(Object key) {
Object cacheKey = getKey(key);
Object value = ehcacheCache.get(key);
if (value != null) {
log.info("Получение значения из кэша Ehcache, ключ: {}", cacheKey);
return value;
}
value = redisTemplate.opsForValue().get(cacheKey);
log.info("Значение: {}", value);
log.info("Ключ: {}", cacheKey);
if (value != null) {
log.info("Получение значения из кэша Redis и добавление в кэш Ehcache, ключ: {}", cacheKey);
// Перенос данных из вторичного кэша в первичный кэш. Принцип: ключи, недавно использовавшиеся, вероятно, будут использоваться снова
ehcacheCache.put(cacheKey, value);
}
return value;
} /**
* При изменении кэша используем функцию подписки на сообщения Redis для уведомления других узлов о необходимости очистки локального кэша.
* @description
* @param message
*/
private void push(CacheMessage message) {
redisTemplate.convertAndSend(topic, message);
} /**
* @description Очистка локального кэша
* @param key
*/
public void clearLocal(Object key) {
log.info("Очистка локального кэша, этот ключ: {}", key);
if (key == null) {
ehcacheCache.clear();
} else {
ehcacheCache.remove(key);
}
}
public Cache<Object, Object> getLocalCache() {
return ehcacheCache;
}
}
3) Класс-слушатель сообщений Redis
@Slf4j
public class CacheMessageListener implements MessageListener {
private RedisTemplate<Object, Object> redisTemplate;
private RedisEhcacheCacheManager redisEhcacheCacheManager;
public CacheMessageListener(RedisTemplate<Object, Object> redisTemplate,
RedisEhcacheCacheManager redisEhcacheCacheManager) {
super();
this.redisTemplate = redisTemplate;
this.redisEhcacheCacheManager = redisEhcacheCacheManager;
log.info("Отслеживание кэша");
}
} @Override
public void onMessage(Message message, byte[] pattern) {
CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());
log.info("Получено сообщение Redis, имя кеша: {}, ключ: {}", cacheMessage.getCacheName(), cacheMessage.getKey());
// Очистка кеша ehcache
redisEhcacheCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey(), cacheMessage.getSender());
}
}
4) Пользовательский класс управления кешем
@Slf4j
public class RedisEhcacheCacheManager implements CacheManager {
private ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();
private RedisEhcacheProperties redisEhcacheProperties;
private RedisTemplate<Object, Object> redisTemplate;
private boolean dynamic = true;
private Set<String> cacheNames;
} private org.ehcache.CacheManager ehCacheManager;
private CacheConfiguration configuration; public RedisEhcacheCacheManager(RedisEhcacheProperties redisEhcacheProperties,
RedisTemplate<Object, Object> redisTemplate) {
super();
this.redisEhcacheProperties = redisEhcacheProperties;
this.redisTemplate = redisTemplate;
this.dynamic = redisEhcacheProperties.isDynamic();
this.cacheNames = redisEhcacheProperties.getCacheNames();
setAboutEhCache();
}
/**
* Настройка кеша EhCache
*/
private void setAboutEhCache(){
long ehcacheExpire = redisEhcacheProperties.getEhcache().getExpireAfterWrite();
this.configuration =
CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(redisEhcacheProperties.getEhcache().getMaxEntry()))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcacheExpire)))
.build();
this.ehCacheManager = CacheManagerBuilder
.newCacheManagerBuilder()
.build();
this.ehCacheManager.init();
log.info("Настройка кеша");
}
/**
* Получить кэш
* @param name
* @return
*/
@Override
public Cache getCache(String name) {
Cache cache = cacheMap.get(name);
if(cache != null) {
return cache;
}
if(!dynamic && !cacheNames.contains(name)) {
return cache;
}
cache = new RedisEhcacheCache(name, redisTemplate, getEhcache(name), redisEhcacheProperties);
Cache oldCache = cacheMap.putIfAbsent(name, cache);
log.debug("Создан экземпляр кэша, имя кэша : {}", name);
return oldCache == null ? cache : oldCache;
}
/**
* Получение кэша Ehcache
* @param name
* @return
*/
public org.ehcache.Cache<Object, Object> getEhcache(String name){
org.ehcache.Cache<Object, Object> res = ehCacheManager.getCache(name, Object.class, Object.class);
if(res != null){
return res;
}
return ehCacheManager.createCache(name, configuration);
} @Override
public Collection<String> getCacheNames() {
return this.cacheNames;
}
public void clearLocal(String cacheName, Object key, Integer sender) {
Cache cache = cacheMap.get(cacheName);
if (cache == null) {
return;
}
RedisEhcacheCache redisEhcacheCache = (RedisEhcacheCache) cache;
// Если это сообщение от самого отправителя, ключ не удаляется
if (redisEhcacheCache.getLocalCache().hashCode() != sender) {
redisEhcacheCache.clearLocal(key);
}
}
}
5) Класс конфигурации кэша CacheRedisEhcacheAutoConfiguration
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
// Переключение между вторичным кэшем и первичным: true - включить вторичный кэш, false - отключить вторичный кэш
@ConditionalOnProperty(name = "cache.use2L", havingValue = "true", matchIfMissing = false)
@EnableConfigurationProperties(RedisEhcacheProperties.class)
@Slf4j
public class CacheRedisEhcacheAutoConfiguration {
@Autowired
private RedisEhcacheProperties redisEhcacheProperties;```markdown
/**
* Кэш-менеджер
*
* @param lettuceConnectionFactory
* @return
*/
@Bean
public RedisEhcacheCacheManager cacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
return new RedisEhcacheCacheManager(redisEhcacheProperties, redisCacheTemplate(lettuceConnectionFactory));
}
/**
* Отслеживание RedisMessage
* @param redisTemplate
* @param redisEhcacheCacheManager
* @return
*/
@Bean
@ConditionalOnBean(RedisEhcacheCacheManager.class)
public RedisMessageListenerContainer redisMessageListenerContainer(RedisTemplate<Object, Object> redisTemplate,
RedisEhcacheCacheManager redisEhcacheCacheManager) {
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
redisMessageListenerContainer.setConnectionFactory(redisTemplate.getConnectionFactory());
CacheMessageListener cacheMessageListener = new CacheMessageListener(redisTemplate, redisEhcacheCacheManager);
// Добавление слушателя кэша
log.info("3. Добавление слушателя кэша в Redis");
redisMessageListenerContainer.addMessageListener(cacheMessageListener, new ChannelTopic(redisEhcacheProperties.getRedis().getTopic()));
return redisMessageListenerContainer;
}
}
6) Конфигурация Redis RedisCacheConfig
@Configuration
@EnableCaching // Включение аннотаций
@ConditionalOnProperty(name = "cache.use2L", havingValue = "false", matchIfMissing = true)
@EnableConfigurationProperties(RedisEhcacheProperties.class)
@Slf4j
public class RedisCacheConfig {
/**
* Кэш-шаблон
*
* @param lettuceConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(lettuceConnectionFactory);
return template;
}
}
``` public CacheManager cacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(lettuceConnectionFactory);
return builder.build();
}
}
7)Конфигурационный файл EhCache
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false" name="ec"> <diskStore path="java.io.tmpdir"/> <!-- Директория для хранения кэша (по умолчанию используется системная директория для временных файлов), также может быть "D:/cache" или "java.io.tmpdir" -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/> <! -- -->
<cache name="j2CacheRedis"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="140"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<! --
name: Уникальный идентификатор кеша
maxElementsInMemory: Максимальное количество объектов кеша в памяти
maxElementsOnDisk: Максимальное количество объектов кеша на диске, если значение равно 0, то ограничение отсутствует
eternal: Определяет, являются ли элементы вечными, если установлено значение true, то параметр timeout не применяется
overflowToDisk: Если количество элементов в памяти достигает maxElementsInMemory, то элементы будут записаны на диск
timeToIdleSeconds: Время до истечения срока действия элемента, если элемент не является вечным. Необязательный параметр, значение по умолчанию равно 0, что означает бесконечное время до истечения срока действия
timeToLiveSeconds: Время до истечения срока действия элемента, если элемент не является вечным. Значение по умолчанию равно 0, что означает бесконечное время до истечения срока действия
diskPersistent: Сохранение данных кеша при перезагрузке виртуальной машины
diskExpiryThreadIntervalSeconds: Интервал времени выполнения потока проверки истечения срока действия на диске, значение по умолчанию равно 120 секундам
diskSpoolBufferSizeMB: Размер буфера кеша на диске. Значение по умолчанию равно 30 МБ. Каждый кеш должен иметь свой собственный буфер
``` memoryStoreEvictionPolicy: Политика очистки памяти при достижении ограничения maxElementsInMemory. Значение по умолчанию равно LRU (Least Recently Used). Можно установить значение FIFO (First In First Out) или LFU (Least Frequently Used)
</ehcache>
8) Файл конфигурации свойств
spring.redis.hostName=127.0.0.1
spring.redis.port=6379
spring.redis.timeOut=1000
spring.redis.maxIdle=10
spring.redis.maxWaitMillis=15000
spring.redis.testOnBorrow=true
spring.redis.testWhileIdle=false
```## Настройка redis-starter```spring.cache.cache-names=cache1,cache2,cache3
spring.redis.timeout=10000
#Пользовательские настройки. expire задан в миллисекундах
cache.multi.cacheNames=cache1,cache2,cache3
cache.multi.ehcache.expireAfterWrite=5000
cache.multi.ehcache.maxEntry=1000
cache.multi.redis.defaultExpiration=60000
cache.multi.redis.expires.cache1=50000
cache.multi.redis.expires.cache2=70000
cache.multi.redis.expires.cache3=70000
#Включение двухуровневого кэширования
cache.use2L=true
9) Тестирование
@Component
@Slf4j
public class CacheTestService {
//cacheManager = "cacheManager" можно не указывать
@Cacheable(value = "gerritCache", key = "#root.targetClass + '_' + #root.methodName + '_' + #root.args[0]", cacheManager = "cacheManager")
public String get(long id) {
log.info("Получение данных из базы данных");
return "test";
}
}
public class CacheTestServiceTest extends SpringbootCacheApplicationTests {
@Autowired
private CacheTestService cacheTestService;
@Test
public void get0() throws Exception {
cacheTestService.get(123L);
}
}
Описание:
Если параметр cache.use2L=true установлен в true,
то будет включен кэширование Ehcache. Если параметр установлен в false, то будет использоваться только кэширование Redis.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )