很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下

这里假设已经成功创建了一个springboot项目。

redis连接工厂类

第一步,需要加上springboot的redis jar包

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>

然后我们写一个配置类,创建了一个redis连接的工厂的spring bean。(Redis连接工厂会生成到Redis数据库服务器的连接)

  1. @Configuration
  2. public class RedisConfig {
  3. @Bean
  4. public RedisConnectionFactory redisCF(){
  5. //如果什么参数都不设置,默认连接本地6379端口
  6. JedisConnectionFactory factory = new JedisConnectionFactory();
  7. factory.setPort(6379);
  8. factory.setHostName("localhost");
  9. return factory;
  10. }
  11. }

单元测试,看看这个工厂方法的使用

  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @SpringBootTest(classes = Application.class)
  3. public class RedisTest {
  4.  
  5. @Autowired
  6. RedisConnectionFactory factory;
  7.  
  8. @Test
  9. public void testRedis(){
  10. //得到一个连接
  11. RedisConnection conn = factory.getConnection();
  12. conn.set("hello".getBytes(), "world".getBytes());
  13. System.out.println(new String(conn.get("hello".getBytes())));
  14. }
  15.  
  16. }

输出结果 :world,说明已经成功获取到连接,并且往redis获取添加数据,

template(模版)

但是我们发现每次添加的key和value都是byte数组类型(使用很麻烦),于是spring为我们带来了redis template(模版)

Spring Data Redis提供了两个模板:
  RedisTemplate
  StringRedisTemplate

首先我们先创建一个RedisTemplate模板类,类型的key是String类型,value是Object类型(如果key和value都是String类型,建议使用StringRedisTemplate)

  1. @Bean
  2. public RedisTemplate redisTemplate(RedisConnectionFactory factory){
  3. //创建一个模板类
  4. RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  5. //将刚才的redis连接工厂设置到模板类中
  6. template.setConnectionFactory(factory);
  7. return template;
  8. }

单元测试

  1. @Autowired
  2. RedisTemplate<String, Object> template;
  3.  
  4. @Test
  5. public void testRedisTemplate(){
  6. template.opsForValue().set("key1", "value1");
  7. System.out.println(template.opsForValue().get("key1"));
  8. }

得到结果输出value1,是不是很方便了呢。

如果是操作集合呢,也很方便的哈。

  1. @Test
  2. public void testRedisTemplateList(){
  3.  
  4. Pruduct prud = new Pruduct(1, "洗发水", "100ml");
  5. Pruduct prud2 = new Pruduct(2, "洗面奶", "200ml");
  6. //依次从尾部添加元素
  7. template.opsForList().rightPush("pruduct", prud);
  8. template.opsForList().rightPush("pruduct", prud);
  9. //查询索引0到商品总数-1索引(也就是查出所有的商品)
  10. List<Object> prodList = template.opsForList().range("pruduct", 0,template.opsForList().size("pruduct")-1);
  11. for(Object obj:prodList){
  12. System.out.println((Pruduct)obj);
  13. }
  14. System.out.println("产品数量:"+template.opsForList().size("pruduct"));
  15.  
  16. }

key和value序列化

当保存一条数据的时候,key和value都要被序列化成json数据,取出来的时候被序列化成对象,key和value都会使用序列化器进行序列化,spring data redis提供多个序列化器

  • GenericToStringSerializer:使用Spring转换服务进行序列化;
  • JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON;
  • Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON;
  • JdkSerializationRedisSerializer:使用Java序列化;
  • OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实
  • 现序列化,用于XML序列化;
  • StringRedisSerializer:序列化String类型的key和value。

RedisTemplate会默认使用JdkSerializationRedisSerializer,这意味着key和value都会通过Java进行序列化。StringRedisTemplate默认会使用StringRedisSerializer

例如,假设当使用RedisTemplate的时候,我们希望将Product类型的value序列化为JSON,而key是String类型。RedisTemplate的setKeySerializer()和setValueSerializer()方法就需要如下所示:

  1. @Bean
  2. public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
  3. // 创建一个模板类
  4. RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  5. // 将刚才的redis连接工厂设置到模板类中
  6. template.setConnectionFactory(factory);
  7. // 设置key的序列化器
  8. template.setKeySerializer(new StringRedisSerializer());
  9. // 设置value的序列化器
  10. //使用Jackson 2,将对象序列化为JSON
  11. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  12. //json转对象类,不设置默认的会将json转成hashmap
  13. ObjectMapper om = new ObjectMapper();
  14. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  15. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  16. jackson2JsonRedisSerializer.setObjectMapper(om);
  17. template.setValueSerializer(jackson2JsonRedisSerializer);
  18.  
  19. return template;
  20. }

到这里,大家肯定会对springboot使用redis有了简单的了解。


springboot缓存某个方法

申明缓存管理器

在某些时候,我们可能有这样的需求,用户登录的时候,我们会从数据库中读取用户所有的权限,部门等信息。而且每次刷新页面都需要判断该用户有没有这个权限,如果不停的从数据库中读并且计算,

是非常耗性能的,所以我们这个时候就要用到了springboot为我们带来的缓存管理器

首先在我们的RedisConfig这个类上加上@EnableCaching这个注解。

这个注解会被spring发现,并且会创建一个切面(aspect) 并触发Spring缓存注解的切点(pointcut) 。 根据所使用的注解以及缓存的状态, 这个切面会从缓存中获取数据, 将数据添加到缓存之中或者从缓存中移除某个值。
 
接下来我们需要申明一个缓存管理器的bean,这个作用就是@EnableCaching这个切面在新增缓存或者删除缓存的时候会调用这个缓存管理器的方法
  1. /**
  2. * 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
  3. * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值
  4.  
  5. * @return
  6. */
  7. @Bean
  8. public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
  9. return new RedisCacheManager(redisTemplate);
  10. }

当然,缓存管理器除了RedisCacheManager还有一些其他的。例如

  1. SimpleCacheManager
  2. NoOpCacheManager
  3. ConcurrentMapCacheManager
  4. CompositeCacheManager
  5. EhCacheCacheManager

ConcurrentMapCacheManager,这个简单的缓存管理器使用java.util.concurrent.ConcurrentHashMap作为其缓存存储。它非常简单,因此对于开发、测试或基础的应用来讲,这是一个很不错的选择.

添加缓存

接下来我们在controller层的方法内加上注解,然后启动我们的项目。

  1. @RequestMapping("/getPrud")
  2. @Cacheable("prudCache")
  3. public Pruduct getPrud(@RequestParam(required=true)String id){
  4. System.out.println("如果第二次没有走到这里说明缓存被添加了");
  5. return pruductDao.getPrud(Integer.parseInt(id));
  6. }

发现打印的这段话只被打印一次,说明在走到这个方法的时候触发了一个切面,并且查找返回缓存中的数据。

当然@Cacheable注解也可以放到这个dao层的方法里面,但是这里会报一个错,Integer无法转成String,因为我们dao层方法的参数类型是int,而RedisTemplate的key类型是String,这里是要注意的。

打开redis的客户端发现redis对应的key就是我们的参数1,这个时候就会出问题,比如说我在其他要缓存的方法的参数也是1,就会重复。后面我们会将自定义这个key的值。

除了@Cacheable添加缓存外,springboot还为我们带了了其他几个注解

删除缓存

在delete的时候用@CacheEvict清楚这条缓存。

  1. @RequestMapping("/deletePrud")
  2. @CacheEvict("pruddeleteCache")
  3. public String deletePrud(@RequestParam(required=true)String id){
  4. return "SUCCESS";
  5. }

@CachePut将这个方法的返回值放到缓存,如果我们放一个Pruduct对象,他会将这个对象作为key,这显然不是我们想要的。这个时候就需要自定义我们的key。

自定义key

@Cacheable和@CachePut都有一个名为key属性,这个属性能够替换默认的key,它是通过一个表达式(Spel表达式,spring提供的,很简单)计算得到的。

例如下面的就是将返回对象的id当作key来存储(但是Pruduct的id是int类型,所以需要将数字转化成String类型)

  1. @RequestMapping("/savePrud")
  2. @CachePut(value="prudsaveCache",key="#result.id +''")
  3. public Pruduct savePrud(Pruduct prud){
  4. return prud;
  5. }

另外除了#result是代表函数的返回值,spring还为我们带来了其他的一些元数据

条件化缓存

通过为方法添加Spring的缓存注解,Spring就会围绕着这个方法创建一个缓存切面。但是,在有些场景下我们可能希望将缓存功能关闭。

@Cacheable和@CachePut提供了两个属性用以实现条件化缓存:unless和condition,这两个属性都接受一个SpEL表达式。如果unless属性的SpEL表达式计算结
果为true,那么缓存方法返回的数据就不会放到缓存中。与之类似,如果condition属性的SpEL表达式计算结果为false,那么对于这个方法缓存就会被禁用掉

表面上来看,unless和condition属性做的是相同的事情。但是,这里有一点细微的差别。

unless属性只能阻止将对象放进缓存,但是在这个方法调用的时候,依然会去缓存中进行查找,如果找到了匹配的值,就会返回找到的值。

与之不同,如果condition的表达式计算结果为false,那么在这个方法调用的过程中,缓存是被禁用的。就是说,不会去缓存进行查找,同时返回值也不会放进缓存中。

  1. @RequestMapping("/getPrud2")
  2. @CachePut(value ="prudCache",unless="#result.desc.contains('nocache')")
  3. public Pruduct getPrud2(@RequestParam(required=true)String id){
  4. System.out.println("如果走到这里说明,说明缓存没有生效!");
  5. Pruduct p = new Pruduct(Integer.parseInt(id), "name_nocache"+id, "nocache");
  6. return p;
  7. }

上面的代码中,如果返回的对象desc中包含nocache字符串,则不进行缓存。

总结demo:

将类名方法名和pruduct的id作为key

  1. @RequestMapping("/getPrud3")
  2. @Cacheable(value ="prudCache",key="#root.targetClass.getName() + #root.methodName + #id")
  3. public Pruduct getPrud3(@RequestParam(required=true)String id){
  4. System.out.println("如果第二次没有走到这里说明缓存被添加了");
  5. return pruductDao.getPrud(Integer.parseInt(id));
  6. }

最后注意

#result 方法返回值不能用在@Cacheable上,只能用在@CachePut

springboot配置升级简单化

当然上面的配置只是为了了解原理的哈,实际上我们使用会更简单点。我们重写了RedisConfig

  1. @Configuration
  2. @EnableCaching//开启缓存
  3. public class RedisConfig extends CachingConfigurerSupport {
  4.  
  5. @Bean
  6. public KeyGenerator keyGenerator() {
  7. return new KeyGenerator() {
  8. @Override
  9. public Object generate(Object target, Method method, Object... params) {
  10. StringBuilder sb = new StringBuilder();
  11. sb.append(target.getClass().getName());
  12. sb.append(method.getName());
  13. for (Object obj : params) {
  14. sb.append(obj.toString());
  15. }
  16. return sb.toString();
  17. }
  18. };
  19. }
  20. /**
  21. * 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
  22. * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值
  23.  
  24. * @return
  25. */
  26. @Bean
  27. public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
  28. return new RedisCacheManager(redisTemplate);
  29. }
  30.  
  31. @Bean
  32. @Primary
  33. public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
  34. // 创建一个模板类
  35. RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  36. // 将刚才的redis连接工厂设置到模板类中
  37. template.setConnectionFactory(factory);
  38. // 设置key的序列化器
  39. template.setKeySerializer(new StringRedisSerializer());
  40. // 设置value的序列化器
  41. //使用Jackson 2,将对象序列化为JSON
  42. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  43. //json转对象类,不设置默认的会将json转成hashmap
  44. ObjectMapper om = new ObjectMapper();
  45. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  46. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  47. jackson2JsonRedisSerializer.setObjectMapper(om);
  48. template.setValueSerializer(jackson2JsonRedisSerializer);
  49.  
  50. return template;
  51. }
  52.  
  53. }

然后在resources下的application.properties下配置

  1. # REDIS (RedisProperties)
  2. # Redis数据库索引(默认为0)
  3. spring.redis.database=0
  4. # Redis服务器地址
  5. spring.redis.host=127.0.0.1
  6. # Redis服务器连接端口
  7. spring.redis.port=6379
  8. # Redis服务器连接密码(默认为空)
  9. spring.redis.password=
  10. # 连接池最大连接数(使用负值表示没有限制)
  11. spring.redis.pool.max-active=8
  12. # 连接池最大阻塞等待时间(使用负值表示没有限制)
  13. spring.redis.pool.max-wait=-1
  14. # 连接池中的最大空闲连接
  15. spring.redis.pool.max-idle=8
  16. # 连接池中的最小空闲连接
  17. spring.redis.pool.min-idle=0
  18. # 连接超时时间(毫秒)
  19. spring.redis.timeout=0

大家发现我们并没有注册RedisConnectionFactory,那是因为spring默认帮我们读取application.properties文件并且注册了一个factorybean

keyGenerator方法帮我们注册了一个key的生成规则,就不用我们写spel表达式了,根据反射的原理读取类名+方法名+参数。但是我们有时候还是需要结合spel的。

然后在controller上加上@Cacheable("cachename"),之后就可以在redis看到保存了并且key的值是keyGenerator生成的名字

本文代码github地址

由浅入深学习springboot中使用redis的更多相关文章

  1. (一)由浅入深学习springboot中使用redis

    很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下 这里假设已经成功创建了一个springboot项目. redis连接工厂类 ...

  2. SpringBoot中使用Redis

    在SpringBoot中使用Redis,思路如下: 查询时先查Redis缓存,如果缓存中存在信息,就直接从缓存中获取. 如果缓存中没有相关信息,就去数据库中查找,查完顺便将信息存放进缓存里,以便下一次 ...

  3. 在springboot中使用redis缓存,将缓存序列化为json格式的数据

    背景 在springboot中使用redis缓存结合spring缓存注解,当缓存成功后使用gui界面查看redis中的数据 原因 springboot缓存默认的序列化是jdk提供的 Serializa ...

  4. SpringBoot中整合Redis、Ehcache使用配置切换 并且整合到Shiro中

    在SpringBoot中Shiro缓存使用Redis.Ehcache实现的两种方式实例 SpringBoot 中配置redis作为session 缓存器. 让shiro引用 本文是建立在你是使用这sh ...

  5. SpringBoot学习笔记(10)-----SpringBoot中使用Redis/Mongodb和缓存Ehcache缓存和redis缓存

    1. 使用Redis 在使用redis之前,首先要保证安装或有redis的服务器,接下就是引入redis依赖. pom.xml文件如下 <dependency> <groupId&g ...

  6. 在SpringBoot中引入Redis

    前言 之前我们只是在Spring中加入Redis用于session的存放,并没有对redis进行主动的存放,这次我们需要加入redis工具类来方便我们在实际使用过程中操作redis 已经加入我的git ...

  7. SpringBoot中集成redis

    转载:https://www.cnblogs.com/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html 不是使用注解而是代码调用 需要在springbo ...

  8. 你知道如何在springboot中使用redis吗

    特别说明:本文针对的是新版 spring boot 2.1.3,其 spring data 依赖为 spring-boot-starter-data-redis,且其默认连接池为 lettuce ​  ...

  9. [SpringBoot]SpringBoot中使用redis事务

    本文基于SpringBoot 2.X 事务在关系型数据库的开发中经常用到,其实非关系型数据库,比如redis也有对事务的支持,本文主要探讨在SpringBoot中如何使用redis事务. 事务的相关介 ...

随机推荐

  1. CN_Week1_Receptive_Field

    0. The introduction: 1. An example: Models of "Receptive Fields". 2. An intuitive method o ...

  2. Python自学笔记-time模块(转)

    在Python中,通常有这几种方式来表示时间:1)时间戳 2)格式化的时间字符串 3)元组(struct_time)共九个元素.由于Python的time模块实现主要调用C库,所以各个平台可能有所不同 ...

  3. VLAN的三种类型及三种属性

  4. Druid源码阅读之连接池

    概述 Druid是阿里巴巴开源的一个数据库连接池 源码地址.下面简单分析一下连接池是怎么实现的 怎么开始阅读 如果使用过Druid连接池的都只要在Spring配置中配置jdbc的时候配置Driver是 ...

  5. Echarts图表统计学习

    史上最全的Echarts图表学习文档 http://echarts.baidu.com/doc/doc.html 勤加练习,多做总结! http://www.stepday.com/topic/?79 ...

  6. WinForm 读写配置文件

    //读配置文件 方法(1) //ConfigurationManager.RefreshSection("appSettings"); //强制重新载入 string settin ...

  7. Kettle文本文件输出和输入控件使用中,换行符导致的问题处理

    1.如下图通过输入控件从数据库读取数据然后生成TXT文本文件,TXT文件生成原则是每一条数据生成一行数据,第二条数据换行保存 2.如下图所示,使用文本文件输入控件读入上图生成的文件,文件读入原则是按行 ...

  8. 有人提了一个问题:一定要RESTful吗?

    写在前面的话 这个问题看起来就显得有些萌,或者说类似的问题都有些不靠谱,世上哪有那么多一定的事情,做开发都不一定做多久呢,所以说如果你有这个疑问的话是真真有点儿不着调,不过可能也就是随口一问吧,没有深 ...

  9. Bootstrap表格样式(附源码文件)--Bootstrap

    1.表格默认样式 <h4>表格默认样式</h4><table><!--默认样式--> <tr><th>序号</th> ...

  10. WPF MVVM模式的一些理解

    /*本文转自 http://www.cnblogs.com/sirkevin/archive/2012/11/28/2793471.html */ 使用WPF+Mvvm开发一年多,期间由于对Mvvm模 ...