一、前言

Spring Cache 对 Cahce 进行了抽象,提供了 @Cacheable、@CachePut、@CacheEvict 等注解。Spring Boot 应用基于 Spring Cache,既提供了基于内存实现的缓存管理器,可以用于单体应用系统,也集成了 EhCache、Redis 等缓存服务器,可以用于大型系统或者分布式系统。

二、关于 Cache

应用系统需要通过 Cache 来缓存不经常改变的数据以提高系统性能和增加系统吞吐量,避免直接访问数据库等低速的存储系统。缓存的数据通常存放在访问速度更快的内存中或者是低延迟存取的存储器、服务器上。应用系统缓存通常有以下作用:

缓存 Web 系统的输出,如伪静态页面;

缓存系统中不经常改变的业务数据,如用户权限、字典数据、配置信息等。

三、使用 Spring Cache

Spring Boot 自带了基于 ConcurrentHashMap 的 Simple 缓存管理器,也集成了 EhCache、Redis 等缓存管理器。Spring Boot 应用通过注解的方式使用统一的缓存,只需要在方法上使用缓存注解即可,其缓存的具体实现依赖于选择的目标缓存管理器。本节先介绍 Spring Boot 自带的 Simple 缓存,然后再介绍 EhCahce 和 Redis 缓存。需要注意的是,Simple 只适合单体应用或者开发环境使用,再或者是一个小微系统,通常应用为分布式应用时,则需要集成 EhCache、Redis 等分布式缓存管理器。

配置过程示例

新建SpringBoot微服务并整合Mybatis,我的项目中使用整合的事Mybatis通用的Mapper以及自动Mybatis-generate插件。

pom.xml导入maven依赖

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

数据库建表:

JAVA springboot map 缓存 springboot内置缓存_缓存


启动类开启缓存配置

JAVA springboot map 缓存 springboot内置缓存_User_02


编写缓存业务

@Service
public class CacheService {


    @Autowired
    UserMapper userMapper;


    /**
     * 将方法的运行结果进行缓存,再次运行该方法时从缓存中返回结果
     * CacheManager 管理多个 Cache 组件,Cache 组件进行缓存的 CRUD,每一个缓存组件都有唯一一个名字。
     * 属性:
     *      cacheNames/value:指定缓存组件的名字
     *      key:缓存数据键值对的 key,默认是方法的参数的值
     *      keyGenerator:key 的生成器,可以自己指定 key 的生成器的组件 id 与 key 二选一
     *      cacheManager:指定缓存管理器 cacheResolver:缓存解析器,二者二选一
     *      condition/unless(否定条件):符合指定条件的情况下才缓存
     *
     * @param id
     * @return
     */
    @Cacheable(cacheNames = {"user"},condition = "#id > 0")
    public User getUser(Integer id){
//        Example example = new Example(User.class);
//        example.createCriteria().andEqualTo("id",id);
//        List<User> users = userMapper.selectByExample(example);
        return userMapper.selectByPrimaryKey(id);
    }



    /**
     * 更新缓存,既调用方法 (更新数据库),又更新缓存
     * 测试步骤:1、查询 1 号员工,将其纳入缓存
     *          2、修改 1 号员工
     *          3、再次查询 1 号员工,若结果是从缓存中查询数据,且数据为更新后的缓存则测试通过
     * @param user
     * @return
     */
    @CachePut(cacheNames = {"user"}, key = "#result.id")
    public User updateUser(User user) {
        try {
            userMapper.insertSelective(user);
        } catch (DuplicateKeyException e) {
            e.printStackTrace();
            userMapper.updateByPrimaryKey(user);
        }
        return user;
    }



    /**
     * 清空缓存
     *      beforeInvocation:默认为 false 表示在方法调用之后清空缓存,
     *                        若为 true,则表示在方法调用之前清空缓存
     * @param id
     */
    @CacheEvict(cacheNames = {"user"}, beforeInvocation = true/*, key = "#id"*/)
    public void deleteUser(Integer id) {
//        userMapper.deleteByPrimaryKey(id);
    }
}

通过http请求调用服务的controller层调用缓存业务:

@RequestMapping(value = "/test-cache",method = RequestMethod.GET)
    @ApiOperation(value = "测试缓存",produces = "application/json",response = ResMessage.class)
    public ResMessage cache(@RequestParam("id")int id){
        User user = cacheService.getUser(id);
        ResMessage retMsg = ResMessage.newInstance().setContent(user);
        return retMsg;
    }


    @RequestMapping(value = "/update-cache",method = RequestMethod.POST)
    @ApiOperation(value = "测试缓存修改",produces = "application/json",response = ResMessage.class)
    public ResMessage cache(@RequestBody ReqMessage reqMessage){
        User userUpdate = cacheService.updateUser(reqMessage.getContentObject(User.class));
        ResMessage retMsg = ResMessage.newInstance().setContent(userUpdate);
        return retMsg;
    }


    @RequestMapping(value = "/del-cache",method = RequestMethod.GET)
    @ApiOperation(value = "测试缓存删除",produces = "application/json",response = ResMessage.class)
    public ResMessage delCache(@RequestParam("id")int id){
        cacheService.delUser(id);
        ResMessage retMsg = ResMessage.newInstance();
        return retMsg;
    }

请求执行getUser信息时,第一次会执行数据库查询,第二次请求时不会查询数据库而是通过缓存获取。

第一次:

JAVA springboot map 缓存 springboot内置缓存_User_03


第二次:

JAVA springboot map 缓存 springboot内置缓存_spring_04


请求执行updateUser/delUser修改或删除数据库信息的同时也会更新缓存的信息。

注意内容:

  • 当项目整合redis时,缓存的形式会转变为通过redis缓存,此时若不想将缓存数据存入redis中,可在项目配置中添加:
  • 内部方法直接调用缓存的方法无法生效,原因是@Cacheable是基于Spring AOP代理类,内部方法调用是不走代理的,@Cacheable是不起作用的。需要通过spring实例对象并且调用缓存方法才有效。

集成 EhCache使用

添加 EhCache 依赖

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

<!-- EhCache -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

添加 ehcache.xml 配置文件

在 resources 目录下创建 ehcache.xml 文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd">
    <cache name="user" 
           eternal="false"
           maxEntriesLocalHeap="0" 
           timeToIdleSeconds="50">
    </cache>
</ehcache>

在 application.yml中配置目标缓存管理器

spring:
  cache:
    #ehcache配置文件路径
    ehcache:
      config: classpath:ehcache.xml
    #指定缓存类型,可加可不加
    #type: ehcache

集成 Redis使用

添加 Redis 依赖

<!-- Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在 application.yml中配置目标缓存管理器和redis配置

spring:
  cache:
    type: ehcache
  redis:
    database: 6 #  数据库索引
    host: 127.0.0.1 #    服务器地址
    port: 6379  #    服务器连接端口
    #    password: 123456   #    密码
    jedis:
      pool: #    链接池
        max-active: 8 #    最大连接数(负值表示没有限制)
        max-wait: 1ms #      最大阻塞等待时间(负值表示没有限制)
        max-idle: 8 #      最大空闲链接
        min-idle: 0  #      最小空闲链接
    timeout: 5000ms #    链接超时时间(毫秒)

其他步骤与使用 Simple 和 Ehcache 时的步骤一致。