Spring Boot整合Redis

一、Spring Boot 对 Redis 的支持

Spring 对 Redis 的支持是使用 Spring Data Redis 来实现的,一般使用 Jedis 或者 lettuce(默认),Java 客户端在 org.springframework.boot.autoconfigure.data.redis(Spring Boot 2.x) 中 redis 的自动配置 AutoConfigureDataRedis 

 

                  

RedisAutoConfiguration 提供了 RedisTemplate 与 StringRedisTemplate(只针对键值都是字符型的数据)模板,其中注解 @ConditionalOnMissingBean 是关键,表明该 Bean 如果在 Spring 中已经存在,则忽略,如果没有存在则在此处注册由 Spring 管理,也就是说我们可以“重写”该 bean,实现自己的 RedisTemplate 与 StringRedisTemplate,事实上,是要需要重写的,理由如下:

  • 没有实现我们所需要的序列化;
  • 泛型总是 <Object, Object>,大部分场景我们更需要 <String, Object>。
@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; }

二、实战

 

1、添加依赖

1)需要 spring-boot-starter-cache 依赖,管理缓存

<!-- Spring Boot Cache --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>

2)需要 spring-boot-starter-data-redis 依赖(注:spring boot 2.x 改为在 data 下),支持 redis:主要以为 Jedis 客户端为主,排除默认的 lettuce 作为客户端的依赖

<!-- Redis Cache --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <!-- 排除 lettuce 包,使用 jedis 代替--> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency>

3)需要 jedis-client 依赖(注:Redis Client 3 版本以上会报错与 spring-boot-starter-data-redis 冲突,最好使用 2.9.x),使用 jedis 作为客户端

<!-- Redis Client 3 版本以上会报错与 spring-boot-starter-data-redis 冲突 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>

2、redis 配置

创建 RedisConfig 配置类,增加 @Configuration 注解,同时开启缓存管理支持(添加注解 @EnableCaching),继承 CachingConfigurerSupport 重写 key 生成策略

@Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { /** * 生成 key 的策略: 根据类名 + 方法名 + 所有参数的值生成唯一的一个 key * @return */ @Bean @Override public KeyGenerator keyGenerator() { return (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();}; } }

之后使用的 application.yml 配置文件,其中这里已经选择 jedis 作为客户端。

# redis 配置 redis: port: 6379 # Redis 服务器连接密码(默认为空) password: host: xxx.xxx.xxx.xxx database: 0 jedis: pool: #连接池最大连接数(使用负值表示没有限制) max-active: 300 # 连接池中的最小空闲连接 max-idle: 100 # 连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: 10000 # 连接超时时间(毫秒) timeout: 5000

同时读取配置属性,注入 JedisPoolConfig

  /** * redis 配置属性读取 */ @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.database}") private int database; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.max-wait}") private long maxWaitMillis; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; /** * JedisPoolConfig 配置 * @return */ @Bean public JedisPoolConfig jedisPoolConfig() { log.info("初始化 JedisPoolConfig"); JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(maxActive); jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); jedisPoolConfig.setMaxIdle(maxIdle); return jedisPoolConfig; }

3、实现序列化

针对 RedisTemplate 或 StringRedisTemplate 进行序列化,同时重写注册 Bean

RedisTemplate 默认使用 JdkSerializationRedisSerializer,StringRedisTmeplate 默认使用的是 StringRedisSerializer。但都是不符合实际要求的

 /** * 重新实现 RedisTemplate:解决序列化问题 * @param redisConnectionFactory * @return */ @Bean @SuppressWarnings({"rawtype", "unchecked"}) public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); // 设置任何字段可见 om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 设置不是 final 的属性可以转换 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); log.info("objectMapper: {}", om); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key 采用 String 的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash 的 key 采用 String 的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value 序列化方式采用 jackson 序列化方式 template.setValueSerializer(jackson2JsonRedisSerializer); // hash 的 value 序列化方式采用 jackson 序列化方式 template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); template.setEnableTransactionSupport(true); return template; } /** * 重新实现 StringRedisTmeplate:键值都是 String 的的数据 * @param redisConnectionFactory * @return */ @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { StringRedisTemplate template = new StringRedisTemplate(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); template.setConnectionFactory(redisConnectionFactory); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key 采用 String 的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash 的 key 采用 String 的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value 序列化方式采用 jackson 序列化方式 template.setValueSerializer(jackson2JsonRedisSerializer); // hash 的 value 序列化方式采用 jackson 序列化方式 template.setHashValueSerializer(jackson2JsonRedisSerializer); return template; }
View Code

 

4、创建 Redis 连接工厂,同时注册 Bean

 注意 Spring Boot 1.x 与 Spring Boot 2.x 的区别,已在代码中注释表明,Spring Boot 1.x 使用的是 JedisConnectionFactory 。而 Spring Boot 2.x 使用的是 RedisStandaloneConfiguration ,之后传入 JedisConnectionFactory 返回 Bean

  /** * 注入 RedisConnectionFactory * @return */ @Bean public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) { log.info("初始化 JedisConnectionFactory"); /* 在 Spring Boot 1.x 中已经过时,采用 RedisStandaloneConfiguration 配置 JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(jedisPoolConfig); jedisConnectionFactory.setHostName(host); jedisConnectionFactory.setDatabase(database);*/ // JedisConnectionFactory 配置 hsot、database、password 等参数 RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); redisStandaloneConfiguration.setPort(port); redisStandaloneConfiguration.setDatabase(database); // JedisConnectionFactory 配置 jedisPoolConfig JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jedisPoolConfigBuilder = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder(); jedisPoolConfigBuilder.poolConfig(jedisPoolConfig); return new JedisConnectionFactory(redisStandaloneConfiguration); }

5、完整的 RedisConfig 配置类

 

/** * * @author jian * @date 2019/4/14 * @description * 1) RedisTemplate(或 StringRedisTemplate)虽然已经自动配置,但是不灵活(第一没有序列化,第二泛型为 <Object, Object> 不是我们想要的类型) * 所以自己实现 RedisTemplate 或 StringRedisTemplate) * 2) 采用 RedisCacheManager 作为缓存管理器 * */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { private static final Logger log = LoggerFactory.getLogger(RedisConfig.class); /** * redis 配置属性读取 */ @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.database}") private int database; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.max-wait}") private long maxWaitMillis; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; /** * JedisPoolConfig 配置 * @return */ @Bean public JedisPoolConfig jedisPoolConfig() { log.info("初始化 JedisPoolConfig"); JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(maxActive); jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); jedisPoolConfig.setMaxIdle(maxIdle); return jedisPoolConfig; } /** * 注入 RedisConnectionFactory * @return */ @Bean public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) { log.info("初始化 JedisConnectionFactory"); /* 在 Spring Boot 1.x 中已经过时,采用 RedisStandaloneConfiguration 配置 JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(jedisPoolConfig); jedisConnectionFactory.setHostName(host); jedisConnectionFactory.setDatabase(database);*/ // JedisConnectionFactory 配置 hsot、database、password 等参数 RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); redisStandaloneConfiguration.setPort(port); redisStandaloneConfiguration.setDatabase(database); // JedisConnectionFactory 配置 jedisPoolConfig JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jedisPoolConfigBuilder = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder(); jedisPoolConfigBuilder.poolConfig(jedisPoolConfig); return new JedisConnectionFactory(redisStandaloneConfiguration); } /** * 采用 RedisCacheManager 作为缓存管理器 * @param connectionFactory */ @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheManager redisCacheManager = RedisCacheManager.create(connectionFactory); return redisCacheManager; } /** * 生成 key 的策略: 根据类名 + 方法名 + 所有参数的值生成唯一的一个 key * @return */ @Bean @Override public KeyGenerator keyGenerator() { return (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();}; } /** * 重新实现 RedisTemplate:解决序列化问题 * @param redisConnectionFactory * @return */ @Bean @SuppressWarnings({"rawtype", "unchecked"}) public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); // 设置任何字段可见 om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 设置不是 final 的属性可以转换 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); log.info("objectMapper: {}", om); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key 采用 String 的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash 的 key 采用 String 的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value 序列化方式采用 jackson 序列化方式 template.setValueSerializer(jackson2JsonRedisSerializer); // hash 的 value 序列化方式采用 jackson 序列化方式 template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); template.setEnableTransactionSupport(true); return template; } /** * 重新实现 StringRedisTmeplate:键值都是 String 的的数据 * @param redisConnectionFactory * @return */ @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { StringRedisTemplate template = new StringRedisTemplate(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); template.setConnectionFactory(redisConnectionFactory); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key 采用 String 的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash 的 key 采用 String 的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value 序列化方式采用 jackson 序列化方式 template.setValueSerializer(jackson2JsonRedisSerializer); // hash 的 value 序列化方式采用 jackson 序列化方式 template.setHashValueSerializer(jackson2JsonRedisSerializer); return template; } }
View Code

 

三、测试

1、编写 redis 工具类

虽然 RedisTemplate 与 StringRedisTemplate 模板有提供的主要数据访问方法:

  • opsForValue():操作只有简单属性的数据
  • opsForList():操作含有 List 的数据
  • opsForSet():操作含有 set 的数据
  • opsForHash():操作含有 hash 的数据
  • opsForZSet():操作含有有序 set 类型 ZSet 的数据

但是相关比较抽象,实现起来比较复杂,有必要进一步封装,比如使用 redisTmeplate 中的简单 value 的 get 操作:

Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key);

但是封装之后,相对客户端用户来说比较明了

  /** * 读取缓存 * * @param key * @return */ public Object get(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); return result; }

完整的简单工具类如下:

@Component public class RedisUtils { @Autowired private RedisTemplate redisTemplate; /** * 批量删除对应的 value * * @param keys */ public void remove(final String... keys) { for (String key : keys) {remove(key); } } /** * 批量删除 key * * @param pattern */ public void removePattern(final String pattern) { Set<Serializable> keys = redisTemplate.keys(pattern); if (keys.size() > 0) {redisTemplate.delete(keys); } } /** * 删除对应的 value * * @param key */ public void remove(final String key) { if (exists(key)) {redisTemplate.delete(key); } } /** * 判断缓存中是否有对应的 value * * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 读取缓存 * * @param key * @return */ public Object get(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); result = true; } catch (Exception e) {e.printStackTrace(); } return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) {e.printStackTrace(); } return result; } }
View Code

 

2、Person 实体类

需要注意的是一定要实现序列化,并且有序列化版本 ID

public class Person implements Serializable { private final long serialVersionUID = 1L; private String id; private String name; private int age; private String gender; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Person{" + "id='"+ id +'\'' +", name='" + name +'\'' +", age="+ age +", gender='" + gender +'\'' +'}'; } }
View Code

 

3、编写测试类

Redis 工具类 Spring 已经做了管理(增加 @Compent 注解),使用很简单,只需要注入 RedisUtils 即可

@RunWith(SpringRunner.class) @SpringBootTest public class RedisTest { @Autowired private RedisUtils redisUtils; @Test public void test(){ Person person = new Person(); person.setAge(23); person.setId("001"); person.setName("Zhangsan"); redisUtils.set("person-001", person); System.out.println(redisUtils.get("person-001"));} }

4、测试结果

在 IDE 控制台中:

在登录客户端后查看 value 值

 


__EOF__

本文作者Jian
本文链接https://www.cnblogs.com/jian0110/p/10833056.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!