SpringBoot整合Redis 之 lettuce #私藏项目实操分享#

Posted 梁云亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot整合Redis 之 lettuce #私藏项目实操分享#相关的知识,希望对你有一定的参考价值。

功能说明

SpringBoot2.2.6整合Redis,实现

  • 对Redis各种类型数据的操作
  • 业务层查改删缓存的管理

代码下载地址:<a rel="nofollow" href="https://github.com/hcitlife/IntegrateRedis">代码</a>;

第一步:创建项目

maven依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
</dependency>
<!--        解决java8新日期API反序列化异常:com.fasterxml.jackson.databind.exc.InvalidDefinitionException-->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</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>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.10.1</version>
</dependency>
<!--redis默认使用的Lettuce客户端-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--使用默认的Lettuce时,若配置spring.redis.lettuce.pool则必须配置该依赖-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

application.yml

server:
  port: 80
  servlet:
    context-path: /ir
spring:
  redis:
    database: 0
    host: 172.16.85.140
    port: 6379
    password: 1234
    timeout: 10000ms  # 超时时间
    lettuce:
      pool:
        max-idle: 8 # 最大空闲连接数,默认值为8
        max-wait: -1ms # 最大连接阻塞等待时间,默认值-1
        min-idle: 2 # 最小空闲连接数
        max-active: 20 #最大连接数

第二步:Redis配置文件

@Configuration
//继承CachingConfigurerSupport,为了自定义生成KEY的策略,可以不继承。
public class RedisConfig extends CachingConfigurerSupport 
    private Duration timeToLive = Duration.ofDays(1);    //过期时间1天
    private StringRedisSerializer keySerializer = new StringRedisSerializer();
    //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
    private Jackson2JsonRedisSerializer valueSerializer = new 
Jackson2JsonRedisSerializer(Dept.class);
     //解决缓存转换异常的问题
        ObjectMapper objectMapper = new ObjectMapper();
        //下面两行解决Java8新日期API序列化问题
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());
        //设置所有访问权限以及所有的实际类型都可序列化和反序列化
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的 
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        valueSerializer.setObjectMapper(objectMapper);
        System.out.println("--------------------");
    
    @Bean(name = "cacheManager")
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) 
        // 配置序列化(解决乱码的问题),通过config对象对缓存进行自定义配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(this.timeToLive)// 设置缓存的默认过期时间
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))
                .disableCachingNullValues();// 不缓存空值

        //缓存配置
        Map<String,RedisCacheConfiguration> cacheConfig=new HashMap<>();
        //自定义缓存名,后面使用的@Cacheable的CacheName
        cacheConfig.put("dept",config);

        //根据redis缓存配置和reid连接工厂生成redis缓存管理器
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .transactionAware()
                .withInitialCacheConfigurations(cacheConfig)
                .build();
        return redisCacheManager;
    

    //缓存键自动生成器
    @Override
    @Bean(name = "myKeyGen")
    public KeyGenerator keyGenerator()  //设置自定义keyClassName + methodName + params
        return (target, method, params) -> 
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(".");
            sb.append(method.getName());
            sb.append("(");
            for (int i = 0; i < params.length; i++) 
                sb.append(params[i].toString());
                if (i != (params.length - 1)) 
                    sb.append(",");
                
            
            sb.append(")");
            return sb.toString();
        ;
    
    //自定义keyGenerator,Key生成器
    @Bean(name = "updateByIdKeyGen")
    public KeyGenerator updateByIdkeyGenerator() 
        return (target, method, params) -> 
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(".");
            sb.append("findDeptByDeptno(");
            try 
                Field id = params[0].getClass().getDeclaredField("deptno");
                id.setAccessible(true);
                sb.append(id.get(params[0]).toString());
             catch (IllegalAccessException e) 
                e.printStackTrace();
             catch (NoSuchFieldException e) 
                e.printStackTrace();
            
            sb.append(")");
            return sb.toString();
        ;
    
    //自定义keyGenerator,Key生成器
    @Bean(name = "deleteByIdKeyGen")
    public KeyGenerator deleteByIdkeyGenerator() 
        return (target, method, params) -> 
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(".");
            sb.append("findDeptByDeptno(");
            for (int i = 0; i < params.length; i++) 
                sb.append(params[i].toString());
                if (i != (params.length - 1)) 
                    sb.append(",");
                
            
            sb.append(")");
            return sb.toString();
        ;
    
    /**
     * @param factory
     * @return
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory)
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        //配置连接工厂
        redisTemplate.setConnectionFactory(factory);

        //设置key的序列化规则
        redisTemplate.setKeySerializer(keySerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(keySerializer);
        //设置value的序列化规则
        redisTemplate.setValueSerializer(valueSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(valueSerializer);

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    

在SpringBoot2.0之后,spring容器自动的生成了StringRedisTemplate和RedisTemplate<Object,Object>,可以直接注入。但是在实际使用中,大多情况下不会直接使用RedisTemplate<Object,Object>,而是会对key和value进行序列化,所以我们还需要新增一个配置类。

第三步:Redis工具类

参看博客:Redis工具类:RedisTemplate

第四步:实体类:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Dept implements Serializable 
    private static final long serialVersionUID = -1092294594385392402L;
    private Integer deptno;
    private String dname;
    private String loc;

第五步:Service 接口及实现类:

DeptService.java

public interface DeptService 
    Dept findDeptByDeptno(Integer deptno);

    Dept updateDept(Dept dept);

    Dept deleteDeptByDeptno(Integer deptno);

DeptServiceImpl.java

@Service
@CacheConfig(cacheManager = "cacheManager", cacheNames = "dept")
public class DeptServiceImpl implements DeptService 
    @Override
    @Cacheable(keyGenerator = "myKeyGen")
    public Dept findDeptByDeptno(Integer deptno) 
        System.out.println("DeptService findDeptByDeptno ...");
        return new Dept(10, "aa", "aaaaaaa");
    

    @CachePut(keyGenerator = "updateByIdKeyGen")
    @Override
    public Dept updateDept(Dept dept) 
        System.out.println("DeptService updateDept ...");
        return new Dept(10, "123", "rewrweq");
    

    @CacheEvict(keyGenerator = "deleteByIdKeyGen")
    @Override
    public Dept deleteDeptByDeptno(Integer deptno) 
        System.out.println("DeptService deleteDeptByDeptno ...");
        return new Dept(10, "aa", "aaaaaaa");
    

第六步:Controller层:

@RestController
@RequestMapping("/redis")
public class DemoController 
    @Autowired
    private RedisUtil redisUtil;

    //http://localhost:8080/redis/setStr?key=ab&value=aaabbb
    @RequestMapping("/setStr")
    public String setStr(String key, String value) 
        redisUtil.set(key, value);
        return "success";
    

    //http://localhost:8080/redis/getStr?key=ab
    @RequestMapping("/getStr")
    public Object getStr(String key) 
        return redisUtil.get(key);
    

    //http://localhost:8080/redis/setList?key=abc&value1=aa&value2=bb&value3=cc
    @RequestMapping("/setList")
    public String setList(String key, String value1, String value2, String value3) 
        List<String> list = new ArrayList<>();
        list.add(value1);
        list.add(value2);
        list.add(value3);
        redisUtil.lSets(key, Collections.singletonList(list));
        return "success";
    

    @Resource
    private DeptService deptService;

    @RequestMapping("/findDeptByDeptno/deptno")
    public Dept getEmpByEmpno(@PathVariable("deptno") int deptno) 
        return deptService.findDeptByDeptno(deptno);
    

测试

运行程序,在网页中进行测试。

DeptService测试

@RunWith(SpringRunner.class)
@SpringBootTest
class DeptServiceImplTest 

    @Resource
    private DeptService deptService;

    @Test
    void findDeptByDeptno() 
        Dept dept = deptService.findDeptByDeptno(10);
        System.out.println(dept);
    

    @Test
    void updateDept()
        Dept dept0 = new Dept(10,"abc","cba");
        Dept dept = deptService.updateDept(dept0);
        System.out.println(dept);
    

    @Test
    void deleteDeptByDeptno()
        Dept dept = deptService.deleteDeptByDeptno(10);
        System.out.println(dept);
    

以上是关于SpringBoot整合Redis 之 lettuce #私藏项目实操分享#的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot之整合Redis

32springboot——缓存之整合Redis

九springboot整合redis二之缓冲配置

SpringBoot缓存管理之整合Redis缓存的实现

lettuce之springboot整合redis

springboot整合redis之发送手机验证码注册登录