SpringBoot整合Redis

注意:这是接着SpringBoot缓存(点击进入)继续的,之前用到的代码和数据都在里面
点击进入源代码

SpringBoot整合Redis

SpringBoot缓存默认使用的是ConcurrentMapCacheManager创建的ConcurrentMapCache组件,将数据保存在ConcurrentMap<Object, Object>中,而开发中经常使用的是缓存中间件。开发中经常使用缓存中间件:redis、memcached、ehcache;下面就对Redis整合。

一、安装Redis

1.用docker安装redis

  • 用远程工具连接上linux服务器

  • docker images检查是否已经安装redis
    c3afe2f362701f2fca6c4a415610e901.png
    我是没有安装docker的

  • 输入docker pull redis
    391b4760df2a9254844ce70b7bf7f15e.png

  • 安装完成之后再次输入docker images
    45f399337a174ff582fe5cfb03cd1fbe.png

  • 输入docker run -d -p 6379:6379 --name myredis redis仓库名字
    6c71fd1eee9572f7a9bdd4d7018b22c8.png

    -d 后台运行
    -p 暴露端口
    6379:6379 将服务器的6379映射到容器的6379
    –name 起一个名字

  • 查看redis镜像是否运行,docker ps
    405a79afdc578451c3634714e402c79e.png

2.用连接工具连接redis

5cf644dba4ae55150c040eed488495b6.png

能连接行redis就说明redis环境没有问题了

二、整合Redis

1.引入starter

  • 在pom.xml文件中添加依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

24db202a818257e164960e96a8d30d79.png
springboot 2.x使用的是lettuce,是对jedis的进一步封装,功能更强

  • 打开RedisAutoConfiguration类,这是redis的配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@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;
	}

}

其中:
StringRedisTemplate //操作k-v都是字符串的
RedisTemplate //k-v都是对象的

2.配置redis

  • 在application.properties中配置redis
spring.redis.host=47.94.229.156

3.测试

序列化javabean

21cacf7ce78a4e64bf32df983dfeab45.png

在测试类里操作
@SpringBootTest
class DemoApplicationTests {

    @Autowired
    EmployeeMapper employeeMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate; //操作k-v都是字符串的

    @Autowired
    RedisTemplate redisTemplate; //k-v都是对象

    /**
     * Redis常见的五大数据类型:
     * String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集)
     * stringRedisTemplate.opsForValue()[String(字符串)]
     * stringRedisTemplate.opsForList()[List(列表)]
     * stringRedisTemplate.opsForSet()[Set(集合)]
     * stringRedisTemplate.opsForHash()[Hash(散列)]
     * stringRedisTemplate.opsForZset()[Zset(有序集)]
     */
    @Test
    public void test01(){
        //给redis中保存数据
        stringRedisTemplate.opsForValue().append("xx","cool");
        //读取数据
        stringRedisTemplate.opsForValue().get("xx");

        stringRedisTemplate.opsForList().leftPush("mylist1","1");
        stringRedisTemplate.opsForList().leftPush("mylist1","2");
    }

    @Test
    public void test02(){
        Employee empById = employeeMapper.getEmpById(1);
        //把查出的数据放到缓存,Employee需要序列化
        //默认如果保存对象,使用jdk序列化机制,序列化后的数据保存到redis中
        redisTemplate.opsForValue().set("emp-01",empById);
        

    }

    @Test
    void contextLoads() {
        Employee empById = employeeMapper.getEmpById(1);
        System.out.println(empById);
    }

}

在test02测试中可以发现k-v都是序列化的值,如下图所示:
64695c905984e8904c9239cb9e5f7b3d.png
这是因为 StringRedisTemplate只能处理key和value都是字符串的信息

将数据以json的方式保存

(1)自己将对象转为json
(2)redisTemplate默认的序列化规则(使用这一种)

  • 在config包下建立一个MyRedisConfig类
@Configuration
public class MyRedisConfig {
    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, Employee> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> employeeJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(employeeJackson2JsonRedisSerializer);
        return template;
    }
}    

上面的方法还要用到泛型,不能照顾多数,还可以写一个通用的方法,如下:

@Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
       /*ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        如果不注释掉value的值为:["com.example.demo.bean.Employee",{"id":1,"lastName":"张三","email":"111","gender":1,"dId":1}]
        注释后value的值为:{"id":1,"lastName":"张三","email":"111","gender":1,"dId":1}
        */
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }
  • 在测试类中添加如下代码

@AutowiredRedisTemplate<Object,Employee> empRedisTemplate;

@Test
    public void test03(){
        Employee empById = employeeMapper.getEmpById(1);
        empRedisTemplate.opsForValue().set("emp-01",empById);
    }

62a8d0234852b8bb2917f7a0917324f1.png

可以看到,使用了jso的格式缓存

三、测试缓存

原理: CacheManager 生成 缓存组件 缓存组件来给缓存中存取数据
1)、引入redis的starter,容器中保存的是RedisCacheManager
2)、RedisCacheManager 帮我们创建RedisCache来作为缓存组件,RedisCache通过操作redis操作数据

1.启动项目测试

  • 启动项目,在浏览器中输入http://localhost:8080/emp/1
    可以看到浏览器中有返回结果,控制台打印信息

  • 再次在浏览器中输入http://localhost:8080/emp/1
    可以看到浏览器有返回数据,控制台没有返回,说明数据来源于缓存(redis)

  • 打开redis连接工具查看
    167492545ceb450521c983ca9f297f5f.png
    可以看到value是序列化之后的值,这是因为默认保存数据 k-v 都是Object;利用序列化保存;如何保存为json?

2.缓存数据如何保存为json

引入redis的starter,CacheManager变为RedisCacheManager

  • 在MyRedisConfig类里添加如下代码:
 @Bean
    public CacheManager empCacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //解决查询缓存转换异常的问题
        /*ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        注掉前的value值:["com.example.demo.bean.Employee",{"id":1,"lastName":"张三","email":"111","gender":1,"dId":1}]
        注掉后的value值:{"id":1,"lastName":"张三","email":"111","gender":1,"dId":1}
*/
        // 配置序列化(解决乱码的问题),过期时间30秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(30))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
  • 清除原来的缓存,在浏览器输入http://localhost:8080/emp/1
    a2c8aa725f142b23d0caed73ee58baac.png
    可以看到缓存数据正常了
  • 但是当我们从缓存中拿取数据的时候就会报错
    eg:当有这条数据缓存的时候,再次输入http://localhost:8080/emp/1,可以看到:
  • 56482565fceb74f76d4b4985abcb67cd.png
    原因是:
    缓存是以json的形式传入的缓存,取数据的时候我的是CacheManager默认使用RedisTemplate<Object,Object>操作的redis缓存
    改正:
    8bbb42dfee57f38c993319c205199ed8.png
    将图中的Object改成需要转换的类,我的就是Employee
  • 由此可见如果有好多javabean的话,对每个需要缓存的javabean的类型,创建一个CacheManager
(1)新增DepartmentService类
@Service
public class DepartmentService {

    @Autowired
    private DepartmentMapper departmentMapper;

    @Cacheable(cacheNames = "dept")
    public Department getDeptById(Integer id){
        System.out.println("查询部门"+id);
        Department deptById = departmentMapper.getDeptById(id);
        return deptById;
    }

}
(2)在DepartmentMapper中新增方法
@Select("SELECT * FROM department WHERE id = #{id}")
    public Department getDeptById(Integer id);
(3)新增DepartmentController类
@RestController
public class DepartmentController {

    @Autowired
    private DepartmentService departmentService;

    @GetMapping("/dept/{id}")
    public Department getDeptById(@PathVariable("id") Integer id){
        Department deptById = departmentService.getDeptById(id);
        return deptById;
    }
}
(4)在数据库department表中新增数据

226e435c57c4093e38f0ef02e19909e7.png

(5)测试一下会出现的错误
  • 启动项目,在浏览器中输入http://localhost:8080/dept/1
    可以看到第一次可以正常返回数据,

  • 再次在浏览器输入http://localhost:8080/dept/1

53333c84011760b6276164a83788e519.png

(6)解决方法
  • 在MyRedisConfig类中添加代码如下:
  @Primary//标注默认使用的CacheManager,不标注会报错
  @Bean
    public CacheManager deptCacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Department.class);
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }

指定缓存需要哪一个CacheManager

  • EmployeeService类
    7bddb854a66a52547c5a5fa2242367ca.png

  • DepartmentService类
    e5f822443ed84f9fdbd2248b9181b790.png
    这样就可以在缓存中查询employee和department中的数据了

四、编码方式的缓存

  • 以上都是通过注解的方式来缓存,下面用编码的方式缓存

  • 使用缓存管理器得到缓存,进行api调用

1.修改DepartmentService类

  • 注释掉下面的方法
    f41829609b5c6bcea3cbb84a36361e73.png

  • 添加下面的代码

@Qualifier("deptCacheManager")//明确指定
    @Autowired
    private CacheManager deptCacheManager;//注入部门的缓存管理器
    
    public Department getDeptById(Integer id){
        System.out.println("查询部门"+id);
        Department deptById = departmentMapper.getDeptById(id);
        //拿到缓存,对编码增删改查
        Cache dept = deptCacheManager.getCache("dept");
        dept.put("dept:1",deptById);
        return deptById;
    }    
  • 重启项目,在浏览器中输入http://localhost:8080/dept/1

4e7763816abe2e0f41b0b6bd1c7701eb.png

可以看到缓存中已经传入了数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值