spring boot 集成 redis

2333

记录一下

一、关于 spring-redis

spring-data-redis 针对 jedis 提供了如下功能:

  1. 连接池自动管理,提供了一个高度封装的 RedisTemplate 类
  2. 针对 jedis 客户端中大量 api 进行了归类封装,将同一类型操作封装为 operation 接口
ValueOperation: 简单 K-V 操作
SetOperation: set类型数据操作
ZSetOperations: zset 类型数据操作
HashOperations: 针对 map 类型的数据操作
ListOperations: 针对 list 类型的数据操作

RedisTemplate中定义了5种数据结构操作

  • redisTemplate.opsForValue(): 操作字符串
  • redisTemplate.opsForHash(): 操作hash
  • redisTemplate.opsForList(): 操作list
  • redisTemplate.opsForSet(): 操作set
  • redisTemplate.opsForZSet(): 操作有序set

二、Maven依赖

本文所采用的是 SpringBoot2.X 的版本,在 pom 中引入 redis 及其 jar 包

<!-- spring boot redis 缓存引入 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.1.5.RELEASE</version> </dependency> <!-- lettuce pool 缓存连接池 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- jackson json 优化缓存对象序列化 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> <!-- jackson-databind 依赖以下2个包,不添加的情况下会报异常 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.10.2</version> </dependency>

三、application.properties中加入redis相关配置

redis 目前常用的连接池有jedis 和 lettuce,这里我使用的是 lettuce 连接池。

# Redis服务器地址
spring.redis.host=xxxxx
# Redis端口
spring.redis.port=6379
# Redis 密码
spring.redis.password=xxxxx
# Redis数据库索引(默认为0)
spring.redis.database=3
# 连接池最大连接数(使用负值表示没有限制) 
spring.redis.lettuce.pool.max-active=10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=4
# 连接超时时间(毫秒)
spring.redis.timeout=4000

使用 spring boot redis 默认的配置类,你会发现通过 redis 客户端链接查看完全是乱码的,阅读性非常差。
redis默认链接.png

四、自定义 redis 配置类

1)、关于 redisTemplate 的自动配置
源码

@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }

通过源码可以看出,SpringBoot自动帮我们在容器中生成了一个 RedisTemplate 和一个 StringRedisTemplate。但是这个 RedisTemplate 的泛型是 <Object, Object>,写代码不方便,需要写好多类型转换的代码,我们需要一个泛型为 <String, Object> 形式的 RedisTemplate。并且这个 RedisTemplate 没有设置数据存在Redis时,key及value的序列化方式。

看到这个 @ConditionalOnMissingBean 注解后,就知道如果 Spring 容器中有了 RedisTemplate 对象了,这个自动装配的 RedisTemplate 不会实例化,因此我们可以直接自己写个配置类,配置RedisTemplate。

2)、既然自动配置不好用,就重新配置一个 RedisTemplate
代码如下:

@EnableCaching @Configuration public class RedisConfig extends CachingConfigurerSupport { /** * 自定义缓存 key 的生成策略,默认的生成策略是看不懂的(乱码内容), * 通过 Spring 的依赖注入特性进行自定义的配置注入,并且此类是一个配置类可以更多程度的自定义配置 * @return */ @Override public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); } }; } /** * 缓存配置管理器 * @param factory * @return */ @Bean public CacheManager cacheManager(LettuceConnectionFactory factory) { // 以锁写入的方式创建 RedisCacheWriter 对象 RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory); /** * 设置 CacheManager 的 value 序列化方式为 JdkSericalizationRedisSerializer * 但是其实 RedisCacheConfiguration 默认就是使用 StringRedisSerializer 序列化key, JdkSerializationRedisSerializer 序列化 value */ // 创建默认缓存配置对象 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); RedisCacheManager cacheManager = new RedisCacheManager(writer, config); return cacheManager; } /** * 获取还操作助手对象 * @param factory * @return */ @Bean public RedisTemplate<String, String> redisTemplate(LettuceConnectionFactory factory) { // 创建 redis 缓存操作助手 RedisTemplate 对象 StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(factory); /** * 以下代码将 RedisTemplate 的 value 序列化方式由 JdkSerializationRedisSerializer 更换为 Jackson2JsonRedisSerializer * 此种序列化方式结果清晰、容易阅读、存储字节少、速度快 */ Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); // StringRedisTemplate 是 RedisTempLate<String, String> 的子类 return template; } }

3)、封装的 RedisUtils 工具类
①首先我们需要需要封装一个 SpringUtils 工具类用来获取 RedisTemplate bean 对象(PS: new 出来的对象和 Spring 管理的 bean 对象并不是同一个)

@Component public final class SpringUtils implements BeanFactoryPostProcessor { /** Spring应用上下文环境 */ private static ConfigurableListableBeanFactory beanFactory; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { SpringUtils.beanFactory = beanFactory; } /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws org.springframework.beans.BeansException * */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) throws BeansException{ return (T) beanFactory.getBean(name); } /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws org.springframework.beans.BeansException * */ public static <T> T getBean(Class<T> clz) throws BeansException{ T result = (T) beanFactory.getBean(clz); return result; } }

② 把常用的 redis 工具方法统一封装

public class RedisUtils { private RedisUtils() { } /** * Redis 模板 * redisTemplate 不能通过new获取,需要从 spring 容器中得到,这里是通过 static 静态代码块初始化 */ private static RedisTemplate<String, Object> redisTemplate; static { redisTemplate = SpringUtils.getBean("redisTemplate"); } /** * 设置有效时间 * * @param key Redis 键 * @param timeout 超时时间 * @param unit 时间单位 * @return true = 设置成功;false = 设置失败 */ public static boolean expire(final String key, final long timeout, final TimeUnit unit) { Boolean ret = redisTemplate.expire(key, timeout, unit); return ret != null && ret; } /** * 删除单个key * * @param key 键 * @return true=删除成功; false = 删除失败 */ public static boolean delKey(final String key) { Boolean ret = redisTemplate.delete(key); return ret != null && ret; } /** * 删除多个key * * @param keys 键集合 * @return 成功删除的个数 */ public static long delKeys(final Collection<String> keys) { Long ret = redisTemplate.delete(keys); return ret == null ? 0 : ret; } /** ================ 存储普通对象操作 ================== **/ /** * 存入普通对象 * * @param key 建 * @param value 值 */ public static void setValue(final String key, final Object value) { redisTemplate.opsForValue().set(key, value, 1, TimeUnit.MINUTES); } /** * 存入普通对象 * * @param key 建 * @param value 值 * @param timeout 有效期,单位秒 */ public static void setValueTimeout(final String key, final Object value, final long timeout) { redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS); } /** * 获取普通对象 * * @param key 建 * @return 返回值(对象) */ public static Object getValue(final String key) { return redisTemplate.opsForValue().get(key); } /** ================ 存储Hash操作 ================== **/ /** * 确定哈希hasKey是否存在 * * @param key Redis键 * @param hkey hash键 * @return true=存在;false=不存在 */ public static boolean hasHashKey(final String key, String hkey) { Boolean ret = redisTemplate.opsForHash().hasKey(key, hkey); return ret != null && ret; } /** * 往Hash中存入数据 * * @param key key Redis键 * @param hKey hKey Hash键 * @param value value 值 */ public static void hashPut(final String key, final String hKey, final Object value) { redisTemplate.opsForHash().put(key, hKey, value); } /** * 往Hash中存入多个数据 * * @param key Redis键 * @param values Hash键值对 */ public static void hashPutAll(final String key, final Map<String, Object> values) { redisTemplate.opsForHash().putAll(key, values); } /** * 获取Hash中的数据 * * @param key key Redis键 * @param hKey hKey Hash键 * @return Hash中的对象 */ public static Object hashGet(final String key, final String hKey) { return redisTemplate.opsForHash().get(key, hKey); } /** * 获取Hash中的数据 * * @param key Redis键 * @return Hash对象 */ public static Map<Object, Object> hashGetAll(final String key) { return redisTemplate.opsForHash().entries(key); } /** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ public static List<Object> hashMultiGet(final String key, final Collection<Object> hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } /** * 删除Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ public static long hashDeleteKeys(final String key, final Collection<Object> hKeys) { return redisTemplate.opsForHash().delete(key, hKeys); } /** ================ 存储Set操作 ================== **/ /** * 往Set中存入数据 * * @param key Redis键 * @param values 值 * @return 存入的个数 */ public static long setSet(final String key, final Object... values) { Long count = redisTemplate.opsForSet().add(key, values); return count == null ? 0 : count; } /** * 删除Set中的数据 * * @param key Redis键 * @param values 值 * @return 移除的个数 */ public static long setDel(final String key, final Object... values) { Long count = redisTemplate.opsForSet().remove(key, values); return count == null ? 0 : count; } /** * 获取set中的所有对象 * * @param key Redis键 * @return set集合 */ public static Set<Object> getSetAll(final String key) { return redisTemplate.opsForSet().members(key); } /** ================ 存储ZSet操作 ================== **/ /** * 往ZSet中存入数据 * * @param key Redis键 * @param values 值 * @return 存入的个数 */ public static long zsetSet(final String key, final Set<ZSetOperations.TypedTuple<Object>> values) { Long count = redisTemplate.opsForZSet().add(key, values); return count == null ? 0 : count; } /** * 删除ZSet中的数据 * * @param key Redis键 * @param values 值 * @return 移除的个数 */ public static long zsetDel(final String key, final Set<ZSetOperations.TypedTuple<Object>> values) { Long count = redisTemplate.opsForZSet().remove(key, values); return count == null ? 0 : count; } /** ================ 存储List操作 ================== **/ /** * 往List中存入数据 * * @param key Redis键 * @param value 数据 * @return 存入的个数 */ public static long listPush(final String key, final Object value) { Long count = redisTemplate.opsForList().rightPush(key, value); return count == null ? 0 : count; } /** * 往List中存入多个数据 * * @param key Redis键 * @param values 多个数据 * @return 存入的个数 */ public static long listPushAll(final String key, final Collection<Object> values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); return count == null ? 0 : count; } /** * 往List中存入多个数据 * * @param key Redis键 * @param values 多个数据 * @return 存入的个数 */ public static long listPushAll(final String key, final Object... values) { Long count = redisTemplate.opsForList().rightPushAll(key, values); return count == null ? 0 : count; } /** * 从List中获取begin到end之间的元素 * * @param key Redis键 * @param start 开始位置 * @param end 结束位置(start=0,end=-1表示获取全部元素) * @return List对象 */ public static List<Object> listGet(final String key, final int start, final int end) { return redisTemplate.opsForList().range(key, start, end); } }

更多的 RedisTemplate 可以查看 RedisTemplate Api总结

③ 到这里已经算是配置成功了,接下来我们简单使用下,先创建个 Controller 控制器。

@RestController public class Hello { @Autowired private RedisTemplate redisTemplate; /* * 设置 redis 缓存 */ @GetMapping("/api/redis/set") public String saySet(@RequestParam String key, @RequestParam String value) { redisTemplate.opsForValue().set(key, value); return "ok"; } /* * 获取 redis 缓存 */ @GetMapping("/api/redis/get") public String sayGet(@RequestParam String key) { String result = (String)redisTemplate.opsForValue().get(key); return result; } }

在浏览器输入:

http://127.0.0.1:8088/api/redis/set?key=name&value=yxf233

参考文章