SpringBoot系列:Spring Boot集成redis

一、Redis

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value的非关系型数据库,并提供多种语言的API。

由于Redis实现使用C语言编写,且采用了I/O多路复用,在响应速度与并发支持上都要优于关系型数据库,所以常被用来配合关系型数据库做高速缓存、共享Session、分布式锁、消息系统等。

Redis的重要性不言而喻,无论是其对于系统,还是对于自身技能而言,我们都应该好好掌握它,不过这里并不是Redis的教学,仅仅介绍Spring Boot如何集成·Redis。

二、redisTemplate方式使用Redis

要使用Redis,首先得引入Redis的依赖,Redis如此受欢迎,自然Spring Boot为它定制了专属的starter。

<!--redis依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后在配置文件中,我们需要在spring.redis节点下配置redis的ip、端口、密码、超时时间等等配置。

spring:
  redis:
    host: localhost
    port: 6379
    password: 1234
    timeout: 60000

在SpringBoot中,我们只要配置好对应的属性,就可以使用Redis了,且SpringBoot自动在容器中生成了一个RedisTemplate和一个StringRedisTemplate的Bean对象。但是,这个RedisTemplate的泛型是<Object,Object>,使用时需要频繁强转,编写代码并不是很方便,且这个RedisTemplate,key及value的序列化方式采用了JDK的序列化,会导致在库中的序列化数据看起来乱码。

一般业务中,我们常使用泛型为<String,Object>形式的RedisTemplate,因此我们自己来注入一个RedisTemplate<String,Object>的Bean。

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        redisTemplate.setValueSerializer(serializer);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

@Configuration表示这是一个配置类,@EnableCaching用于开启缓存。当配置类@Configuration和@EnableCaching一起注解时,会触发一个post processor,这会扫描每一个spring bean,查看是否已经存在注解对应的缓存。如果找到了,就会自动创建一个代理拦截方法调用,使用缓存的bean执行处理。

@Bean表示注入一个Bean对象,其中使用Jackson2JsonRedisSerializer来序列化和反序列化Redis的value值,StringRedisSerializer来序列化和反序列化redis的key值。需要一提的事,当多个系统使用同一个(实际通常是同一个Redis集群)时,需要保证每个系统的序列化和反序列化方式一致。

接着我们创建一个CacheDao,提供了set与get方法用于添加和获取缓存,缓存时间这里设置了一分钟。

@Repository
public class CacheDao {

    @Resource
    private RedisTemplate<String, Object> template;

    public void set(String key, Object value){
        System.out.println("添加缓存key->" + key);
        ValueOperations<String, Object> valueOperations = template.opsForValue();
        // 1分钟过期
        valueOperations.set(key, value, 1, TimeUnit.MINUTES);
    }

    public Object get(String key){
        System.out.println("获取缓存key->" + key);
        ValueOperations<String, Object> valueOperations = template.opsForValue();
        return valueOperations.get(key);
    }

}

那么缓存是否起到了作用呢,我们添加测试类UserApi,这是一个Controller,提供添加用户和获取用户的方法。

@RestController
@RequestMapping("user")
public class UserApi {

    @Autowired
    private UserService userService;

    /**
     * 添加用户
     * @param user
     */
    @PostMapping("addUser")
    public String addUser(User user){
        int id = userService.addUser(user);
        return "添加用户成功,主键id:" + id;
    }

    /**
     * 获取用户信息
     * @param id
     * @return
     */
    @GetMapping("getUser")
    public User getUser(@RequestParam(value = "id") int id){
        return userService.getUser(id);
    }

}

接下来是UserService,与之相对应的保存用户和获取用户的方法。IUserDao使用了mybatis,从数据库中获取,这里就不贴具体的代码了,而CacheDao则是上面展示过的,从缓存中读取。在添加用户的时候,我们将用户同时添加到缓存,或取用户的时候,我们将先从缓存中获取,如果缓存中不存在,则去数据库中查找,并同时进行缓存。

@Service
public class UserService {

    @Autowired
    private IUserDao userDao;

    @Autowired
    private CacheDao cacheDao;

    /**
     * 添加用户
     * @param user
     */
    public int addUser(User user){
        userDao.add(user);
        // 添加缓存
        cacheDao.set("user:id:" + user.getId(), user);
        return user.getId();
    }

    /**
     * 获取用户信息
     * @param id
     * @return
     */
    public User getUser(int id){
        // 从缓存中获取
        User user = (User)cacheDao.get("user:id:" + id);
        if(user == null){
            user = userDao.find(id);
            cacheDao.set("user:id:" + user.getId(), user);
        }
        return user;
    }

}

启动项目,下面我们通过swagger-ui进行测试(本项目已经集成了swagger2),我们添加张三、password的数据。
在这里插入图片描述
调用结果显示成功,且id为1。
在这里插入图片描述
然后我们去获取这条数据,使用/user/getUser接口,可以成功获取。
在这里插入图片描述
然后我们查看后台打印的信息,先是调用了缓存,然后又进行了查询数据库,诶……这好像不对啊,我刚刚添加时不是进行缓存了吗,为什么没有生效?
在这里插入图片描述
这是因为我在获取的时候已经超过了缓存生效时间1分钟,这时再次查询,就不会去数据库中查询了。
在这里插入图片描述

三、注解方式使用Redis

在Spring Boot项目中,注解满天飞,其实也可以使用注解形式进行缓存,该机制依赖于Spring CaChe,但是这种方式一般较少,因为其缓存机制依赖方法的入参和返回结果,缺乏一定的灵活性。

缓存的注解主要有@CachePut、@Cacheable、@CacheEvict,都作用于方法上。

  • @CachePut,添加缓存,key可以从入参中获取,value为方法返回值,注意并不是注解的value属性,value属性是用于表示缓存的空间,例如@CachePut(value=“user”, key="‘anno:user?’ + #user.id"),当然这必须要求方法有返回值,否则缓存的数据为空。
  • @Cacheable,既是获取,也是存储,存在缓存则从缓存中获取,不存在则进行缓存,缓存值为方法返回值
  • @CacheEvict表示清除缓存。
    在UserService中,我们新增updateUser更新用户,使用了@CacheEvict注解,getUserAnno获取用户,使用了@Cacheable。
 		/**
     * 修改用户信息
     * @param user
     */
    // @CachePut,每次都会进行缓存添加,缓存值为方法返回值,该方法无返回值无法使用
    // @CachePut(value="user", key="'anno:user:id:' + #user.id")
    // 直接删除缓存,下次获取重新查库
    @CacheEvict(value="user", key="'anno:user:id:' + #user.id")
    public void updateUser(User user){
        userDao.update(user);
    }


    /**
     * 获取用户信息,注解缓存
     * @param id
     * @return
     */
    // @Cacheable存在缓存则从缓存中获取,不存在则进行缓存,缓存值为方法返回值
    @Cacheable(value="user", key="'anno:user:id:' + #id")
    public User getUserAnno(int id){
        return userDao.find(id);
    }

多次调用getUserAnno方法,可以发现,仅第一遍进行了数据库访问,后面都没有调用数据库查询数据。我们接着再调用更新方法,然后在调用getUserAnno又从数据库中查取了。

源码地址:https://github.com/imyanger/springboot-project/tree/master/p10-springboot-redis

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值