博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot In Practice (1):Redis缓存实战
阅读量:5876 次
发布时间:2019-06-19

本文共 5711 字,大约阅读时间需要 19 分钟。

阅读本文需要对Spring和Redis比较熟悉。

Spring Framework 提供了对缓存层进行了抽象封装,通过几个annotation可以透明给您的应用增加缓存支持,而不用去关心底层缓存具体由谁实现。目前支持的缓存有java.util.concurrent.ConcurrentMap,Ehcache 2.x,Redis等。

一般我们使用最常用的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//开启cachingpublic 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@Beanpublic RedisTemplate
redisTemplate(JedisConnectionFactory jedisConnectionFactory) { RedisTemplate
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.class));复制代码

如果我们应用中有大量对象需要缓存,这显然是不合适的,而前者直接把类型信息序列化到了JSON格式中,让一个实例可以操作多个对象的反序列化。

定制RedisCacheManager

有时候Spring Boot自动给我们配置的RedisCacheManager也不能满足我们应用的需求,我看到很多用法都直接声明了一个自己的RedisCacheManager,其实使用CacheManagerCustomizer可以对自动配置的RedisCacheManager进行定制化:

@Bean    public CacheManagerCustomizer
cacheManagerCustomizer() { return new CacheManagerCustomizer
() { @Override public void customize(RedisCacheManager cacheManager) { cacheManager.setUsePrefix(true); //事实上这是Spring Boot的默认设置,为了避免key冲突 Map
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生成方案,方式有两种:

1.实现自己的KeyGenerator;

@Configuration@EnableCachingpublic 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();          }        };   }}复制代码

2.在@Cacheable标注中直接声明key:

@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的声明形式支持。

❶ 最终生成的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

@Servicepublic class FeedService {    @Resource(name="redisTemplate") ❶    private ZSetOperations
feedOp; public List
getFeed(int count, long maxId) { return new ArrayList<>(feedOp.reverseRangeByScore(FEED_CACHE_KEY, 0, maxId, offset, count)); } //省略}复制代码

❶ 我们可以直接把RedisTemplate的实例注入为ZSetOperationsListOperationsValueOperations等类型(Spring IoC Container帮我们做了转化工作,可以参考org.springframework.data.redis.core.ZSetOperationsEditor)。

除了当缓存,Redis还能干啥

org.springframework.data.redis.support包中提供了一些以Redis作为后端存储的组件,包括原子计数器和Java Collections的一些实现类。

@Servicepublic class ActivityService {    RedisAtomicInteger counter;    public ActivityService(RedisConnectionFactory connectionFactory) {        counter = new RedisAtomicInteger(counterKey,                connectionFactory, 1);    }}复制代码

以上代码创建了一个分布式的原子计数器。

III. 参考

#coding/spring

转载地址:http://nckix.baihongyu.com/

你可能感兴趣的文章
java查找string1和string2是不是含有相同的字母种类和数量(string1是否是string2的重新组合)...
查看>>
Android TabActivity使用方法
查看>>
Eclipse的 window-->preferences里面没有Android选项
查看>>
《麦田里的守望者》--[美]杰罗姆·大卫·塞林格
查看>>
遇到的那些坑
查看>>
央行下属的上海资信网络金融征信系统(NFCS)签约机构数量突破800家
查看>>
[转] Lazy evaluation
查看>>
常用查找算法总结
查看>>
被神话的大数据——从大数据(big data)到深度数据(deep data)思维转变
查看>>
修改校准申请遇到的问题
查看>>
Linux 进程中 Stop, Park, Freeze【转】
查看>>
文件缓存
查看>>
远程协助
查看>>
Scrum实施日记 - 一切从零开始
查看>>
关于存储过程实例
查看>>
配置错误定义了重复的“system.web.extensions/scripting/scriptResourceHandler” 解决办法...
查看>>
AIX 7.1 install python
查看>>
PHP盛宴——经常使用函数集锦
查看>>
重写 Ext.form.field 扩展功能
查看>>
Linux下的搜索查找命令的详解(locate)
查看>>