2020-12-01 springboot缓存

一、JSR-107

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

1、CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。

2、CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。

3、Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。

4、Entry是一个存储在Cache中的key-value对。

5、Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

二、Spring缓存抽象

Spring从开始定义了org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口来统一不同的缓存技术,并提供了提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等,支持使用JCache(JSR-107)注解简化我们开发;
1、使用@Cacheable缓存
(1)、导入依赖

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

(2)、@EnableCaching开启注解缓存

@EnableCaching
@MapperScan("com.example.springbootcache.mapper")
@SpringBootApplication
public class SpringbootCacheApplication {

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

}

(3)、使用注解进行缓存

 @Cacheable(value = "emp",key = "#id",condition = "#id>1")
    public Employee getEmp(Integer id){
        Employee byId = employeeMapper.getById(id);
        System.out.println("查询数据库");
        return byId;
    }

注解作用:

value:指定缓存组件Cache的名称,并将方法执行结果存储在其中。一个Cache可以储存多个key-value缓存键值对。可以同时指定多个,让一个键值对存储在多个Cache中。

key:缓存的键,如果不指定,则默认是使用方法参数的值,指定的话可以使用SpEL表达式。例如:#id。	
	除获取参数值外,Cache中SpEL表达式还可以获取如下值:
		#root.methodName: 当前被调用的方法名
		#root.method.name: 当前被调用的方法名
		#root.target:当前被调用的目标对象
		#root.targetClass:当前被调用的目标对象的类
		#root.args[0]:当前参数列表的第一个值
		#a0:方法的参数,a为参数名臣,0为索引
		#result: 方法执行的返回值
condition:指定符合条件的情况下才缓存
unless:否定缓存;即当unless指定的条件为满足是,方法的返回值就不会被缓存
cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
 sync:是否使用异步模式

2、缓存原理分析
(1)、缓存自动配置类,根据注解可以看出,这个类中导入了一个CacheConfigurationImportSelector,用于收集需要导入的配置类。

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({CacheManager.class})
@ConditionalOnBean({CacheAspectSupport.class})
@ConditionalOnMissingBean(
    value = {CacheManager.class},
    name = {"cacheResolver"}
)
@EnableConfigurationProperties({CacheProperties.class})
@AutoConfigureAfter({CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class})
@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class, CacheAutoConfiguration.CacheManagerEntityManagerFactoryDependsOnPostProcessor.class})
public class CacheAutoConfiguration {
    public CacheAutoConfiguration() {
    }

(2)、这个类中调用了一个selectImports方法,这个方法中的CacheType为一个枚举类,定义了10种缓存类型,通过values得到所有10种类中,遍历后取得到所有的缓存的类型,并导入。

static class CacheConfigurationImportSelector implements ImportSelector {
        CacheConfigurationImportSelector() {
        }

        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            CacheType[] types = CacheType.values();
            String[] imports = new String[types.length];

            for(int i = 0; i < types.length; ++i) {
                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
            }

            return imports;
        }
    }
public enum CacheType {
    GENERIC,
    JCACHE,
    EHCACHE,
    HAZELCAST,
    INFINISPAN,
    COUCHBASE,
    REDIS,
    CAFFEINE,
    SIMPLE,
    NONE;

在这里插入图片描述
(3)、经过断电调试,最终确定SimpleCacheConfiguration为默认生效的配置类。这个类为容器中注入了一个ConcurrentMapCacheManager,通过这个Manager可以获取到Cache组件,ConcurrentMapCache调用其put方法数据保存在ConcurrentMap中。

 @Bean
    ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers) {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        List<String> cacheNames = cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            cacheManager.setCacheNames(cacheNames);
        }

        return (ConcurrentMapCacheManager)cacheManagerCustomizers.customize(cacheManager);
    }
 protected Cache createConcurrentMapCache(String name) {
        SerializationDelegate actualSerialization = this.isStoreByValue() ? this.serialization : null;
        return new ConcurrentMapCache(name, new ConcurrentHashMap(256), this.isAllowNullValues(), actualSerialization);
    }

(4)、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
* (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
(5)、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;

*      key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
 *          SimpleKeyGenerator生成key的默认策略;
 *                  如果没有参数;key=new SimpleKey();
 *                  如果有一个参数:key=参数的值
 *                  如果有多个参数:key=new SimpleKey(params);

key和KeyGenerator只能同时使用一个。 KeyGenerator使用方法如下:
(A)定义keyGenenrator

@Configuration
public class MyKeygenerator {

    @Bean
    public KeyGenerator myKeyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                return method.getName()+"["+ Arrays.asList(objects)+"]";
            }
        };
    }

(B)、调用keyGenenrator
此处的其对应的值需是定义的keyGenerator的方法名

  @Cacheable(value = "emp",keyGenerator = "myKeyGenerator")
    public Employee getEmp(Integer id){
        Employee byId = employeeMapper.getById(id);
        System.out.println("查询数据库");
        return byId;
    }

(6)没有查到缓存就调用目标方法;
(7)、将目标方法返回的结果,放进缓存中
2、@CachePut的使用
@CachePut:既调用方法,又更新缓存数据;例如在修改操作时,修改了数据库的某个数据,同时更新缓存。
默认情况下:先调用目标方法,之后再将目标方法的结果缓存起来。在一下两种情况下,修改更新后,getEmp中的缓存值不会发生变化,因为updateEmp()getEmp()的key值不一致,导致存储为两个键值对。若要一直,就要保证键相同,可以在updateEmp()中使用key = "#employee.id"或者"#result.id",但是@Cacheable不可已使用"#result.id",因为它是在方法执行前进行缓存查询和创建Cache,此时没有result结果。

  @CachePut(/*value = "emp",*/key = "#employee")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.update(employee);
        return employee;
    }

  @Cacheable(value = "emp",key = "#id")
    public Employee getEmp(Integer id){
        Employee byId = employeeMapper.getById(id);
        System.out.println("查询数据库");
        return byId;
    }

3、@CacheEvict的使用
@CacheEvict:清除缓存数据

@CacheEvict(value="emp",key = "#id",beforeInvocation = true)
    public void deleteEmp(Integer id){
      empMapper.deleteEmpById(id);
    }

注解内容说明:

key:根据键指定清除缓存
allEntries:默认为false。若配置`allEntries = true`,则清除当前Cache中的所有缓存内容,此时可不再配置key。
beforeInvocation:默认为false,即在方法执行完毕后再清除缓存。但当方法出现异常时,则不会清除。
							  设置为true,则代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除

4、@Caching 定义复杂的缓存规则

 @Caching(
          cacheable = {@Cacheable("……"),@Cacheable("……")},
            put = {@CachePut("……"),@CachePut("……")},
            evict = {@CacheEvict("……"),@CacheEvict("……")}
    )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值