1. Введение
В последней статье было представлено использование SpringCache и Redis для установки кеша, но аннотация SpringCache не поддерживает установку времени кеша, что на самом деле является головной болью. Эта статья расскажет вам, как использовать самый простой способ решить SpringCache и Redis, чтобы установить кеш и установить время кеша.Эта статья основана на предыдущем блоге. Если вы ничего не понимаете, посмотрите предыдущий блог. Ссылка на предыдущую статью:Элегантное решение для кэширования — интеграция SpringCache и Redis (SpringBoot)
2. Конфигурация
Аннотация @Cacheable не поддерживает настройку времени истечения.Все, что вам нужно, это настроить время истечения по умолчанию, настроив CacheManneg и настроив время истечения кэша для каждого класса или метода.
решать Следующая информация о конфигурации может быть использована для решения проблемы установки информации о конфигурации времени истечения срока действия.
Изменить класс конфигурации
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.io.Serializable;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: MaoLin
* @Date: 2019/3/26 17:04
* @Version 1.0
*/
@Configuration
@EnableCaching
public class RedisConfig implements Serializable {
/**
* 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
* 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值
*/
/* @Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.create(redisConnectionFactory);
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
// 创建一个模板类
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
// 将刚才的redis连接工厂设置到模板类中
template.setConnectionFactory(factory);
// 设置key的序列化器
template.setKeySerializer(new StringRedisSerializer());
// 设置value的序列化器
//使用Jackson 2,将对象序列化为JSON
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//json转对象类,不设置默认的会将json转成hashmap
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
return template;
}*/
/**
* 最新版,设置redis缓存过期时间
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), this.getRedisCacheConfigurationWithTtl( 60), this.getRedisCacheConfigurationMap() // 指定 key 策略
);
}
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
//SsoCache和BasicDataCache进行过期时间配置
redisCacheConfigurationMap.put("messagCache", this.getRedisCacheConfigurationWithTtl(30 * 60)); redisCacheConfigurationMap.put("userCache", this.getRedisCacheConfigurationWithTtl(60));//自定义设置缓存时间
return redisCacheConfigurationMap;
}
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(jackson2JsonRedisSerializer)
).entryTtl(Duration.ofSeconds(seconds));
return redisCacheConfiguration;
}
}
контрольная работа
- Установите имя кеша и время кеша (следующее 60 секунд)
redisCacheConfigurationMap.put("userCache",this.getRedisCacheConfigurationWithTtl(60));
- использовать
Просто добавьте примечание
@Cacheable("userCache")
Примечание. Имя — это имя userCache, заданное в классе конфигурации, и можно задать несколько имен и времени кэширования.
Тестовый класс контроллера
import com.ml.demo.dao.UserDao;
import com.ml.demo.entity.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.Serializable;
/**
* @Author: MaoLin
* @Date: 2019/3/26 17:03
* @Version 1.0
*/
@RestController
public class testController implements Serializable {
@Resource
private UserDao userDao;
/**
* 查询出一条数据并且添加到缓存
*
* @param userId
* @return
*/
@RequestMapping("/getUser")
@Cacheable("userCache")
public User getUser(@RequestParam(required = true) String userId) {
System.out.println("如果没有缓存,就会调用下面方法,如果有缓存,则直接输出,不会输出此段话");
return userDao.getUser(Integer.parseInt(userId));
}
/**
* 删除一个缓存
*
* @param userId
* @return
*/
@RequestMapping(value = "/deleteUser")
@CacheEvict("userCache")
public String deleteUser(@RequestParam(required = true) String userId) {
return "删除成功";
}
/**
* 添加一条保存的数据到缓存,缓存的key是当前user的id
*
* @param user
* @return
*/
@RequestMapping("/saveUser")
@CachePut(value = "userCache", key = "#result.userId +''")
public User saveUser(User user) {
return user;
}
/**
* 返回结果userPassword中含有nocache字符串就不缓存
*
* @param userId
* @return
*/
@RequestMapping("/getUser2")
@CachePut(value = "userCache", unless = "#result.userPassword.contains('nocache')")
public User getUser2(@RequestParam(required = true) String userId) {
System.out.println("如果走到这里说明,说明缓存没有生效!");
User user = new User(Integer.parseInt(userId), "name_nocache" + userId, "nocache");
return user;
}
@RequestMapping("/getUser3")
@Cacheable(value = "userCache", key = "#root.targetClass.getName() + #root.methodName + #userId")
public User getUser3(@RequestParam(required = true) String userId) {
System.out.println("如果第二次没有走到这里说明缓存被添加了");
return userDao.getUser(Integer.parseInt(userId));
}
}
Тестовый запуск и результаты
-
сохранить кеш
-
Кэш просмотра
-
просмотреть редис
-
Срок действия кэша истекает через одну минуту
-
Запросить кеш еще раз
-
Результат работы консоли
3. Разрешение ошибок
2019-03-31 14:21:05.163 ERROR 17056 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `com.ml.demo.entity.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (byte[])"["com.ml.demo.entity.User",{"userId":11,"userName":"\"张三\"","userPassword":"123"}]"; line: 1, column: 29]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.ml.demo.entity.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (byte[])"["com.ml.demo.entity.User",{"userId":11,"userName":"\"张三\"","userPassword":"123"}]"; line: 1, column: 29]] with root cause
На решение этой ошибки ушло много времени, но проблема на самом деле очень проста.
причина:
Причина в том, что я добавил конструктор в класс сущности для удобства создания экземпляра класса, чтобы JVM не добавляла конструктор без аргументов по умолчанию, а для десериализации Джексона требуется конструктор без аргументов, поэтому сообщается об ошибке.
Класс сущности Response аналогичен.
решать:
Просто добавьте конструктор без параметров в класс сущности.
public User() {}
Резюме и ссылки
резюме
На самом деле это хороший способ использовать механизм кэширования (объект), предоставляемый Spring, для реализации кэширования в сочетании с Redis, но нет возможности установить время кэширования, что очень негуманно. RedisTemplate и StringRedisTemplate Оба этих класса поддерживают настройку времени кэширования.Если вы считаете, что использование SpringCache неудобно, вы можете использовать класс RedisTemplate, чтобы настроить класс инструментов Redis для реализации кэширования.