Cache缓存快速使用:
启动类:
package com.atguigu.cache;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
/**
* 一、搭建基本环境
* 1.导入数据库文件 创建出department 和 employee表
* 2.创建javaBean封装数据
* 3.整合MyBatis操作数据库
* 1.配置数据源信息
* 2.使用注解版的MyBatis
* 1).@MapperScan指定需要扫描的mapper接口所在的包
* 二、快速体验缓存
* 步骤:
* 1.开启基于注解的缓存@EnableCaching
* 2.标注缓存注解即可
* @Cacheable
* @CacheEvict
* @EnableCaching
*/
@EnableCaching
@MapperScan("com.atguigu.cache.mapper")
@SpringBootApplication
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
Controller:
package com.atguigu.cache.controller;
import com.atguigu.cache.bean.Employee;
import com.atguigu.cache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 犀角
* @date 2020/12/20 20:19
* @description
*/
@RestController
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@RequestMapping("/emp/{id}")
public Employee getEmployee(@PathVariable("id") Integer id){
Employee emp = employeeService.getEmp(id);
return emp;
}
}
Mapper:
package com.atguigu.cache.mapper;
import com.atguigu.cache.bean.Employee;
import org.apache.ibatis.annotations.*;
/**
* @author 犀角
* @date 2020/12/20 19:36
* @description
*/
@Mapper
public interface EmployeeMapper {
@Select("SELECT * FROM employee WHERE id = #{id}")
public Employee getEmpById(Integer id);
@Update("UPDAT employee SET lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} WHERE id=#{id}")
public void updateEmp(Employee employee);
@Delete("DELETE FROM employee WHERE id=#{id}")
public void deleteEmpById(Integer id);
@Insert("INSERT INTO employee(lastName,email,gender,d_id) VALUES (#{lastName},#{email},#{gender},#{dId})")
public void insertEmployee(Employee employee);
}
service:
package com.atguigu.cache.service;
import com.atguigu.cache.bean.Employee;
import org.springframework.cache.annotation.Cacheable;
/**
* @author 犀角
* @date 2020/12/20 20:15
* @description
*/
public interface EmployeeService {
public Employee getEmp(Integer id);
}
serviceImpl:
package com.atguigu.cache.service.Impl;
import com.atguigu.cache.bean.Employee;
import com.atguigu.cache.mapper.EmployeeMapper;
import com.atguigu.cache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* @author 犀角
* @date 2020/12/20 20:16
* @description
*/
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
/**
* 将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法
* CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字;
*
* 几个属性
* cacheNames/value:指定缓存的名称;
* key:缓存数据时的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值
* 编写SpEL; #id;参数id的值 #a0 #p0 #root.args[0]
* keyGenerator: key的生成器;可以自己指定key的生成器的组件id
* key/keyGenerator:二选一使用
* cacheManager:指定缓存管理器; 或者指定cacheResolver 指定获取解析器
* candition:指定符合条件的情况下才缓存;
* ,condition = "#id>0"
* unless:否定缓存;当unless条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断 ,unless = "#result == null"
* sync:是否使用异步
* @param id
* @return
*/
@Override
@Cacheable(cacheNames = {"emp"})
public Employee getEmp(Integer id) {
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
}
Bean:
package com.atguigu.cache.bean;
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; //性别 1男 0女
private Integer dId;
public Employee() {
super();
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer dId) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.dId = dId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getdId() {
return dId;
}
public void setdId(Integer dId) {
this.dId = dId;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", dId="
+ dId + "]";
}
}
一、Cache缓存的作用
1.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设置。
2.Spring缓存抽象
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、从缓存中读取之前缓存存储的数据
二、几个重要概念&缓存注解
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、 ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存。 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
value | 缓存的名称,在 spring 配置文件中定义,必须指定 至少一个
| 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}
|
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达 式编写,如果不指定,则缺省按照方法的所有参数 进行组合
| 例如: @Cacheable(value=”testcache”,key=”#userName” |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存,在 调用方法之前之后都能判断
| 例如: @Cacheable(value=”testcache”,condition=”#userNam e.length()>2”)
|
allEntries (@CacheEvict )
| 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
| 例如:@CachEvict(value=”testcache”,allEntries=true)
|
beforeInvocation (@CacheEvict)
| 是否在方法执行前就清空,缺省为 false,如果指定 为 true,则在方法还没有执行的时候就清空缓存, 缺省情况下,如果方法执行抛出异常,则不会清空 缓存
| 例如:@CachEvict(value=”testcache”, beforeInvocation=true) |
unless (@CachePut) (@Cacheable) | 用于否决缓存的,不像condition,该表达式只在方 法执行之后判断,此时可以拿到返回值result进行判 断。条件为true不会缓存,fasle才缓存 | 例如:@Cacheable(value=”testcache”,unless=”#result == null”) |
名字 | 位置 | 描述 | 实例 |
methodName | root object | 当前被调用的方法名 | #root.methodName |
method | root object | 当前被调用的方法 | #root.method.name |
target | root object | 当前被调用的目标对象 | #root.target |
targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
args | root object | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache。
| #root.caches[0].name
|
argument name
| evaluation context | 方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的 形式,0代表参数的索引。
| #a0、#p0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行之后的判断有效,如 ‘unless’,’cache put’的表达式 ’cache evict’的表达式 beforeInvocation=false)
| #result |
三、缓存实现原理
将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法,CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字。
详细原理:
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.GuavaCacheConfiguration
* org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
* org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
默认SimpleCacheConfiguration生效
3)、给容器中注册了一个CacheManager:ConcurrentMapCacheManager;
4)、获取和创建ConcurrentMapCache类型的缓存组件,他的作用将数据保存在ConcurrentMap中;
5)、 运行流程:
方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取,(CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建;
去Cache中查找缓存的内容,使用一个key,默认就是方法的参数,key是按照某种策略生成的,默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key,SimpleKeyGenerator生成key的默认策略:如果没有参数:key=new SimpleKey();如果有一个参数:key=参数的值;如果有多个参数:key=new SimpleKey(params);
没有查到缓存就调用目标方法,将目标方法返回的结果,放进缓存中,@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,如果没有就运行方法并将结果放入缓存,以后再来调用就可以直接使用缓存中的数据。