SpringBoot-Cache(缓存)

JSR107

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设置。

在这里插入图片描述

Spring从3.1开始定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager接口来统一不同的缓存技术;
并支持使用JCache(JSR-107)注解简化我们开发;
• Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
• Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,
ConcurrentMapCache等;
• 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否
已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法
并缓存结果后返回给用户。下次调用直接从缓存中获取。

• 使用Spring缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据

在这里插入图片描述

缓存的使用

搭建环境

1. 导入依赖

 <dependency>
 	<!--缓存-->
    <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-cache</artifactId>
   </dependency>
   <!--web-->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <!--mybatis-->
   <dependency>
       <groupId>org.mybatis.spring.boot</groupId>
       <artifactId>mybatis-spring-boot-starter</artifactId>
       <version>2.1.2</version>
   </dependency>
	<!--mysql-->
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <scope>runtime</scope>
   </dependency>

2. 创建数据库

database : spring_cache
table_1 : employee
table_2 : department

3. 创建jave bean 封装数据库数据

4. 整合mybatis

1. 配置数据源和mybatis基本配置

application.yml配置文件

#数据源
spring:
  datasource:
    username: root
    password: 001129
    url: jdbc:mysql://localhost:3306/spring_cache?serverTimezone=UTC&characterEncoding=utf-8
    driver-class-name: com.mysql.jdbc.Driver

#mybatis 配置
mybatis:
  type-aliases-package: com.it.bean
  configuration:
    map-underscore-to-camel-case: true

debug: true

2. 编写mapper接口

EmployeeMapper.java

@Mapper
@Repository
public interface EmployeeMapper {

  @Select("SELECT * FROM employee WHERE `id` = #{id}")
   public Employee getEmployeeById(@Param("id") Integer id);

   @Update("UPDATE employee SET `lastName` = #{lastName},`email`=#{email},`gender`=#{gender},`d_id`=#{dId} WHERE `id`=#{id}")
   public void updateEmployee(Employee employee);

   @Delete("DELETE FROM employee WHERE `id`=#{id}")
   public void deleteEmployee(@Param("id") Integer id);

   @Insert("INSERT INTO employee (`lastName`,`email`,`gender`,`d_id`) VALUES(#{lastName},#{email},#{gender},#{dId})")
   public void insertEmployee(Employee employee);
}

DepartmentMapper.java 略

3. 编写Service层

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    public Employee getEmployeeById(Integer id){
        System.out.println("查询" + id + "号员工");
        Employee employee = employeeMapper.getEmployeeById(id);
        return employee;
    }
}

4. 编写Controller层测试

@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @GetMapping("getEmployee/{id}")
    public Employee getEmployee(@PathVariable("id") Integer id){
        return employeeService.getEmployeeById(id);
    }
}

5. 开启缓存

在主程序上标注注解 @EnableCaching 开启缓存
@SpringBootApplication
@EnableCaching
public class Springboot09CacheApplication {

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

}

6. 使用缓存注解@Cacheable

在方法上标注缓存注解
service层下的getEmployeeById方法下标注 @Cacheable使用缓存
@Cacheable 将方法的返回结果进行缓存 ,
下次需要可以直接从缓存中获取,不用调用方法
    @Cacheable(cacheNames = "emp" )
    public Employee getEmployeeById(Integer id){
        System.out.println("查询" + id + "号员工");
        Employee employee = employeeMapper.getEmployeeById(id);
        return employee;
    }

编写Controller 来测试

    @GetMapping("/getEmployee/{id}")
    public Employee getEmployee(@PathVariable("id") Integer id){
        return employeeService.getEmployeeById(id);
    }

测试访问页面 查询一号员工第一次需要调用方法,第二次则不需要调用方法,直接从缓存中获取到数据

缓存的主要参数

在这里插入图片描述
在这里插入图片描述

/**
     * @Cacheable 将方法的返回结果进行缓存 ,下次需要可以直接从缓存中获取,不用调用方法
     *
     * CacheManager 管理多个Cache组件,对缓存的CRUD操作在Cache组件中,每个组件有自己的名字
     *
     * @Cacheable 中属性
     *      cacheName / value : 指定缓存组件的名字
     *      key   :  缓存数据使用的key 可以用它来指定,默认是方法的参数值
     *      keyGenerator : key生成器,可以自己指定  key 和  keyGenerator 二选一
     *      cacheManager : 指定缓存管理器
     *      cacheResolver : 指定缓存解析器
     *      condition : 指定符合条件情况下才缓存 condition = "#id>0"
     *      unless : 否定缓存 当unless指定的条件为true  这个方法的返回值就不会被缓存
     *              unless = "#result == null"
     *      sync : 缓存是否使用异步模式

缓存的工作原理

缓存的工作原理
     *  1. 自动配置类 CacheAutoConfiguration
     *  2. 缓存配置类
     *      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.SimpleCacheConfiguration
     *      org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
     *  3. 默认生效的缓存配置类  SimpleCacheConfiguration
     *  4. 给容器中注册  CacheManager : ConcurrentMapCacheManager
     *  5. 获取和创建 ConcurrentMapCache类型的缓存组件,将数据保存在ConcurrentMap中

在这里插入图片描述

缓存的运行流程

* 运行流程
     * @Cacheable:
     *  1. 方法执行前,先去查询Cache(缓存组件),按照cacheNames 指定名字查找
     *     CacheManager先获取相应的缓存,第一次获取如果没有cache组件会自动创建
     *  2. 去cacheMap中查找缓存内容,使用一个key ,默认是方法的参数
     *          key 是按照某种策略生成,默认使用keyGenerator,默认使用simpleKeyGenerator生成
     *  3. 没有查到缓存就调用目标方法
     *  4. 将目标方法返回的结果,放进缓存中
     *
     * @cacheable标注的方法执行前先检查缓存中有无这个数据,默认按照参数值作为key去查询缓存
     * 如果没有运行方法并将返回值存入缓存,以后调用就直接可以使用缓存中的数据

7. 使用缓存注解@CachePut

@CachePut : 即调用方法,又更新数据,同步更新缓存
 修改了数据库中的信息,同时更新缓存
    //@CachePut : 即调用方法,又更新数据
    // 修改了数据库中的信息,同时更新缓存
    @CachePut(value = "emp",key = "#result.id")
    public Employee updateEmployee(Employee employee){
        employeeMapper.updateEmployee(employee);
        System.out.println("更新了一条数据");
        return employee;
    }
}

编写Controller 来测试

    @GetMapping("/updateEmployee")
    public Employee updateEmployee(Employee employee){
        Employee employee1 = employeeService.updateEmployee(employee);
        return employee1;
    }
运行:
1. 先调用目标方法
2. 将结果缓存起来  
(@Cacheable是方法执行前去缓存中查询,没有就在方法执行完存入
有就直接取
 
@CachePut是方法执行后将结果存入缓存
不查询
)

测试步骤:
 1. 查询1号员工
 2. 更新1号员工
 3. 再次查询1号员工
 
 上述步骤测试完发现,(Key值不统一的情况下)会发现更新完
 1号员工 再次查询1号员工  结果还是第一次查询的1号员工信息,
 我们需要的是员工信息更改后,查询出来的是更改后的信息
 
 因为 缓存是 key  value 形式
 
这时候我们就需要将查询员工的缓存key值 和 更新员工的缓存key值 保持统一
因为如果@CachePut不指定key值 默认的是 方法参数作为key 
我们查询员工的key 是参数 id  而更新员工则是参数 employee对象
这样就形成了两个key 
key 为  id  的缓存中保存的是 第一次查询后的员工信息值
key 为 employee对象的缓存中保存的是更新后的员工信息
我们调用查询员工 就会到缓存key为id中将数据拿出来

这时我们将两个方法设置为同一key  
这样我们更新后的数据也会在这个key中更新
再次调用查询就会从缓存中拿出我们更新后的值

8. 使用缓存注解 @CacheEvict

@CacheEvict :缓存清除

allEntries = true   删除缓存中所有数据
beforeInvocation = false  缓存清除是否在方法执行前执行
          默认 false  代表方法执行后执行,如果出错,缓存不会被清除
          设置为 true 代表方法执行前执行,无论方法是否异常,都会清除缓存
 @CacheEvict(value = "emp",key = "#id")
    public void deleteEmployee(Integer id){
        System.out.println("删除了一名员工");
        employeeMapper.deleteEmployee(id);
    }

编写Controller 来测试

    @GetMapping("/deleteEmployee/{id}")
    public String deleteEmployee(@PathVariable("id") Integer id){
        employeeService.deleteEmployee(id);
        return "删除了" + id + "员工 ";
    }

测试发现,我们查询1号员工,第二次查询时不需要走数据库,直接从缓存中获取,
但是我们调用了@CacheEvict修饰的方法  执行后,再次查询我们发现需要走数据库
也就证明,@CacheEvict将我们的缓存删除了 

 key = xxx   : 删除 指定key的缓存
 allEntries = true  : 删除缓存中所有数据
beforeInvocation = false  缓存清除是否在方法执行前执行
          默认 false  代表方法执行后执行,如果出错,缓存不会被清除
          设置为 true 代表方法执行前执行,无论方法是否异常,都会清除缓存

9. 使用缓存注解@Caching

@Caching  定义复杂缓存规则
//@Caching  定义复杂缓存规则
    @Caching(
            cacheable = {
                    @Cacheable(value="emp",key = "#lastName")
            },
            put = {
                    @CachePut(value = "emp",key = "#result.id")
            }

    )
    public Employee getEmployeeByLastName(String lastName){
        System.out.println("查询到了");
        return employeeMapper.getEmployeeByLastName(lastName);
    }

编写Controller 来测试

我们测试发现, 我们根据lastName查询员工信息,由于我们定义了
复杂缓存规则,@CachePut  会将查询后的数据添加到key为id的缓存中
所以下次我们根据id来查询就不需要走数据库了
就会从缓存中拿

可以在类上添加 @CacheConfig(cacheNames = “emp”)
这样我们就统一了缓存组件的名字
下面的缓存就不需要写 cacheName 或 value了


版权声明:本文为weixin_44900198原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。