1.1_springboot2.x与缓存原理介绍&使用缓存
一、springboot与缓存介绍&使用缓存
1、JSR107
JAVA Cahing定义了5个核心接口,分别是CachingProvider、CacheManager、Cache、Entry、Expiry。
CachingProvider:定义创建、配置、获取、管理、和控制多个CacheManager.一个应用在运行期间可以访问多个CacheManager;
CacheManager:定义了创建、配置、获取、管理、和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CachingProvider所拥有;
Cache:是一个类似Map的数据结构并临时存储以key为索引的值,一个Cache仅被一个CacheManager所拥有;
Entry:是一个存储在Cache中的key-value对;
Expiry:每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态,一旦过期,条目将不可访问,更新和删除。缓存有效期可以通过ExpriyPolicy设置;
由于整合系统是使用JSR107难度较大,因此spring提供了自己的缓存抽象、
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、从缓存中读取之前缓存存储的数据
3、几个重要概念&缓存注解
1、几个重要的注解
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
---|---|
CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存。 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
2、@Cacheable/@CachePut/@CacheEvict主要的参数
3、Cache SpEL available metadata
4、缓存的使用
一、搭建基本环境,
1**、导入基本环境,创建出department和employee**
2、创建javabean
3、整合mybatis操作数据库
1)、配置数据源对象
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_cache?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.initialization-mode=always
logging.level.com.springboot.springboot.mapper
debug=true
2)、注解版的mybatis
@Mapper
public interface EmployeeMapper {
@Select("select * from employee where id=#{id}")
public Employee getEmpById(Integer id);
@Update("update employee set lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} where id=#{id}")
public void updateEmp(Employee employee);
@Insert("insert into employee(lastName,email,gender,d_id) values(#{lastName},#{email},#{gender},#{id}")
public void insertEmp(Employee employee);
@Delete("delete from employee where id=#{id}")
public void deleteEmp(Integer id);
@Select("select * from employee where lastName=#{lastName}")
public Employee getEmpByLastName(String lastName);
}
1、@MapperScan需要扫描的mapper接口
可以新建一个mybatis配置类
@EnableCaching //开启基于缓存的注解
@MapperScan("com.springboot.springboot.mapper")
@org.springframework.context.annotation.Configuration
public class MybatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
这里开启驼峰命名也可以在properties中配置
二、快速体验缓存
步骤:
1、开启基于注解的缓存*
在mybatis配置类中添加:
@EnableCaching //开启基于缓存的注解
2、标注缓存注解*
在EmployeeService类中标注缓存注解进行测试,将方法的运行结果进行缓存,以后获取相同的数据,直接从缓存找,不用调用方法
1)@EnableCaching 开启基于注解的缓存*
2)@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存**
CacheManager管理多个Cache组件,对缓存的真正的CRUD操作在Cache组件中,每一个缓存组件有自己的名字。
属性介绍:
cacheNames/value: 指定缓存组件的名字,将方法的返回结果放在缓存中,是数组的方式,可以指定多个缓存
key:缓存数据使用的key,可以用它进行指定默认是使用方法参数的值1,值为方法的返回值*
编写可使用spEL: #id参数id的值 #a0 #p0 #root.args[0]*
自定义key:*
1、实例:key显示为getEmp[2]*
@Cacheable(cacheNames = {"emp"},key = "#root.methodName+'['+#id+']'
2、自定义keyGenerator生成器,在配置类中配置
@Configuration
public class MyCacheConfig {
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName()+"["+ Arrays.asList(params)+"]";
}
};
}
}
- keyGenerator:key的生成器,可以指定key的生成器组件id
注意:keyGenerator/key二选一*
cacheManager:指定缓存管理器,或者cacheResolver指定获取解析器*
condition:指定符合条件的情况向下才进行缓存*
unless:否定缓存,当unless指定的条件为true时,方法的返回值才不会缓存,可以获取结果进行判断* unless=’#resultnull’* unless=’#a02’ 如果第一个参数值是2,结果不缓存*
sync:是否使用异步模式
@CacheEvict 清空缓存
@CachePut 保证方法被调用,又希望结果被缓存。
默认使用的是:ConcurrentMapCacheManager===ConcurrentMapCache(private final ConcurrentMap<Object, Object> store;)
实际开发:使用缓存中间件:redis|memcached|ehcache
EmployeeService
// @Cacheable(cacheNames = {"emp"},key = "#root.methodName+'['+#id+']'")
@Cacheable(cacheNames = {"emp"}/*keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2"*/)
public Employee getEmp(Integer id){
System.out.println("查询员工"+id);
Employee emp =employeeMapper.getEmpById(id);
return emp;
}
Controller:
@RestController
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@GetMapping("/emp/{id}")
public Employee getEmp(@PathVariable("id") Integer id){
Employee emp= employeeService.getEmp(id);
return emp;
}
3、原理介绍
这里以@Cacheable 为例子介绍原理:
1、自动配置类 CacheAutoConfiguration*
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
/**
* {@link ImportSelector} to add {@link CacheType} configuration classes.
给容器中导入一些缓存配置类
*/
static class CacheConfigurationImportSelector implements ImportSelector {
@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;
}
}
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**
@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;
}
@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);
}
}
ConcurrentMapCacheManager
ConcurrentMapCacheManager
@Override
@Nullable
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
// 调用本类的
cache = createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
}
return cache;
}
5、可以获取和创建createConcurrentMapCache类型的缓存,
这里会创建一个Cache组件
protected Cache createConcurrentMapCache(String name) {
SerializationDelegate actualSerialization = (isStoreByValue() ? this.serialization : null);
return new ConcurrentMapCache(name, new ConcurrentHashMap<>(256),
isAllowNullValues(), actualSerialization);
}
ConcurrentMapCache:
作用将数据保存在ConcurrentMap中
private final ConcurrentMap<Object, Object> store;
4、运行流程
运行流程(以@Cacheable为列子)
1、方法运行之前,先查询Cache(缓存组件),按照cacheNames指定的名字进行获取 (CacheManager先获取相应的缓存)第一次获取缓存如果没有Cache组件会自动创建出来**
@Override
@Nullable
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
}
return cache;
}
2、去Cache去查找缓存的内容,使用一个key,key默认就是方法的参数,
ConcurrentMapCache
@Override
@Nullable
protected Object lookup(Object key) {
return this.store.get(key);
}
key是按照某种策略生成的,默认使用SimpleKeyGenerator生成key ,默认使用SimpleKeyGenerator生成策略。
SimpleKeyGenerator生成策略:
如果没有参数:key=new SimpleKey()
如果有一个参数:key=参数的值
如果有多个参数:key=new SimpleKey(params)
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);
}
CacheAspectSupport
Object key = generateKey(context, result);
@Nullable
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
for (CacheOperationContext context : contexts) {
if (isConditionPassing(context, result)) {
Object key = generateKey(context, result);
Cache.ValueWrapper cached = findInCaches(context, key);
if (cached != null) {
return cached;
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
}
}
}
}
return null;
}
调用本类此方法
private Object generateKey(CacheOperationContext context, @Nullable Object result){
Object key = context.generateKey(result);
if (key == null) {
throw new IllegalArgumentException("Null key returned for cache operation (maybe you are " +
"using named params on classes without debug info?) " + context.metadata.operation);
}
if (logger.isTraceEnabled()) {
logger.trace("Computed cache key '" + key + "' for operation " + context.metadata.operation);
}
return key;
}
再调用本类:generateKey()方法,计算给定缓存操作的密钥(key)
/**
* Compute the key for the given caching operation.
*/
@Nullable
protected Object generateKey(@Nullable Object result) {
if (StringUtils.hasText(this.metadata.operation.getKey())) {
EvaluationContext evaluationContext = createEvaluationContext(result);
return evaluator.key(this.metadata.operation.getKey(), this.metadata.methodKey, evaluationContext);
}
return this.metadata.keyGenerator.generate(this.target, this.metadata.method, this.args);
}
3、没有查询到缓存就调用目标方法
4、将目标方法返回的结果,放进缓存中
总结:@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,如果没有就运行方法,并且将结果放到缓存中,以后再来调用就直接可以从缓存中拿到数据的值** *
核心: 1、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件*
2、key使用keyGenerator生成的,默认是SimpleKeyGenerator
5、其它注解说明
@CachePut 即调用方法,又更新缓存数据 达到同步更新的目的
修改了数据库的某个数据,同时更新缓存;
运行时机: 1、先调用目标方法 2、将目标方法的结果缓存起来
测试步骤:
1、先查询1号员工,查到结果会放到缓存中
key :1 , value:lastName
2、以后查询还是之前的结果
3、更新1号员工的信息(lastName:zhangsan,gender=0) ,将方法的返回值也放进了缓存中
key:employee
value:返回的employee对象
4、查询1号员工信息
预测:应该是更新后的信息,但是数据还是没更新前的数据(缓存中的数据),这时数据库已经更新了*
key应该是更新的员工信息:
key="#employee.id",使用传入员工的参数id*
key="#result.id" 使用返回后的id*
注意:@Cacheable的key是不能用result* 为什么没有更新?(1号员工没有在缓存中进行更新)
EmployeeService
@CachePut(value = "emp",key = "#result.id")
public Employee updateEmp( Employee employee){
System.out.println("udapte..."+employee);
employeeMapper.updateEmp(employee);
return employee;
}
controller
@GetMapping("/emp")
public Employee updateEmp(Employee employee){
Employee emp = employeeService.updateEmp(employee);
return emp;
}
测试自行测试
@CacheEvict 清空缓存
**key:**指定要删除的数据
allEntries:清空这个value中所有的缓存数据,
beforeInvocation:缓存的清除是否在方法之前执行,默认false ,false:代表方法之后执行,如果出现异常缓存就不会清除
beforeInvocation =true: 代表清除缓存是在方法执行之前清空,无论方法是否出现异常方法都会清空
@CacheEvict(value = "emp",allEntries = true,beforeInvocation =true)
public void deleteEmp(Integer id){
System.out.println("deleteEmp"+id);
int a=10/0;
//employeeMapper.deleteEmp(id);
}
@Caching:定义多个缓存规则
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
定义复杂的缓存规则
@Caching(
cacheable={
@Cacheable(value = "emp",key="#lastName")
},
put={
@CachePut(value = "emp",key = "#result.id"),
@CachePut(value = "emp",key="#result.email")
}
)
public Employee getEmpByLastName(String lastName){
return employeeMapper.getEmpByLastName(lastName);
}
@CacheConfg:抽取缓存的公共配置,之后的其他缓存属性可以不用写
@Service
@CacheConfig(cacheNames = "emp")
public class EmployeeService {}
1.1_springboot2.x与缓存原理介绍&使用缓存的更多相关文章
- 1.2_springboot2.x中redis缓存&原理介绍
1.整合redis作为缓存 说明这里springboot版本2.19 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构 ...
- 写给后端程序员的HTTP缓存原理介绍
There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Ka ...
- 写给后端程序员的HTTP缓存原理介绍--怎样决定一个资源的Cache-Control策略呢
通过Internet获取资源既缓慢,成本又高.为此,Http协议里包含了控制缓存的部分,以使Http客户端可以缓存和重用以前获 取的资源,从而优化性能,提升体验.虽然Http中关于缓存控制的部分,随着 ...
- Solr缓存原理分析及配置优化
一.缓存原理 缓存,带来急速性能体验! Solr提供了一系列的内置缓存来优化查询性能.Solr的缓存原理主要涉及以下4个方面: 1.缓存大小及缓存置换法 从缓存大小的角度来看,不能将缓存设置的太大,否 ...
- Springboot中的缓存Cache和CacheManager原理介绍
背景理解 什么是缓存,为什么要用缓存 程序运行中,在内存保持一定时间不变的数据就是缓存.简单到写一个Map,里面放着一些key,value数据,就已经是个缓存了 所以缓存并不是什么高大上的技术,只是个 ...
- CYQ.Data V5 分布式缓存Redis应用开发及实现算法原理介绍
前言: 自从CYQ.Data框架出了数据库读写分离.分布式缓存MemCache.自动缓存等大功能之后,就进入了频繁的细节打磨优化阶段. 从以下的更新列表就可以看出来了,3个月更新了100条次功能: 3 ...
- ahjesus 前端缓存原理 转载
LAMP缓存图 从图中我们可以看到网站缓存主要分为五部分 服务器缓存:主要是基于web反向代理的静态服务器nginx和squid,还有apache2的mod_proxy和mod_cache模 浏览器缓 ...
- Hibernate缓存原理与策略
Hibernate缓存原理: 对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等 ...
- 65.Android 三大图片缓存原理、特性对比 (转)
这是 Trinea 在 MDCC 上分享的内容(略微改动),也是源码解析第一期发布时介绍的源码解析后续会慢慢做的事. 从总体设计和原理上对几个图片缓存进行对比,没用到他们的朋友也可以了解他们在某些特性 ...
随机推荐
- Python with语句的概率,不多说了直接上代码!
python中的with语句用于访问资源.它确保执行指定的__exit__(“清理”)操作,而不管释放被访问资源的处理过程中的错误或异常,例如读取和写入文件后自动关闭.线程中锁的自动获取和释放等. p ...
- springMVC快速入门 共分为五步
springMVC快速入门 共分为5步分别为: 1 导入依赖 2 spring-mvc.xml 配置 3 web.xml配置 4 自定义一个核心控制类 5 页面配置 详细步骤以及代码 ...
- 《DNS攻击防范科普系列3》 -如何保障 DNS 操作安全
引言 前两讲我们介绍了 DNS 相关的攻击类型,以及针对 DDoS 攻击的防范措施.这些都是更底层的知识,有同学就来问能否讲讲和我们的日常操作相关的知识点,今天我们就来说说和我们日常 DNS 操作相关 ...
- NX二次开发-UFUN获取当前导出CGM选项设置UF_CGM_ask_session_export_options
文章转载自唐康林NX二次开发论坛,原文出处: http://www.nxopen.cn/thread-126-1-1.html 刚才有同学问到这个问题,如果是用NXOpen来做,直接录制一下就可以了: ...
- NX二次开发-C++的vector排序去重用法
#include <algorithm> //vector排序去重 sort( BoxNum.begin(), BoxNum.end()); BoxNum.erase(unique(Box ...
- [转]C# 将类的内容写成JSON格式的字符串
将类的内容写入到JSON格式的字符串中 本例中建立了Person类,赋值后将类中内容写入到字符串中 运行本代码需要添加引用动态库Newtonsoft.Json 程序代码: using System; ...
- 使用反射机制,获取 ArrayList 的容量大小
本文所有说明及代码示例都是基于JDK 1.8 ArrayList 提供size()方法获取当前集合的元素数量,但无法知道当前集合的容量,翻看 ArrayList 的源代码,可以看到字段 elemen ...
- Opencv稍微高级点的鼠标事件-OpenCV步步精深
今天我们要来点稍微高级的东西.在我们按下鼠标时可以画矩形,而我们按下键盘m键时,切换到画圆的模式,再按下m键,回到画矩形模式. 一起来写下代码,首先当然还是调用库 import cv2 import ...
- 牛客CSP-S提高组赛前集训营1
牛客CSP-S提高组赛前集训营1 比赛链接 官方题解 before:T1观察+结论题,T2树形Dp,可以换根或up&down,T3正解妙,转化为图上问题.题目质量不错,但数据太水了~. A-仓 ...
- K-Anonymous Sequence
K-Anonymous Sequence 给出一个递增的长度为n的序列\(\{a_i\}\),现在你可以进行一次操作,选择若干个数,分别减少任意一个正整数,定义权值为这些正整数之和,询问操作使得新序列 ...