整合Spring Cache

整合Spring Cache

Spring Cache是Spring框架为我们提供的一个缓存抽象层,无论我们使用一个map作为本地缓存,还是EnCache和Redis这种专业的缓存,都可以使用Spring Cache方便地集成,使用起来也很简单,使用注解在业务逻辑方法上标注,即可完成缓存操作,而不用将缓存操作和业务代码耦合在一起

基本使用

使用Spring Cache,首先需要导入对应的starter依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

这里我使用最常用的缓存方案,使用Redis作为缓存,在使用Spring Cache整合Redis之前,就需要先配置Redis

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 1
    password: 12345

然后配置缓存的相关配置

spring:
  cache:
  	# 缓存类型
    type: redis
    redis:
      # 缓存失效时间
      time-to-live: 60000
      # 是否缓存空值
      cache-null-values: true
	  # 是否启用前缀
      use-key-prefix: true
      # 前缀
      key-prefix: cache_

然后在主启动类上或者配置类上添加@EnableCaching注解开启缓存功能

@EnableCaching
@SpringBootApplication
public class SpringDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringDemoApplication.class, args);
    }

}

Spring Cache默认使用JDK序列化写入Redis服务器,需要缓存的实体类实现Serializable接口

但是这样做缓存服务器中的数据是不可读的,而且不兼容其他非Java构建的服务,我们可以使用以下配置,将序列化机制改为JSON序列化方式

@Configuration
@EnableConfigurationProperties(CacheProperties.class)
public class RedisConfig {

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        CacheProperties.Redis redis = cacheProperties.getRedis();
        if (redis.getTimeToLive() != null) {
            configuration = configuration.entryTtl(redis.getTimeToLive());
        }
        if (redis.getKeyPrefix() != null) {
            configuration = configuration.prefixCacheNameWith(redis.getKeyPrefix());
        }
        if (!redis.isCacheNullValues()) {
            configuration = configuration.disableCachingNullValues();
        }
        if (!redis.isUseKeyPrefix()) {
            configuration = configuration.disableKeyPrefix();
        }
        return configuration;
    }

}

这里的配置会将配置文件中的配置覆盖掉,所以需要注入CacheProperties类,然后再次判断配置文件中的值,重新设置

最后只需要在需要缓存的方法上添加@Cacheable注解即可,Spring Cache会缓存方法的返回值,调用方法时先查询缓存,如果缓存未命中才执行方法

@Cacheable注解需要配置两个属性,一个是cacheNames,这个属性指定缓存的分区,如果需要缓存在多个分区,可以使用数组的形式

还有一个属性是key,这个属性指定了缓存在Redis中的键的值,使用SpEL表达式,这里使用方法名作为键的值

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Cacheable(cacheNames = "user", key = "#root.methodName")
    @Override
    public List<User> queryUsers() {
        System.out.println("查询数据库");
        return userMapper.selectList(null);
    }
}

编写一个测试接口来测试一下

@RestController
public class UserController {
    
    @Autowired
    UserService userService;

    @RequestMapping("/getUsers")
    public List<User> getUsers() {
        return userService.queryUsers();
    }
}

第一次调用正常执行queryUsers方法,因为此时缓存中还没有数据

在这里插入图片描述

不过这时候查看Redis中的数据,发现已经有缓存了

在这里插入图片描述

可以看到缓存的键为配置文件中指定的前缀+分区名+注解中指定的键名

后续在缓存失效时间内多次调用接口,就不会再执行queryUsers方法,而是直接从缓存中返回数据

读模式加锁

缓存读模式中的三个问题在Spring Cache中都可以得到解决,缓存穿透问题可以通过设置缓存空对象来解决,缓存雪崩问题可以通过设置缓存失效时间来解决,而缓存击穿问题可以通过设置加锁来解决

设置加锁在Spring Cache中也可以轻松实现,在@Cacheable注解中设置sync属性为true即可,这样在缓存失效时,如果有多个请求同时调用该方法,会以加锁排队的方式同步调用,底层是使用synchronized锁机制同步执行方法

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Cacheable(cacheNames = "user", key = "#root.methodName", sync = true)
    @Override
    public List<User> queryUsers() {
        System.out.println("查询数据库");
        return userMapper.selectList(null);
    }
}

失效模式

在缓存写模式中,需要考虑数据一致性问题,有两种方法,失效模式和双写模式

失效模式既如果数据发生改变,直接删除缓存,等到下一次查询数据时,会因为缓存未命中再次查询数据库,将最新的数据缓存到缓存服务器中

在Spring Cache中使用失效模式,只需要在修改数据的方法上添加@CacheEvict注解即可,然后使用cacheNames和key属性分别指定分区名和缓存的键名就可以在调用该方法的时候删除对应的缓存

以下示例在调用modifyUser方法进行用户修改的时候删除queryUsers方法在缓存服务器中的缓存,注意key属性使用SpEL表达式进行赋值,需要在""中再加上''才是指定字符串的含义

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Cacheable(cacheNames = "user", key = "#root.methodName", sync = true)
    @Override
    public List<User> queryUsers() {
        System.out.println("查询数据库");
        return userMapper.selectList(null);
    }

    @CacheEvict(cacheNames = "user", key = "'queryUsers'")
    @Override
    public void modifyUser(User user) {
        userMapper.updateById(user);
    }
}

如果需要删除分区中所有的缓存,只需要将allEntries属性设置为true既可

双写模式

双写模式既在数据发生改变时,将最新的数据即时地写入缓存中

在Spring Cache中使用双写模式,只需要在修改数据的方法上添加@CachePut注解即可,使用方式和@CacheEvict注解相同

注意@CachePut注解标注的方法也需要有对应返回值,缓存的最新数据为该方法的返回值,如以下示例中调用addUser方法时,插入数据后会调用queryUsers方法再次查询数据库得到最新的数据,然后用新数据将旧的缓存覆盖

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Cacheable(cacheNames = "user", key = "#root.methodName", sync = true)
    @Override
    public List<User> queryUsers() {
        System.out.println("查询数据库");
        return userMapper.selectList(null);
    }


    @CachePut(cacheNames = "user", key = "'queryUsers'")
    @Override
    public List<User> addUser(User user) {
        userMapper.insert(user);
        return queryUsers();
    }


    @CachePut(cacheNames = "user", key = "'queryUsers'")
    @Override
    public List<User> addUser(User user) {
        userMapper.insert(user);
        return queryUsers();
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值