具备相当的好的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCacheRedisGuava 的集成。

  • 基于 annotation 即可使得现有代码支持缓存
  • 开箱即用 Out-Of-The-Box,不用安装和部署额外第三方组件即可使用缓存
  • 支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 keycondition
  • 支持 AspectJ,并通过其实现任何方法的缓存支持
  • 支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

使用前后对比

  • 使用前 我们需要硬编码,如果切换Cache Client还需要修改代码,耦合度高,不易于维护

    public String get(String key) {
      String value = userMapper.selectById(key);
      if (value != null) {
          cache.put(key,value);
      }
      return value;
    }
    
  • 使用后 基于Spring Cache注解,缓存由开发者自己配置,但不用参与到具体编码

    @Cacheable(value = "user", key = "#key")
    public String get(String key) {
      return userMapper.selectById(key);
    }
    

属性配置

使用了Spring Cache后,能指定spring.cache.type就手动指定一下,虽然它会自动去适配已有Cache的依赖,但先后顺序会对Redis使用有影响(JCache -> EhCache -> Redis -> Guava

spring.redis.host=localhost
spring.redis.password=battcn
# 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配
spring.cache.type=redis
# 连接超时时间(毫秒)
spring.redis.timeout=10000
# Redis默认情况下有16个分片,这里配置具体使用的分片
spring.redis.database=0
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0

缓存Service

UserSerivce.java

public interface UserService {

    /**
     * 删除
     *
     * @param user 用户对象
     * @return 操作结果
     */
    User saveOrUpdate(User user);

    /**
     * 添加
     *
     * @param id key值
     * @return 返回结果
     */
    User get(Long id);

    /**
     * 删除
     *
     * @param id key值
     */
    void delete(Long id);
}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    private static final Map<Long, User> DATABASES = new HashMap<>();

    static {
        DATABASES.put(1L, new User(1L, "u1", "p1"));
        DATABASES.put(2L, new User(2L, "u2", "p2"));
        DATABASES.put(3L, new User(3L, "u3", "p3"));
    }

    @CachePut(value = "user", key = "#user.id")
    @Override
    public User saveOrUpdate(User user) {
        DATABASES.put(user.getId(), user);
        log.info("进入 saveOrUpdate 方法");
        return user;
    }

    @Cacheable(value = "user", key = "#id")
    @Override
    public User get(Long id) {
        // TODO 我们就假设它是从数据库读取出来的
        log.info("进入 get 方法");
        return DATABASES.get(id);
    }

    @CacheEvict(value = "user", key = "#id")
    @Override
    public void delete(Long id) {
        DATABASES.remove(id);
        log.info("进入 delete 方法");

    }

}

示例验证

@EnableAutoConfiguration
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Chapter7Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Chapter9ApplicationTests {

    private static final Logger log = LoggerFactory.getLogger(Chapter9ApplicationTests.class);

    @Autowired
    private UserService userService;

    @Test
    public void test() {
        final User user = userService.saveOrUpdate(new User(5L, "u5", "p5"));
        log.info("[saveOrUpdate] - [{}]", user);
        final User user1 = userService.get(5L);
        log.info("[get] - [{}]", user1);
        userService.delete(5L);
    }
}

启动测试类,结果和我们期望的一致,可以看到增删改查中,查询是没有日志输出的,因为它直接从缓存中获取的数据,而添加、修改、删除都是会进入方法内执行具体的业务代码,然后通过切面去删除掉Redis中的缓存数据。其中 # 号代表这是一个 SpEL 表达式,此表达式可以遍历方法的参数对象,具体语法可以参考 Spring 的相关文档手册。

2018-08-22 12:08:39.810  INFO 13308 --- [           main] xin.rtime.service.impl.UserServiceImpl   : 进入 saveOrUpdate 方法
2018-08-22 12:08:40.126  INFO 13308 --- [           main] io.lettuce.core.EpollProvider            : Starting without optional epoll library
2018-08-22 12:08:40.129  INFO 13308 --- [           main] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
2018-08-22 12:08:40.976  INFO 13308 --- [           main] xin.rtime.Chapter9ApplicationTests       : [saveOrUpdate] - [xin.rtime.model.User@283bb8b7]
2018-08-22 12:08:40.982  INFO 13308 --- [           main] xin.rtime.Chapter9ApplicationTests       : [get] - [xin.rtime.model.User@32d1d6c5]
2018-08-22 12:08:40.983  INFO 13308 --- [           main] xin.rtime.service.impl.UserServiceImpl   : 进入 delete 方法

注解介绍

  • 根据条件操作缓存

    根据条件操作缓存内容并不影响数据库操作,条件表达式返回一个布尔值,true/false,当条件为true,则进行缓存操作,否则直接调用方法执行的返回结果。

    • 长度: @CachePut(value = "user", key = "#user.id",condition = "#user.username.length() < 10") 只缓存用户名长度少于10的数据
    • 大小: @Cacheable(value = "user", key = "#id",condition = "#id < 10") 只缓存ID小于10的数据
    • 组合: @Cacheable(value="user",key="#user.username.concat(##user.password)")
    • 提前操作: @CacheEvict(value="user",allEntries=true,beforeInvocation=true) 加上beforeInvocation=true后,不管内部是否报错,缓存都将被清除,默认情况为false
  • 注解介绍

    • @Cacheable(根据方法的请求参数对其结果进行缓存)
      • key: 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合(如:@Cacheable(value="user",key="#userName"))
      • value: 缓存的名称,必须指定至少一个(如:@Cacheable(value="user") 或者 @Cacheable(value={"user1","use2"}))
      • condition: 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存(如:@Cacheable(value = "user", key = "#id",condition = "#id < 10"))
    • @CachePut(根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用)
      • key: 同上
      • value: 同上
      • condition: 同上
    • @CachEvict(根据条件对缓存进行清空)
      • key: 同上
      • value: 同上
      • condition: 同上
      • allEntries: 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存(如:@CacheEvict(value = "user", key = "#id", allEntries = true))
      • beforeInvocation: 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存(如:@CacheEvict(value = "user", key = "#id", beforeInvocation = true))
Copyright © wswzms.top 2019 all right reserved,powered by Gitbook该文件修订时间: 2019-04-17 17:03:57

results matching ""

    No results matching ""