栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

补习系列(14)-springboot redis 整合-数据读写

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

补习系列(14)-springboot redis 整合-数据读写

[TOC]

一、简介

在 补习系列(A3)-springboot redis 与发布订阅 一文中,我们介绍了使用 Redis 实现消息订阅发布的机制,并且给出了一个真实用例。

然而,绝大多数场景下 Redis 是作为缓存被使用的(这是其主要优势)。除此之外,由于Redis 提供了 AOF以及RDB两种持久化机制,某些情况下也可以作为临时数据库使用。

本次将介绍 SpringBoot 中如何使用 Redis 进行缓存读写。

Redis 的基本命令

在学习之前,需要先了解一些Redis 的基本命令,可以参考这里

http://www.redis.cn/

二、SpringBoot Redis 读写

A. 引入 spring-data-redis

添加依赖

 

  

   org.springframework.boot

   spring-boot-starter-data-redis

   ${spring-boot.version}

  

spring-boot-starter-redis在1.4版本已经废弃

配置redis连接

application.properties

# redis 连接配置

spring.redis.database=0 

spring.redis.host=127.0.0.1

spring.redis.password=

spring.redis.port=6379

spring.redis.ssl=false



# 连接池最大数

spring.redis.pool.max-active=10 

# 空闲连接最大数

spring.redis.pool.max-idle=10

# 获取连接最大等待时间(s)

spring.redis.pool.max-wait=600000

B. 序列化

同样,我们需要指定 JSON作为 Key/HashKey/Value的主要方式:

    

    @Bean

    public Jackson2JsonRedisSerializer jackson2JsonSerializer() {

 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(

  Object.class);



 // 初始化objectmapper

 ObjectMapper mapper = new ObjectMapper();

 mapper.setSerializationInclusion(Include.NON_NULL);

 mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

 jackson2JsonRedisSerializer.setObjectMapper(mapper);

 return jackson2JsonRedisSerializer;

    }



   

    @Bean

    public RedisTemplate redisTemplate(JedisConnectionFactory connectionFactory,

     Jackson2JsonRedisSerializer jackson2JsonRedisSerializer) {



 RedisTemplate template = new RedisTemplate();

 template.setConnectionFactory(connectionFactory);



 // 设置key/hashkey序列化

 RedisSerializer stringSerializer = new StringRedisSerializer();

 template.setKeySerializer(stringSerializer);

 template.setHashKeySerializer(stringSerializer);



 // 设置值序列化

 template.setValueSerializer(jackson2JsonRedisSerializer);

 template.setHashValueSerializer(jackson2JsonRedisSerializer);

 template.afterPropertiesSet();



 // template.setValueSerializer(new

 // GenericToStringSerializer(Object.class));

 return template;

    }


Jackson2JsonRedisSerializer是Jackson转换的桥接器;

RedisTemplate是用于读写的主要操作类;

C. 读写样例

首先定义一个Pet实体类

public class RedisPet {



    private String name;

    private String type;

... ignore get set

利用RedisTemplate封装一层Repository,如下:

    @Repository

    public static class PetRepository {



 private static final String KEY = "Pets";



 @Autowired

 private RedisTemplate redisTemplate;

 private HashOperations hashOperations;



 @PostConstruct

 private void init() {

     hashOperations = redisTemplate.opsForHash();

 }



 public void add(RedisPet pet) {

     hashOperations.put(KEY, pet.getName(), pet);

 }



 public RedisPet find(String name) {

     return (RedisPet) hashOperations.get(KEY, name);

 }



 public Map findAll() {

     return hashOperations.entries(KEY);

 }

 

 public void clear() {

     hashOperations.getOperations().delete(KEY);

 }

    }

在**PetRepository **的实现中,我们利用Hash结构来存储 Pet信息(Pet.name是key)

分别实现了添加(add)/查找(get)/清除(clear)等方法。

最后,实现读写调用:



@Service

public class RedisDataOperation {



    private static final Logger logger = LoggerFactory.getLogger(RedisDataOperation.class);



    @Autowired

    private PetRepository petRepo;





    @PostConstruct

    public void start() {



 RedisPet pet1 = new RedisPet("Polly", "Bird");

 RedisPet pet2 = new RedisPet("Tom", "Cat");



 //写入宠物信息

 petRepo.add(pet1);

 petRepo.add(pet2);



 //打印宠物信息

 logger.info("polly {}", JsonUtil.toJson(petRepo.find("Polly")));

 logger.info("pets  {}", JsonUtil.toJson(petRepo.findAll()));



 //清空

 petRepo.clear();

    }

上面的代码在应用启动时,会写入两个Pet信息,之后完成清理,控制台输出如下:

RedisDataOperation : polly {"name":"Polly","type":"Bird"}

RedisDataOperation : pets {"Tom":{"name":"Tom","type":"Cat"},"Polly":{"name":"Polly","type":"Bird"}}

三、方法级缓存

除了上面的RedisTemplate,spring-data-redis还提供了方法级缓存,

就是将业务方法的执行结果缓存起来,后面再次调用直接从缓存中取得结果返回。

这种方式可以简化缓存逻辑的代码,比如配置类数据的读取,通过方法注解就可以实现,

下面是一个样例:



@Service

public class RedisCacheOperation {



    private static final Logger logger = LoggerFactory.getLogger(RedisCacheOperation.class);



    public static final String PREFIX = "pets:";

    public static final String WRAP_PREFIX = "'pets:'";



    

    @Cacheable(value = "petCache", key = WRAP_PREFIX + "+#name", unless = "#result==null")

    public RedisPet getPet(String name) {

 logger.info("get pet {}", name);

 return new RedisPet(name, "Bird");

    }



    

    @CacheEvict(value = "petCache", key = WRAP_PREFIX + "+#pet.name", condition = "#result!=null")

    public RedisPet updatePet(RedisPet pet) {

 logger.info("update pet {}", pet.getName());

 return new RedisPet(pet.getName(), "Bird1");

    }



    

    @CacheEvict(value = "petCache", key = WRAP_PREFIX + "+#name", condition = "#result==true")

    public boolean deletePet(String name) {

 logger.info("delete pet {}", name);

 return true;

    }

}

涉及到几个注解:

|注解 |说明|

|-----|-----|

|@Cachable |方法执行结果缓存 |

|@CachePut |方法执行结果缓存(强制) |

|@CacheEvict |方法执行时触发删除 |

其中 @CachePut@Cachable 的区别在于,前者一定会执行方法,并尝试刷新缓存(条件满足),

而后者则是当缓存中不存在时才会执行方法并更新。

注解中的属性 key/condition 都支持通过 Spring EL 表达式来引用参数对象。

启用注解

除了上面的代码,我们还需要使用 @EnableCaching 启用注解:

@EnableCaching

@Configuration

public class RedisConfig {



    private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);



    

    @Bean

    public RedisCacheManager cacheManager(RedisTemplate template) {

 RedisCacheManager redisCacheManager = new RedisCacheManager(template);

 // 默认过期时间

 redisCacheManager.setDefaultExpiration(30 * 60 * 1000);

 return redisCacheManager;

    }

当@Cacheable 的key属性为空时,框架会自动生成,格式类似:

param1,param2,param3...

如果希望修改默认的行为,可以使用自定义的 KeyGenerator

    

    @Bean

    public KeyGenerator keyGenerator() {

 return new KeyGenerator() {

     @Override

     public Object generate(Object target, Method method, Object... args) {

  StringBuilder sb = new StringBuilder();

  sb.append(target.getClass().getName());

  sb.append(method.getName());



  for (Object arg : args) {

      sb.append(arg.toString());

  }

  return sb.toString();

     }

 };

    }

单元测试

使用一小段单元测试代码来测试方法级缓存功能

@RunWith(SpringRunner.class)

@SpringBootTest(classes =BootSampleRedis.class)

public class RedisCacheOperationTest {



    private static final Logger logger = LoggerFactory.getLogger(RedisCacheOperationTest.class);



    @Autowired

    private RedisTemplate redisTemplate;



    @Autowired

    private RedisCacheOperation operation;



    private RedisPet pet1 = new RedisPet("Polly", "Bird");



    @Test

    public void testGet() {

 operation.getPet(pet1.getName());



 Object object = redisTemplate.opsForValue().get(RedisCacheOperation.PREFIX + pet1.getName());

 logger.info(String.valueOf(object));



 assertNotNull(object);

    }



    @Test

    public void testUpdate() {

 operation.updatePet(pet1);



 Object object = redisTemplate.opsForValue().get(RedisCacheOperation.PREFIX + pet1.getName());

 logger.info(String.valueOf(object));



 assertNull(object);

    }



    @Test

    public void testDelete() {

 operation.getPet(pet1.getName());



 // delete cache

 operation.deletePet(pet1.getName());



 Object object = redisTemplate.opsForValue().get(RedisCacheOperation.PREFIX + pet1.getName());

 logger.info(String.valueOf(object));



 assertNull(object);

    }



}

四、连接池

如果希望通过代码来配置 Jedis 的连接池(熟悉的方式),可以声明 JedisConnectionFactory 实现:

    

    @Bean

    public JedisConnectionFactory jedisConnectionFactory() {

 JedisPoolConfig config = new JedisPoolConfig();



 // 最大连接

 config.setMaxTotal(10);

 // 最大空闲,与最大连接保持一致,可减少频繁键链的开销

 config.setMaxIdle(10);

 // 连接最大空闲时间

 config.setMinEvictableIdleTimeMillis(10 * 60 * 1000);

 // 获取连接等待的最大时长

 config.setMaxWaitMillis(30000);



 // 进行空闲连接检测的时间间隔

 config.setTimeBetweenEvictionRunsMillis(30 * 1000);

 // 取消不必要的test,有利于性能提升

 config.setTestonBorrow(false);![](https://img2018.cnblogs.com/blog/242916/201812/242916-20181206231048870-1133770725.png)



 config.setTestonReturn(false);



 JedisConnectionFactory factory = new JedisConnectionFactory(config);

 factory.setHostName("127.0.0.1");

 factory.setPort(6379);



 logger.info("redis config init first");

 return factory;

    }

更多配置可参考这里

示例代码可从 码云gitee 下载。

https://gitee.com/littleatp/springboot-samples/

小结

Redis 在大多数项目中的核心用途是缓存,spring-data-redis 为 SpringBoot 中集成 Redis 读写的封装。

除了 RedisTemplate之外,还实现了方法级的缓存注解,一定程度上简化了业务的使用。

Redis 在分布式系统中的应用场景有很多,后续有机会将进行更多的探讨。

欢迎继续关注"美码师的补习系列-springboot篇" ,期待更多精彩内容-

原文链接:https://www.cnblogs.com/littleatp/p/10080296.html

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号