Spring Data Redis为我们封装了Redis客户端的各种操作,简化使用。
关于RedisTemplate的使用可参考: blog.didispace.com/springbootr…
SpringBoot从1.4版本开始, spring-boot-starter-redis
依赖改名了。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>1.5.2.RELEASE</version> </dependency>
spring: redis: host: 127.0.0.1 port: 6379 timeout: 0 database: 0 pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 0
这样,SpringBoot将会自动配置redis,注入相关bean,我们就可以使用 @CacheConfig
, @Cacheable
, @CachePut
, @CacheEvict
了。
缓存对象集合中,缓存是以key-value形式保存的。当不指定缓存的key时,SpringBoot会使用 SimpleKeyGenerator
生成key。
public class SimpleKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { return generateKey(params); } /** * Generate a key based on the specified parameters. */ public static Object generateKey(Object... params) { if (params.length == 0) { return SimpleKey.EMPTY; } if (params.length == 1) { Object param = params[0]; if (param != null && !param.getClass().isArray()) { return param; } } return new SimpleKey(params); } }
public SimpleKey(Object... elements) { Assert.notNull(elements, "Elements must not be null"); this.params = new Object[elements.length]; System.arraycopy(elements, 0, this.params, 0, elements.length); this.hashCode = Arrays.deepHashCode(this.params); }
查看源码可以发现,它是使用方法参数组合生成的一个key。 此时有一个问题:
如果2个方法,参数是一样的,但执行逻辑不同,那么将会导致执行第二个方法时命中第一个方法的缓存。
解决办法是在 @Cacheable
注解参数中指定key,或者自己实现一个 KeyGenerator
,在注解中指定KeyGenerator。
但是如果这样的情况很多,每一个都要指定key、KeyGenerator很麻烦。
Spring同样提供了方案:继承 CachingConfigurerSupport
并重写 keyGenerator()
下面贴出代码:
@EnableCaching @Configuration public class RedisCacheConfig extends CachingConfigurerSupport { @Autowired private JedisConnectionFactory jedisConnectionFactory; @Bean public RedisTemplate redisTemplate() { StringRedisTemplate redisTemplate = new StringRedisTemplate(jedisConnectionFactory); Jackson2JsonRedisSerializer 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); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public CacheManager cacheManager() { String[] cacheNames = {"app_default", "users", "blogs", "goods", "configs", "info"}; RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate(), Arrays.asList(cacheNames)); redisCacheManager.setDefaultExpiration(86400); return redisCacheManager; } @Bean public Cache cache() { return cacheManager().getCache("app_default"); } @Bean @Override public KeyGenerator keyGenerator() { return (target, method, objects) -> { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append("::" + method.getName() + ":"); for (Object obj : objects) { sb.append(obj.toString()); } return sb.toString(); }; } }
此时,缓存的key是包名+方法名+参数列表,这样就很难会冲突了。