spring boot2 集成Redis

SpringBoot 2.X集成Redis(Lettuce)

SpringBoot2整合Redis缓存

SpringBoot2.0.3 Redis缓存 @Cacheable、@CacheEvict、@CachePut

史上最全面的Spring Boot Cache使用与整

————————————————

springboot1.5的版本redis客户端默认是jedis,2.0以上版本默认是lettuce

本文采用lettuce,就是把application.yml配置文件里的jedis换成lettuce,

一、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.8.RELEASE</version>
 <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>com.urthink.upfs</groupId>
 <artifactId>upfs-provider</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>upfs-provider</name>
 <description>Upfs provider project for Spring Boot</description>

 <properties>
 <java.version>1.8</java.version>
 <spring-cloud.version>Finchley.SR2</spring-cloud.version>
 </properties>

 <dependencies>
 <!--去掉springboot本身日志依赖-->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter</artifactId>
 <exclusions>
 <exclusion>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-logging</artifactId>
 </exclusion>
 </exclusions>
 </dependency>

 <!--log4j2-->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-log4j2</artifactId>
 </dependency>

 <!--redis-->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <!-- 要用redis连接池 必须有pool依赖-->
 <dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-pool2</artifactId>
 </dependency>

 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>

 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
 </dependency>

 <dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <optional>true</optional>
 </dependency>

 <dependency>
 <groupId>com.urthink.upfs</groupId>
 <artifactId>upfs-model</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 </dependency>
 </dependencies>

 <build>
 <plugins>
 <plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 </plugin>
 </plugins>
 </build>

</project>


二、application.yml

max-wait和timeout要写单位,否则提示错误redis timeout Value '2000' is not a valid duration

spring:
 redis:
 # Redis数据库索引(默认为0)
 database: 10
 # Redis服务器地址
 host: 192.168.203.220
 # Redis服务器连接端口
 port: 6379
 # Redis服务器连接密码(默认为空)
 password:
 lettuce:
 pool:
 # 连接池最大连接数(使用负值表示没有限制)
 max-active: 200
 # 连接池中的最大空闲连接
 max-idle: 20
 # 连接池中的最小空闲连接
 min-idle: 10
 # 连接池最大阻塞等待时间(使用负值表示没有限制)
 max-wait: -1ms
 # 连接超时时间(毫秒)默认是2000ms
 timeout: 2000ms


三、RedisConfiguration.java

这个类主要是做一些配置,没有这个类,下面的3个测试类也能跑通,

package com.urthink.upfs.provider.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * redis配置器
 * 参考:RedisAutoConfiguration
 * @author zhao
 * @date 2019.1.16
 */
@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {

 //@Autowired
 //private RedisConnectionFactory redisConnectionFactory;


 @Bean //在没有指定缓存Key的情况下,key生成策略
 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();
 }
 };
 }

 @Bean
 public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
 //spring cache注解序列化配置
 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer())) //key序列化方式
 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())) //value序列化方式
 .disableCachingNullValues() //不缓存null值
 .entryTtl(Duration.ofSeconds(60)); //默认缓存过期时间

 // 设置一个初始化的缓存名称set集合
 Set<String> cacheNames = new HashSet<>();
 cacheNames.add("user");

 // 对每个缓存名称应用不同的配置,自定义过期时间
 Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
 configMap.put("user", redisCacheConfiguration.entryTtl(Duration.ofSeconds(120)));

 RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisTemplate.getConnectionFactory())
 .cacheDefaults(redisCacheConfiguration)
 .transactionAware()
 .initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
 .withInitialCacheConfigurations(configMap)
 .build();
 return redisCacheManager;
 }

// @Bean
// public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// // 初始化缓存管理器,在这里我们可以缓存的整体过期时间等
// // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
// //RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// //config = config.entryTtl(Duration.ofSeconds(60)) // 设置缓存的默认过期时间,也是使用Duration设置
// // .disableCachingNullValues(); // 不缓存空值
// //RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(config).build();
// RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).build();
// return redisCacheManager;
// }


 @Bean
 public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
 // 配置redisTemplate
 RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
 redisTemplate.setConnectionFactory(redisConnectionFactory);

 //设置序列化
 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// ObjectMapper om = new ObjectMapper();
// om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //{"id":"1","name":"张三","age":18}
 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //json数据带类的名称 //["com.urthink.upfs.model.entity.User",{"id":"1","name":"张三","age":18}]
// jackson2JsonRedisSerializer.setObjectMapper(om);
 RedisSerializer stringSerializer = new StringRedisSerializer();

 redisTemplate.setKeySerializer(stringSerializer); // key序列化
 redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value序列化
 redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化
 redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); // Hash value序列化
 redisTemplate.afterPropertiesSet();
 return redisTemplate;
 }

 @Bean
 public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
 StringRedisTemplate template = new StringRedisTemplate();
 template.setConnectionFactory(redisConnectionFactory);
 return template;
 }
}


三、测试类

1.User.java
package com.urthink.upfs.model.entity;

import lombok.Data;

import java.io.Serializable;

/**
 * 实体类
 */
@Data
public class User implements Serializable {

 private static final long serialVersionUID = 1L;

 private String id;
 private String name;
 private int age;

}
2.UserService.java,spring boot cache注解package com.urthink.upfs.provider.service;

import com.urthink.upfs.model.entity.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

 @Cacheable(value="user", key="#id") //user::0
 public User getUser(String id) {
 System.out.println(id+"进入实现类获取数据!");
 User user = new User();
 user.setId(id);
 user.setName("张三");
 user.setAge(18);
 return user;
 }

 @CacheEvict(value="user", key="#id", condition="#id!='1'")
 public void deleteUser(String id) {
 System.out.println(id+"进入实现类删除数据!");
 }

}
3.RedisService.java,redis 工具类,可以不用package com.urthink.upfs.provider.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * redis 工具类
 */
@Component
public class RedisService<HK, V> {

 // 在构造器中获取redisTemplate实例, key(not hashKey) 默认使用String类型
 private RedisTemplate<String, V> redisTemplate;
 // 在构造器中通过redisTemplate的工厂方法实例化操作对象
 private HashOperations<String, HK, V> hashOperations;
 private ListOperations<String, V> listOperations;
 private ZSetOperations<String, V> zSetOperations;
 private SetOperations<String, V> setOperations;
 private ValueOperations<String, V> valueOperations;

 // IDEA虽然报错,但是依然可以注入成功, 实例化操作对象后就可以直接调用方法操作Redis数据库
 @Autowired
 public RedisService(RedisTemplate<String, V> redisTemplate) {
 this.redisTemplate = redisTemplate;
 this.hashOperations = redisTemplate.opsForHash();
 this.listOperations = redisTemplate.opsForList();
 this.zSetOperations = redisTemplate.opsForZSet();
 this.setOperations = redisTemplate.opsForSet();
 this.valueOperations = redisTemplate.opsForValue();
 }


 public void hashPut(String key, HK hashKey, V value) {
 hashOperations.put(key, hashKey, value);
 }

 public Map<HK, V> hashFindAll(String key) {
 return hashOperations.entries(key);
 }

 public V hashGet(String key, HK hashKey) {
 return hashOperations.get(key, hashKey);
 }

 public void hashRemove(String key, HK hashKey) {
 hashOperations.delete(key, hashKey);
 }

 public Long listPush(String key, V value) {
 return listOperations.rightPush(key, value);
 }

 public Long listUnshift(String key, V value) {
 return listOperations.leftPush(key, value);
 }

 public List<V> listFindAll(String key) {
 if (!redisTemplate.hasKey(key)) {
 return null;
 }
 return listOperations.range(key, 0, listOperations.size(key));
 }

 public V listLPop(String key) {
 return listOperations.leftPop(key);
 }

 public void setValue(String key, V value) {
 valueOperations.set(key, value);
 }

 public void setValue(String key, V value, long timeout) {
 ValueOperations<String, V> vo = redisTemplate.opsForValue();
 vo.set(key, value, timeout, TimeUnit.MILLISECONDS);
 }


 public V getValue(String key) {
 return valueOperations.get(key);
 }

 public void remove(String key) {
 redisTemplate.delete(key);
 }

 public boolean expire(String key, long timeout, TimeUnit timeUnit) {
 return redisTemplate.expire(key, timeout, timeUnit);
 }
}
4.RedisTemplateTest.java使用StringRedisTemplate,RedisTemplate<String, String>,RedisTemplate<Object, Object>都可以
package com.urthink.upfs.provider;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.TimeUnit;

/**
 * redisTemplate测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTemplateTest {

 @Autowired
 //private StringRedisTemplate redisTemplate;
 private RedisTemplate<String, String> redisTemplate;
 //private RedisTemplate<Object, Object> redisTemplate;

 @Test
 public void testRedisTemplate(){
 redisTemplate.opsForValue().set("test2","ddd",50, TimeUnit.SECONDS);
 System.out.println(redisTemplate.opsForValue().get("test2"));
 }
}
5.RedisCacheTestpackage com.urthink.upfs.provider;


import com.urthink.upfs.model.entity.User;
import com.urthink.upfs.provider.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * SpringBoot缓存注解测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisCacheTest {

 @Autowired
 private StringRedisTemplate template;

 @Autowired
 private UserService userService;


 @Test
 public void getUser() {
 for (int i = 0; i < 5; i++) {
 User user = userService.getUser(String.valueOf(i));
 System.out.println(user);
 }
 }

 @Test
 public void deleteUser() {
 for (int i = 0; i < 5; i++) {
 userService.deleteUser(String.valueOf(i));
 }
 }
}
6.RedisServiceTest.javapackage com.urthink.upfs.provider;

import com.urthink.upfs.provider.service.RedisService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * RedisService测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisServiceTest {
 @Autowired
 RedisService redisService;

 @Test
 public void setTest() {
 redisService.setValue("key","hello");
 }

 @Test
 public void getTest() {
 System.out.println("getTest:"+ redisService.getValue("key"));
 }

}

redis里的键值
user::1
{"id":"1","name":"张三","age":18}键上为什么带两个冒号::?
RedisCache类createCacheKey方法
CacheKeyPrefix接口   /**
     * Creates a default {@link CacheKeyPrefix} scheme that prefixes cache keys with {@code cacheName} followed by double
     * colons. A cache named {@code myCache} will prefix all cache keys with {@code myCache::}.
     *
     * @return the default {@link CacheKeyPrefix} scheme.
     */
    static CacheKeyPrefix simple() {
        return name -> name + "::";
    }