侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

行动起来,活在当下

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

SpringBoot(29)之缓存支持

zze
zze
2018-07-18 / 0 评论 / 0 点赞 / 614 阅读 / 26446 字

不定期更新相关视频,抖音点击左上角加号后扫一扫右方侧边栏二维码关注我~正在更新《Shell其实很简单》系列

介绍

Spring 从 3.1 版本开始定义了 org.springframework.cache.Cacheorg.springframework.cache.CacheManager 接口来统一不同的缓存技术,并支持使用 JCache(JSR107)注解简化我们开发。

关于 JSR107:

Java Caching 定义了 5 个核心接口,分别是 CachingProviderCacheManagerCacheEntryExpiry

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

由于 JSR107 的使用相对来说比较繁杂,所以这里我们使用 Spring 提供的缓存抽象。
Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合。 Cache 接口下 Spring 提供了各种 Cache 的实现,如 RedisCacheEhCacheCacheConcurrentMapCache 等。

关于 Spring 缓存抽象的使用:

每次调用具有缓存功能的方法时,Spring 会检查指定目标方法是否已经被调用过,如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回,下次调用就直接从缓存中获取。

使用 Spring 缓存抽象时我们需要关注以下两点:

  1. 确定方法需要被缓存以及它们的缓存策略。
  2. 从缓存中读取之前缓存存储的数据。
Cache缓存接口,定义缓存操作。实现有:RedisCacheEhCacheCacheConcurrentMapCache 等。
@CacheManager缓存管理器,管理各种缓存(Cache)组件。
@Cacheable主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。
@CacheEvict清空缓存。
@CachePut保证方法被调用,又希望结果被缓存。
@EnableCaching开启基于注解的缓存。
keyGenerator缓存数据时 key 的生成策略。
serialize缓存数据时 value 的序列化策略。

CacheManager 是用来管理多个 Cache 组件的,对缓存的 CRUD 操作实际上还是通过 Cache 组件,而每一个 Cache 组件有自己唯一名称。

使用示例

搭建测试环境

1、新建 SpringBoot 项目,引入如下场景启动器:CacheWebMysqlMyBatis

2、初始化测试数据:

-- user.sql
SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(40) DEFAULT NULL,
  `gender` int(11) DEFAULT NULL COMMENT '0:女 1:男',
  `birthday` date DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '张三', '1', '1997-02-23', '北京');
INSERT INTO `user` VALUES ('2', '李四', '0', '1998-02-03', '武汉');
INSERT INTO `user` VALUES ('3', '王五', '1', '1996-06-04', '上海');

3、编写与表对应的 JavaBean:

// com.springboot.bean.User
import java.util.Date;

public class User {
    private Integer id;
    private String name;
    private Integer gender;
    private Date birthday;
    private String address;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                ", birthday=" + birthday +
                ", address='" + address + '\'' +
                '}';
    }
}

4、配置数据库连接信息:

# application.yml
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://192.168.202.136:3306/springboot_cache
    driver-class-name: com.mysql.jdbc.Driver

5、编写测试 Mapper:

// com.springboot.mapper.UserMappper
package com.springboot.mapper;

import com.springboot.bean.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMappper {
    @Select("select * from user")
    public List<User> getAll();

    @Select("select * from user where id=#{id}")
    public User getById(Integer id);

    @Delete("delete from user where id=#{id}")
    public void deleteById(Integer id);

    @Options(useGeneratedKeys = true,keyProperty = "id")
    @Insert("insert into user(name,gender,birthday,address) values(#{name},#{gender},#{birthday},#{address})")
    public void add(User user);

    @Update("update user set name=#{name},gender=#{gender},birthday=#{birthday},address=#{address} where id=#{id}")
    public void update(User user);

    @Select("select * from user where name=#{name}")
    public User getByName(String name);
}

6、编写 Service 并使用注解缓存功能:

// com.springboot.service.UserService
import com.springboot.bean.User;
import com.springboot.mapper.UserMappper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    @Autowired
    private UserMappper userMappper;

    @Cacheable(cacheNames = {"users"})
    public List<User> getAll(){
        System.out.println("UserService.getAll() 执行了");
        return userMappper.getAll();
    }
}

7、配置 mapper 包扫描,开启注解缓存:

// com.springboot.CacheTestApplication
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching // 开启注解缓存
@MapperScan("com.springboot.mapper") // mapper 包扫描
public class CacheTestApplication {

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

}

8、编写 Controller 并测试:

// com.springboot.controller.UserController
import com.springboot.bean.User;
import com.springboot.mapper.UserMappper;
import com.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users")
    public List<User> getAll(){
        return userService.getAll();
        /*
        首次访问会输出以下内容:
            UserService.getAll() 执行了
        后续访问不会输出,说明实际上并没有执行 userService.getAll() 方法,即未查询数据库从中获取数据,而是从缓存中获取
        缓存成功
         */
    }
}

@Cacheable

主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。

属性

cacheNames/value:指定 Cache 组件名称。

key:指定缓存数据所使用的 key,默认 key 使用参数值。比如 id1,那么在 Cache 组件中的 key 就为 1

key 还可以通过 SpEL 表达式进行取值。规则如下:

名字位置描述实力
methodNameroot object当前被调用的方法名#root.methodName
methodroot object当前被调用的方法对象#root.method.name
targetroot object当前被调用的目标对象#root.target
targetClassroot object当前被调用的目标对象类#root.targetClass
argsroot object当前被调用方法的参数列表#root.args[0]
cachesroot object当前被调用方法使用的缓存组件列表,如 @Cacheable(value={"cache1","cache2"}) 就有两个缓存组件#root.caches[0].name
argument nameevaluation context可以直接使用 #参数名 来获取对应参数值,也可以使用 #p0 或 #a0 的形式,0 代表参数索引#id、#a0、#p0
resultevaluation context获取方法执行后的返回值#result
  • keyGenerator:通过 IoC 容器中的生成器组件 id 来指定 key 的生成器,keykeyGenerator 二选一使用。
  • cacheManager:指定缓存管理器 bean。
  • cacheSolver:指定缓存解析器 bean。
  • condition:指定条件,当符合指定条件时才进行缓存,也可使用 SpEL 表达式同 key
  • unless:指定条件,当符合指定条件时不进行缓存,也可使用 SpEL 表达式同 key
  • sync:是否使用异步模式,如果启用,那么 unless 属性失效。

示例

// com.springboot.service.UserService#getById
@Cacheable(cacheNames = {"users"})
public User getById(Integer id) {
    System.out.println("UserService.getById(" + id + ") 执行了");
    return userMappper.getById(id);
}
// com.springboot.controller.UserController#getById
@GetMapping("/user/{id}")
public User getById(@PathVariable Integer id) {
    return userService.getById(id);
    /*
    首次访问 http://localhost:8080/user/1 会输出一下内容:
        UserService.getById(1) 执行了
    后续访问不会输出,说明实际上并没有执行 userService.getById(1) 方法,即未查询数据库从中获取数据,而是从缓存中获取
    而如果访问 http://localhost:8080/user/2 会输出一下内容:
        UserService.getById(2) 执行了
    后续访问也不会输出,即会自动根据参数的不同进行缓存
    OK 缓存成功
     */
}

@CachePut

主要针对方法配置,目标方法正常执行完成后将其结果更新到缓存。

属性

@Cacheable 注解。

示例

// com.springboot.service.UserService#update
/*
注意:这里要保证 key 与 getById 方法所使用 key 的生成后的值相同,否则会出现更新缓存后通过 getById 获取数据依旧为旧数据的情况
 */
@CachePut(cacheNames = {"users"}, key = "#user.id")
public User update(User user) {
    System.out.println("UserService.update(" + user + ") 执行了");
    userMappper.update(user);
    return user;
}
// com.springboot.controller.UserController#update
@PutMapping("/user")
public User update(User user){
    return userService.update(user);
    /*
    1、以 get 方式访问 localhost:8080/user/1 会输出一下内容:
            UserService.getById(1) 执行了
        后续访问不会输出
    2、以 put 方式访问 localhost:8080/user,修改数据,输出以下内容:
            UserService.update(User{id=1, name='张三new', gender=0, birthday=Wed Apr 03 00:00:00 CST 1996, address='深圳'}) 执行了
        每次访问都会输出
    3、以 get 方式访问 localhost:8080/user/1 不会输出内容,返回数据是更新后的数据
     */
}

@CacheEvict

主要针对方法配置,默认在目标方法正常执行完成后清除缓存。

属性

  • beforeInvocation:默认为 false,标识是否在目标方法执行之前清除缓存。
  • allEntries:默认为 false,标识是否清除缓存组件中所有内容。

其它同 @Cacheable 注解。

示例

// com.springboot.service.UserService#delete
/*
删除缓存,key 可以不指定,因为 key 默认就是以 id 为基础生成的
 */
@CacheEvict(cacheNames = {"users"}, key = "#id")
public void delete(Integer id) {
    System.out.println("UserService.delete(" + id + ") 执行了");
}
// com.springboot.controller.UserController#delete
@DeleteMapping("/user/{id}")
public void delete(@PathVariable Integer id){
    userService.delete(id);
    /*
    1、以 get 方式访问 localhost:8080/user/1 会输出以下内容
            UserService.getById(1) 执行了
        后续访问不会输出
    2、以 delete 方式访问 localhost:8080/user/1 会输出以下内容
            UserService.delete(1) 执行了
        每次访问都会输出
    3、以 get 方式再次访问 localhost:8080/user/1 会输出以下内容
            UserService.getById(1) 执行了
    即缓存被删除了重新执行方法获取了数据
     */
}

@Caching

主要针对方法配置,为 @Cacheable@CachePut@CacheEvict 的组合注解,常用于定制较复杂的缓存策略。

属性

  • cacheable:放置 @Cacheable 注解数组。
  • put:放置 @CachePut 注解数组。
  • evict:放置 @CacheEvict 注解数组。

示例

// com.springboot.service.UserService#getByName
@Caching(
        cacheable = {
                // 以 name 生成 key 进行缓存
                @Cacheable(value = "users", key = "#name")
        },
        put = {
                // 以 id 生成 key ,且执行结果不为空时更新缓存
                @CachePut(value = "users", key = "#result.id", condition = "#result != null")
        },
        evict = {
                // name 为 deleteAll 清除所有缓存
                @CacheEvict(value = "users", condition = "#name=='deleteAll'", allEntries = true)
        }
)
public User getByName(String name) {
    System.out.println("UserService.getByName(" + name + ") 执行了");
    return userMappper.getByName(name);
}
// com.springboot.controller.UserController#getByName
@GetMapping("/user/name/{name}")
public User getByName(@PathVariable String name){
    return userService.getByName(name);
    /*
    1、以 get 方式请求 localhost:8080/user/name/李四,输出以下内容:
            UserService.getByName(李四) 执行了
        每次访问都会输出,因为 @CachePut 需要执行结果更新缓存
    2、以 get 方式请求 localhost:8080/user/2,没有输出内容,因为通过 [1] 已经完成了 id 为 2 的缓存,直接从缓存中取出结果返回了
    3、以 get 方式请求 localhost:8080/user/name/deleteAll,输出以下内容:
            UserService.getByName(deleteAll) 执行了
        每次访问都会输出,因为 @CachePut 需要执行结果更新缓存
    4、以 get 方式请求 localhost:8080/user/2,输出以下内容:
            UserService.getById(2) 执行了
        后续访问不会输出,这次输出的原因是通过 [3] 清楚了所有缓存
     */
}

@CacheConfig

通常用来标注在类上,用来定义公共缓存配置。

属性

具有如下属性:cachenameskeyGeneratorcacheManagercacheResolver,可参考 @Cacheable 注解属性。

示例

// com.springboot.service.UserService
import com.springboot.bean.User;
import com.springboot.mapper.UserMappper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

/**
 * 因为下面方法都是使用同一个 Cache 组件,所以可以在类上一次性指定所有方法使用的 Cache 组件名称
 */
@CacheConfig(cacheNames = {"users"} )
@Service
public class UserService {
    @Autowired
    private UserMappper userMappper;

    @Cacheable(keyGenerator = "myKeyGenerator")
    public User getById(Integer id) {
        System.out.println("UserService.getById(" + id + ") 执行了");
        return userMappper.getById(id);
    }

    @CachePut(key = "#user.id")
    public User update(User user) {
        System.out.println("UserService.update(" + user + ") 执行了");
        userMappper.update(user);
        return user;
    }

    @CacheEvict(key = "#id")
    public void delete(Integer id) {
        System.out.println("UserService.delete(" + id + ") 执行了");
    }

    @Caching(
            cacheable = {
                    @Cacheable(key = "#name")
            },
            put = {
                    @CachePut(key = "#result.id", condition = "#result != null")
            },
            evict = {
                    @CacheEvict(condition = "#name=='deleteAll'", allEntries = true)
            }
    )
    public User getByName(String name) {
        System.out.println("UserService.getByName(" + name + ") 执行了");
        return userMappper.getByName(name);
    }
}

原理分析及小结

源码

依旧是从自动配置类入手以 @Cacheable 注解执行流程进行分析:

// org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
package org.springframework.boot.autoconfigure.cache;

@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
        RedisAutoConfiguration.class })
// <1>
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {

    // 注册缓存管理器定制器
    @Bean
    @ConditionalOnMissingBean
    public CacheManagerCustomizers cacheManagerCustomizers(
            ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) {
        return new CacheManagerCustomizers(customizers.getIfAvailable());
    }

    @Bean
    public CacheManagerValidator cacheAutoConfigurationValidator(
            CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
        return new CacheManagerValidator(cacheProperties, cacheManager);
    }

    @Configuration
    @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
    @ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
    protected static class CacheManagerJpaDependencyConfiguration
            extends EntityManagerFactoryDependsOnPostProcessor {

        public CacheManagerJpaDependencyConfiguration() {
            super("cacheManager");
        }

    }

    static class CacheManagerValidator implements InitializingBean {

        private final CacheProperties cacheProperties;

        private final ObjectProvider<CacheManager> cacheManager;

        CacheManagerValidator(CacheProperties cacheProperties,
                ObjectProvider<CacheManager> cacheManager) {
            this.cacheProperties = cacheProperties;
            this.cacheManager = cacheManager;
        }

        @Override
        public void afterPropertiesSet() {
            Assert.notNull(this.cacheManager.getIfAvailable(),
                    "No cache manager could "
                            + "be auto-configured, check your configuration (caching "
                            + "type is '" + this.cacheProperties.getType() + "')");
        }

    }

    static class CacheConfigurationImportSelector implements ImportSelector {

        // <2>
        @Override
        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;
        }
    }
}

看到 <1> 处,该行是通过 @Import(CacheConfigurationImportSelector.class) 导入缓存配置类, CacheConfigurationImportSelector 为自动配置类中的内部类,对应的导入方法为 <2> 处的 selectImports 方法。通过调试会发现它实际导入了如下配置类:

org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration

那么实际生效的是哪个配置类呢?可以在配置文件中开启 debug 模式,接着在控制台中就可以看到:

SimpleCacheConfiguration matched:
  - Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)
  - @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition)

即生效的配置类为 org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration 。查看该配置类:

// org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
package org.springframework.boot.autoconfigure.cache;

@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {

    private final CacheProperties cacheProperties;

    private final CacheManagerCustomizers customizerInvoker;

    SimpleCacheConfiguration(CacheProperties cacheProperties,
            CacheManagerCustomizers customizerInvoker) {
        this.cacheProperties = cacheProperties;
        this.customizerInvoker = customizerInvoker;
    }

    // <3>
    @Bean
    public ConcurrentMapCacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        List<String> cacheNames = this.cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            cacheManager.setCacheNames(cacheNames);
        }
        return this.customizerInvoker.customize(cacheManager);
    }
}

<3> 处可以看到往 IoC 容器中注册了一个 org.springframework.cache.concurrent.ConcurrentMapCacheManager,该类实现了 org.springframework.cache.CacheManager 接口,可以使用它的 getCache(String) 方法来获取缓存组件:

// org.springframework.cache.concurrent.ConcurrentMapCacheManager#getCache
public Cache getCache(String name) {
    // this.cacheMap : private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16);
    Cache cache = (Cache)this.cacheMap.get(name);
    if (cache == null && this.dynamic) {
        synchronized(this.cacheMap) {
            // 从 ConcurrentMap 对象中获取 Cache 组件
            cache = (Cache)this.cacheMap.get(name);
            if (cache == null) { // 如果获取的 Cache 组件为空
                // 新创建一个 ConcurrentMapCache 组件
                cache = this.createConcurrentMapCache(name);
                // 将其放入 ConcurrentMap 对象中
                this.cacheMap.put(name, cache);
            }
        }
    }
    return cache;
}

可以看到该方法的作用实际上就是用来获取一个 ConcurrentMapCache 类型的 Cache 组件,而我们通过 Cache 组件获取数据时是通过 get 方法,最终是通过该类的 get 方法调用 lookup(key) 方法:

// org.springframework.cache.concurrent.ConcurrentMapCache#lookup
@Override
protected Object lookup(Object key) {
    // this.store : private final ConcurrentMap<Object, Object> store;
    return this.store.get(key);
}

而这个 key 默认是使用 org.springframework.cache.interceptor.SimpleKeyGenerator#generateKey 生成的:

// org.springframework.cache.interceptor.SimpleKeyGenerator
package org.springframework.cache.interceptor;

import java.lang.reflect.Method;

public class SimpleKeyGenerator implements KeyGenerator {

    @Override
    public Object generate(Object target, Method method, Object... params) {
        return generateKey(params);
    }

    public static Object generateKey(Object... params) {
        if (params.length == 0) {
            //SimpleKey.EMPTY : public static final SimpleKey EMPTY = new SimpleKey();
            return SimpleKey.EMPTY;
        }
        if (params.length == 1) {
            Object param = params[0];
            if (param != null && !param.getClass().isArray()) {
                return param;
            }
        }
        return new SimpleKey(params);
    }
}

可以看到它的生成规则如下:

  • 当目标方法的参数个数为空时,使用 new SimpleKey() 实例化一个 SimpleKey 作为 key 值。
  • 当目标方法的参数个数为 1 且该参数不为空也不是数组类型时,直接使用该参数值作为 key 值。
  • 当目标方法的参数不止一个时,使用 new SimpleKey(params) 实例化一个 SimpleKey 作为 key 值。
    ConcurrentMapCache 组件也是将实际缓存数据存放在 ConcurrentMap 对象中。

小结

ConcurrentMapCache 类实现了 Cache 接口,Cache 接口中定义了 get 方法用来获取数据、put 方法用来存放数据、evict 方法用来删除指定数据、clear 方法用来清空所有数据。

上述运行流程如下:

  1. 目标方法运行之前会使用 CacheManager 先根据 cacheNames 获取 Cache 组件,如果没有,则会创建一个新的 Cache 组件返回。
  2. 通过返回的 Cache 组件使用生成的 key 获取缓存内容。
  3. 如果获取到的缓存内容为空,就直接调用目标方法,然后将目标方法执行后的返回值通过 Cache 组件的 put 方法放入缓存(ConcurrentMap 对象)中;如果获取到的缓存内容不为空,就直接返回获取到的缓存内容,不会执行目标方法。

默认情况下:

  • 生效的自动配置类为 SimpleCacheConfiguration
  • CacheManager 的实现为 ConcurrentMapCacheManager
  • key 是使用 KeyGenerator 生成的,实现为 SimpleKeyGenerator
0

评论区