SpringBoot 之 Cache

1、缓存核心接口

Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。

  • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可 以在运行期访问多个CachingProvider。
  • CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache 存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
  • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个 CacheManager所拥有。
  • Entry是一个存储在Cache中的key-value对。
  • Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期 的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

示意图:

2、Spring缓存抽象

Spring定义了org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口来统一不同的缓存技术; 并支持使用JCache(JSR-107)注解简化我们开发。(跟面向JDBC编程是一个道理,统一一个规范,统一面向jdbc编程。)

  • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合。
  • Cache接口下Spring提供了各种xxxCache的实现:
    在这里插入图片描述
  • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
  • 使用Spring缓存抽象时我们需要关注以下两点:
    1、确定方法需要被缓存以及他们的缓存策略
    2、从缓存中读取之前缓存存储的数据

缓存注解。
在这里插入图片描述
@Cacheable/@CachePut/@CacheEvict 主要的参数。
在这里插入图片描述
参数值支持支持SpEL表达式。
在这里插入图片描述

3、SpringBoot缓存工作原理

1、首先分析自动配置类:CacheAutoConfiguration
在这里插入图片描述
2、这个自动配置中导入了一个类CacheConfigurationImportSelector,这个类会引入一些缓存配置类。
在这里插入图片描述
3、缓存配置类:CacheProperties
在这里插入图片描述
4、CaffeineCacheConfiguration这个配置类给容器中注册了一个CacheManager。
在这里插入图片描述
5、缓存方法运行之前,先按照cacheNames查询缓存组件,第一次获取缓存如果没有缓存创建一个。
在这里插入图片描述

4、SpringBoot中Cache缓存的使用

1、导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

2、在主程序中开启缓存注解
在这里插入图片描述
3、@Cacheable缓存注解的使用 (标注在service业务层方法上)
执行流程:先执行@Cacheable注解中的getCache(String name)方法,根据name判断ConcurrentMap中是否有此缓存,如果没有缓存那么创建缓存并保存数据,另外service层的方法也会执行。如果有缓存不再创建缓存,另外service层的方法也不会执行。
总结:先执行@Cacheable----->再执行service层的方法

service层代码:
第一次查询数据库打印service类方法日志,并把数据保存到Cahce中。
第二次传入相同参数不再执行service类方法,不会打印日志,查询的数据直接从缓存中获取。

/*1. @Cacheable的几个属性详解:
 *       cacheNames/value:指定缓存组件的名字。
 *       key:缓存数据使用的key,可以用它来指定,默认使用方法参数的值。
 *       keyGenerator:作用和key一样,二选一。
 *       cacheManager和cacheResolver作用相同:指定缓存管理器,二选一。
 *       condition:指定符合条件才缓存。
 *                  比如:condition="#id>3"(也就是说传入的参数id>3才缓存数据)
 *       unless:否定缓存,当unless为true时不缓存,可以获取方法结果进行判断。
 *       sync:是否使用异步模式。
 */
//@Cacheable(cacheNames= "person")
//@Cacheable(cacheNames= "person",key="#id",condition="#id>3")
@Cacheable(cacheNames= "person",key="#id")
public Person query(Long id) {
    log.info("查询数据库。id = {}", id);
    return dao.query(id);
}

4、@CachePut必须结合@Cacheable一起使用,否则没什么意义
@CachePut的作用:即调用方法,又更新缓存数据 ,修改了数据库中的数据,同时又更新了缓存。

// @CachePut(value = "person", key = "#person.id")
@CachePut(value = "person", key = "#result.id")
public Person update(Person person) {  
   	dao.update(person);
    return person;
}

5、@CacheEvict也是结合@Cacheable一起使用才有意义
@CacheEvict的作用:清除缓存中的指定数据或清除缓存中所有数据

/**
 * @CacheEvict:清除缓存
 *    1.key:指定要清除缓存中的某条数据
 *    2.allEntries=true:删除缓存中的所有数据
 *    beforeInvocation=false:默认是在方法之后执行清除缓存
 *    3.beforeInvocation=true:现在是在方法执行之前执行清除缓存,
 *                          作用是:只清除缓存、不删除数据库数据
 */
//@CacheEvict(cacheNames = "person",key = "#id")
@CacheEvict(cacheNames = "person",allEntries=true)
public void delete(Long id) {
    dao.delete(id);
}

6、@Caching是@Cacheable、@CachePut、@CacheEvict注解的组合
@Caching的作用:此注解用于复杂的缓存操作

/**
 *   @Caching是 @Cacheable、@CachePut、@CacheEvict注解的组合
 *   以下注解的含义:
 *   1.当使用指定名字查询数据库后,数据保存到缓存
 *   2.现在使用id、age就会直接查询缓存,而不是查询数据库
 */
@Caching(
        cacheable = { @Cacheable(value = "person", key = "#name") },
        put = {
                @CachePut(value = "person", key = "#result.id"),
                @CachePut(value = "person", key = "#result.age")
        }
)
public Person queryByName(String name) {
    Person person = dao.queryByName(name);
    return person;
}

7、@CacheConfig主要用于配置该类中会用到的一些共用的缓存配置
@CacheConfig的作用:抽取@Cacheable、@CachePut、@CacheEvict的公共属性值

@Service
@CacheConfig(cacheNames = "person") //将cacheNames抽取出来
public class PersonService {

    @Cacheable(key="#id")
    public Person query(Long id) {
        log.info("查询数据库。id = {}", id);
        return personMap.get(id);
    }
    @CachePut(key = "#result.id")
    public Person update(Person person) {
        System.out.println(person.toString());
        personMap.put(person.getId(), person);
        return person;
    }
 }

5、Spring Boot缓存配置

在缓存配置中,比如spring.cache.caffeine.spec=maximumSize=500,expireAfterWrite=10s,所有的缓存的到期策略都是一样的,如果我们要实现不同数据的缓存到期时间不一致,可以用自定义CacheManager。
1、application.yml

caching:
  specs:
    student:
      timeout: 2
    person:
      timeout: 5

上面配置中,缓存person是10秒过期,student是2秒过期。

2、CacheConfiguration

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Ticker;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Configuration
@ConfigurationProperties(prefix = "caching")
@Data
@Slf4j
public class CacheConfiguration {

    @Data
    public static class CacheSpec {
        private Integer timeout;
        private Integer max = 200;
    }

    private Map<String, CacheSpec> specs;

    @Bean
    public CacheManager cacheManager(Ticker ticker) {
        SimpleCacheManager manager = new SimpleCacheManager();
        if (specs != null) {
            List<CaffeineCache> caches = specs.entrySet()
                    .stream()
                    .map(entry -> buildCache(entry.getKey(), entry.getValue(), ticker))
                    .collect(Collectors.toList());
            manager.setCaches(caches);
        }
        return manager;
    }

    private CaffeineCache buildCache(String name, CacheSpec cacheSpec, Ticker ticker) {
        log.info("Cache {} specified timeout of {} min, max of {}", name, cacheSpec.getTimeout(), cacheSpec.getMax());
        final Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder()
                .expireAfterWrite(cacheSpec.getTimeout(), TimeUnit.SECONDS)
                .maximumSize(cacheSpec.getMax())
                .ticker(ticker);
        return new CaffeineCache(name, caffeineBuilder.build());
    }

    // 指定时间源
    @Bean
    public Ticker ticker() {
        return Ticker.systemTicker();
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值