内容简介:Spring Boot In Practice (1):Redis 缓存实战
阅读本文需要对Spring和 Redis 比较熟悉。
Spring Framework 提供了Cache Abstraction对缓存层进行了抽象封装,通过几个annotation可以透明给您的应用增加缓存支持,而不用去关心底层缓存具体由谁实现。目前支持的缓存有 java.util.concurrent.ConcurrentMap
,Ehcache 2.x,Redis等。
一般我们使用最常用的Redis做为缓存实现(Spring Data Redis),
-
需要引入的 starter
:
spring-boot-starter-data-redis
,spring-boot-starter-cache
; -
自动配置生成的Beans:
RedisConnectionFactory
,StringRedisTemplate
,RedisTemplate
,RedisCacheManager
,自动配置的Bean可以直接注入我们的代码中使用;
I. 配置
application.properties
# REDIS (RedisProperties) spring.redis.host=localhost # Redis server host. spring.redis.port=6379 # Redis server port. spring.redis.password= # Login password of the redis server.
具体对Redis cluster或者Sentinel的配置可以参考 这里
开启缓存支持
@SpringBootApplication @EnableCaching//开启caching public class NewsWebServer { //省略内容 }
定制RedisTemplate
自动配置的 RedisTemplate
并不能满足大部分项目的需求,比如我们基本都需要设置特定的 Serializer
(RedisTemplate默认会使用 JdkSerializationRedisSerializer
)。
Redis底层中存储的数据只是字节。虽然Redis本身支持各种类型(List, Hash等),但在大多数情况下,这些指的是数据的存储方式,而不是它所代表的内容(内容都是byte)。用户自己来决定数据如何被转换成String或任何其他对象。用户(自定义)类型和原始数据类型之间的 互相转换 通过RedisSerializer接口(包org.springframework.data.redis.serializer)来处理,顾名思义,它负责处理序列化/反序列化过程。多个实现可以开箱即用,如:StringRedisSerializer和JdkSerializationRedisSerialize。Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer来处理JSON格式的数据。请注意,存储格式不仅限于value 它可以用于key,Hash的key和value。
声明自己的 RedisTemplate
覆盖掉自动配置的Bean:
//通用的RedisTemplate @Bean public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(jedisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //template.setHashKeySerializer(template.getKeySerializer()); //template.setHashValueSerializer(template.getValueSerializer()); return template; }
这里我们使用 GenericJackson2JsonRedisSerializer
而不是 Jackson2JsonRedisSerializer
,后者的问题是你需要为每一个需要序列化进Redis的类指定一个 Jackson2JsonRedisSerializer
因为其构造函数中需要指定一个类型来做反序列化:
redis.setValueSerializer(new Jackson2JsonRedisSerializer<Product>(Product.class));
如果我们应用中有大量对象需要缓存,这显然是不合适的,而前者直接把类型信息序列化到了JSON格式中,让一个实例可以操作多个对象的反序列化。
定制RedisCacheManager
有时候Spring Boot自动给我们配置的 RedisCacheManager
也不能满足我们应用的需求,我看到很多用法都直接声明了一个自己的RedisCacheManager,其实使用 CacheManagerCustomizer
可以对自动配置的RedisCacheManager进行定制化:
@Bean public CacheManagerCustomizer<RedisCacheManager> cacheManagerCustomizer() { return new CacheManagerCustomizer<RedisCacheManager>() { @Override public void customize(RedisCacheManager cacheManager) { cacheManager.setUsePrefix(true); //事实上这是Spring Boot的默认设置,为了避免key冲突 Map<String, Long> expires = new HashMap<>(); expires.put("myLittleCache", 12L*60*60); // 设置过期时间 key is cache-name expires.put("myBiggerCache", 24L*60*60); cacheManager.setExpires(expires); // expire per cache cacheManager.setDefaultExpiration(24*60*60);// 默认过期时间:24 hours } }; }
II. 使用
缓存Key的生成
我们都知道Redis是一个key-value的存储系统,无论我们想要缓存什么值,都需要制定一个key。
@Cacheable(cacheNames = "user") public User findById(long id) { return userMapper.findById(id); }
上面的代码中, findById
方法返回的对象会被缓存起来,key由默认的 org.springframework.cache.interceptor.SimpleKeyGenerator
生成,生成策略是根据被标注方法的参数生成一个 SimpleKey
对象,然后由 RedisTemplate
中定义的KeySerializer序列化后作为key(注意 StringRedisSerializer
只能序列化String类型,对 SimpleKey
对象无能为力,你只能定义其他Serializer)。
不过大多数情况下我们都会采用自己的key生成方案,方式有两种:
-
实现自己的KeyGenerator;
@Configuration @EnableCaching public class CacheConfig extends CachingConfigurerSupport { @Bean public KeyGenerator customKeyGenerator() { return new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... objects) { StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append(method.getName()); for (Object obj : objects) { sb.append(obj.toString()); } return sb.toString(); } }; } }
-
在
@Cacheable
标注中直接声明key:
```java
@Cacheable(cacheNames = "user", key="#id.toString()") ❶
public User findById(long id) {
return userMapper.findById(id);
}
@Cacheable(cacheNames = "user", key="'admin'") ❷
public User findAdmin() {
return userMapper.findAdminUser();
}
@Cacheable(cacheNames = "user", key="#userId + ':address'") ❸
public List
findUserAddress(long userId) {
return userMapper.findUserAddress(userId);
}
key的声明形式支持[SpEL](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html)。 ❶ 最终生成的Redis key为:`user:100234`,user部分是因为`cacheManager.setUsePrefix(true)`,cacheName会被添加到key作为前缀避免引起key的冲突。之所以`#id.toString()`要long型转为String是因为我们设置的KeySerializer为`StringRedisSerializer`只能用来序列化String。 ❷ 如果被标注方法没有参数,我们可以用一个静态的key值,最终生成的key为`user:admin`。 ❸ 最终生成的key为`user:100234:address`。 这种方式更符合我们以前使用Redis的习惯,所以推荐。 ### 直接使用RedisTemplate 有时候标注不能满足我们的使用场景,我们想要直接使用更底层的`RedisTemplate`。 ```java @Service public class FeedService { @Resource(name="redisTemplate") ❶ private ZSetOperations<String, Feed> feedOp; public List<Feed> getFeed(int count, long maxId) { return new ArrayList<>(feedOp.reverseRangeByScore(FEED_CACHE_KEY, 0, maxId, offset, count)); } //省略 }
❶ 我们可以直接把RedisTemplate的实例注入为 ZSetOperations
、 ListOperations
、 ValueOperations
等类型(Spring IoC Container帮我们做了转化工作,可以参考 org.springframework.data.redis.core.ZSetOperationsEditor
)。
除了当缓存,Redis还能干啥
org.springframework.data.redis.support
包中提供了一些以Redis作为后端存储的组件,包括原子计数器和Java Collections的一些实现类。
@Service public class ActivityService { RedisAtomicInteger counter; public ActivityService(RedisConnectionFactory connectionFactory) { counter = new RedisAtomicInteger(counterKey, connectionFactory, 1); } }
以上代码创建了一个分布式的原子计数器。
III. 参考
#coding/spring
以上所述就是小编给大家介绍的《Spring Boot In Practice (1):Redis 缓存实战》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- SpringBoot 缓存实战
- Viewer模型加载本地缓存实战
- SpringBoot 实战 (十一) | 整合数据缓存 Cache
- [实战验证] http缓存(无代理服务器)
- EVCache缓存在 Spring Boot中的实战
- 原 荐 缓存架构之借助消息中间件RabbitMQ实现Redis缓存实时更新实战演练
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Code Reading
Diomidis Spinellis / Addison-Wesley Professional / 2003-06-06 / USD 64.99
This book is a unique and essential reference that focuses upon the reading and comprehension of existing software code. While code reading is an important task faced by the vast majority of students,......一起来看看 《Code Reading》 这本书的介绍吧!