快速建设网站,江苏省住房和城乡建设厅网站首页,企业网站系统,公司注销网站备案目录 SpringCache详解
SpringCache概述
核心原理
接口抽象与多态
AOP动态代理
核心注解以及使用
公共属性
cacheNames
KeyGenerator#xff1a;key生成器
key
condition#xff1a;缓存的条件#xff0c;对入参进行判断
注解
xxl-job详解
SpringcacheRedis实现…目录 SpringCache详解
SpringCache概述
核心原理
接口抽象与多态
AOP动态代理
核心注解以及使用
公共属性
cacheNames
KeyGeneratorkey生成器
key
condition缓存的条件对入参进行判断
注解
xxl-job详解
SpringcacheRedis实现缓存
xxl-job定时刷新缓存 SpringCache详解
SpringCache概述
早期的Java开发中缓存技术需要借助第三方库Ehcache、Guava等导致了代码与具体缓存实现强耦合。此时缺乏统一缓存规范并且在企业中更换缓存组件成本较高。
Spring框架在3.1版本首次引入缓存抽象层定义了Cache和CacheManager接口通过注解如Cacheable实现声明式缓存。随后在Spring4.1开始全面支持JCache规范2012年提出的JSR-107规范草案通过JCacheManager集成第三方缓存实现自此开发者可以通过标准接口切换缓存方案实现了通过标准接口隔离业务代码与具体缓存的抽象解耦符合“开方-封闭原则”。
Spring Cache 是 Spring 框架提供的抽象化缓存解决方案通过注解和 AOP 技术简化了缓存逻辑的集成。它并不直接管理缓存存储而是作为统一接口支持多种缓存实现如 Ehcache、Redis、Caffeine 等使开发者能够通过声明式注解快速为方法添加缓存功能从而减少重复计算提升系统性能。
核心原理
接口抽象与多态
SpringCache定义了两大核心接口实现缓存标准化
Cache接口
定义缓存基本操作get、put、evict不同缓存技术如Redis、Ehcache通过实现该接口完成适配例如
RedisCache通过RedisTemplate操作Redis数据
ConcurrentMapCache使用本地内存Map存储数据
CacheManager接口
管理多个Cache实例的生命周期支持多级缓存混合使用。
EhCacheCacheManager解析ehcahe.xml配置RedisCacheManager配置TTL和序列化策略
AOP动态代理
SpringCache通过CacheInterceptor拦截器实现方法级缓存空值 说明
将 invocation.proceed() 封装为 CacheOperationInvoker 实例延迟执行原始方法。捕获所有 Throwable 并封装为 ThrowableWrapper避免缓存逻辑干扰异常类型。调用 execute传递方法调用器、目标对象、方法对象和参数进入缓存处理核心逻辑。
然后一直跟进execute方法最终找到处理缓存的主逻辑
Nullable
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {if (contexts.isSynchronized()) {CacheOperationContext context (CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next();if (!this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {return this.invokeOperation(invoker);}Object key this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);Cache cache (Cache)context.getCaches().iterator().next();try {return this.wrapCacheValue(method, this.handleSynchronizedGet(invoker, key, cache));} catch (Cache.ValueRetrievalException var10) {Cache.ValueRetrievalException ex var10;ReflectionUtils.rethrowRuntimeException(ex.getCause());}}this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT);Cache.ValueWrapper cacheHit this.findCachedItem(contexts.get(CacheableOperation.class));ListCachePutRequest cachePutRequests new ArrayList();if (cacheHit null) {this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);}Object cacheValue;Object returnValue;if (cacheHit ! null !this.hasCachePut(contexts)) {cacheValue cacheHit.get();returnValue this.wrapCacheValue(method, cacheValue);} else {returnValue this.invokeOperation(invoker);cacheValue this.unwrapReturnValue(returnValue);}this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);Iterator var8 cachePutRequests.iterator();while(var8.hasNext()) {CachePutRequest cachePutRequest (CachePutRequest)var8.next();cachePutRequest.apply(cacheValue);}this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);return returnValue;
}
流程图 核心注解以及使用
这里可以看这篇博客SpringCache详解_spring cache-CSDN博客
公共属性
cacheNames
每个注解中都有自己的缓存名字。该名字的缓存与方法相关联每次调用时都会检查缓存以查看是否有对应cacheNames名字的数据避免重复调用方法。名字可以可以有多个在这种情况下在执行方法之前如果至少命中一个缓存则返回相关联的值。 Springcache提供两个参数来指定缓存名value、cacheNames二者选其一即可每一个需要缓存的数据都需要指定要放到哪个名字的缓存缓存的分区按照业务类型分 KeyGeneratorkey生成器
缓存的本质是key-value存储模式每一次方法的调用都需要生成相应的Key, 才能操作缓存。
通常情况下Cacheable有一个属性key可以直接定义缓存key开发者可以使用SpEL语言定义key值。若没有指定属性key缓存抽象提供了 KeyGenerator来生成key 具体源码如下 import java.lang.reflect.Method;public class SimpleKeyGenerator implements KeyGenerator {public SimpleKeyGenerator() {}public Object generate(Object target, Method method, Object... params) {return generateKey(params);}public static Object generateKey(Object... params) {if (params.length 0) {return SimpleKey.EMPTY;} else {if (params.length 1) {Object param params[0];if (param ! null !param.getClass().isArray()) {return param;}}return new SimpleKey(params);}}
}
如果没有参数则直接返回SimpleKey.EMPTY如果只有一个参数则直接返回该参数若有多个参数则返回包含多个参数的SimpleKey对象。
当然Spring Cache也考虑到需要自定义Key我们可以通过实现KeyGenerator 接口来重新定义key生成方式
默认的 key 生成器要求参数具有有效的 hashCode() 和 equals() 方法实现。
key
key缓存的key如果是redis则相当于redis的key
可以为空如果需要可以使用spel表达式进行表写。如果为空则缺省默认使用key表达式生成器进行生成。默认的 key 生成器要求参数具有有效的 hashCode() 和 equals() 方法实现。key的生成器。key/keyGenerator二选一使用
condition缓存的条件对入参进行判断
可以为空如果需要指定需要使用SPEL表达式返回true/false只有返回true的时候才会对数据源进行缓存/清除缓存。在方法调用之前或之后都能进行判断。
conditionfalse时不读取缓存直接执行方法体并返回结果同时返回结果也不放入缓存。
conditiontrue时读取缓存有缓存则直接返回。无则执行方法体同时返回结果放入缓存如果配置了result且要求不为空则不会缓存结果。
注意
condition 属性使用的SpEL语言只有#root和获取参数类的SpEL表达式,不能使用返回结果的#result 。 所以 condition #result ! null 会导致所有对象都不进入缓存,每次操作都要经过数据库。
使用实例
Override
Caching(evict {CacheEvict(value RedisConstants.CacheName.ZL_CACHE, key ACTIVE_REGIONS),CacheEvict(value RedisConstants.CacheName.SERVE_ICON, key #id),CacheEvict(value RedisConstants.CacheName.SERVE_TYPE, key #id),CacheEvict(value RedisConstants.CacheName.HOT_SERVE, key #id)
})
注解
Cacheable在方法执行前查看是否有缓存对应的数据如果有直接返回数据如果没有调用方法获取数据返回并缓存起来也就是查询数据时缓存将方法的返回值进行缓存
1、unless条件符合则不缓存对出参进行判断
unless属性可以使用#result表达式。效果: 缓存如果有符合要求的缓存数据则直接返回没有则去数据库查数据查到了就返回并且存在缓存一份没查到就不存缓存。
condition 不指定相当于 trueunless 不指定相当于 false 当 condition false一定不会缓存 当 condition true且 unless true不缓存 当 condition true且 unless false缓存
2、sync是否使用异步默认是false.
在一个多线程的环境中某些操作可能被相同的参数并发地调用同一个 value 值可能被多次计算或多次访问 db这样就达不到缓存的目的。针对这些可能高并发的操作我们可以使用 sync 参数来告诉底层的缓存提供者将缓存的入口锁住这样就只能有一个线程计算操作的结果值而其它线程需要等待。当值为true相当于同步可以有效的避免缓存击穿的问题。
CachePut方法在执行前不会去检查缓存中是否存在之前执行过的结果而是每次都会执行该方法并将执行结果以键值对的形式存入指定的缓存中简单来说就是更新缓存将方法的返回值放到缓存中
CacheEvict用于清空缓存方法在调用时会从缓存中移除已存储的数据
1、allEntries是否清空左右缓存。默认为false
当指定了allEntries为true时Spring Cache将忽略指定的key
2、beforeInvocation是否在方法执行前就清空默认为 false可以看上面的流程图
清除操作默认是在对应方法成功执行之后触发的即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间当我们指定该属性值为true时Spring会在调用该方法之前清除缓存中的指定元素。
Caching组合多个缓存注解
xxl-job详解
在分布式环境下进行任务调度需要使用分布式任务调度平台XXL-JOB是一个轻量级分布式任务调度平台其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线开箱即用。
官网https://www.xuxueli.com/xxl-job/
文档https://www.xuxueli.com/xxl-job/#%E3%80%8A%E5%88%86%E5%B8%83%E5%BC%8F%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6%E5%B9%B3%E5%8F%B0XXL-JOB%E3%80%8B
XXL-JOB主要有调度中心、执行器、任务 调度中心
负责管理调度信息按照调度配置发出调度亲你跪求自身不承担业务代码
主要职责为执行器管理、任务管理、监控运维、日志管理等
任务执行器
负责接收调度请求并执行任务逻辑
主要职责是执行任务、执行结果上报、日志服务等
使用xxl-job解决多个jvm进程冲入执行任务问题 XXL-JOB调度中心可以配置路由策略第一个、轮询策略、分片等
第一个每次执行任务都由第一个执行器去执行
轮询执行器轮番执行
分片每次执行任务广播给每个执行器让他们同时执行任务
如果根据需求每次执行任务仅由一个执行器去执行任务可以设置路由策略第一个、轮询
如果根据需求每次执行任务由多个执行器同时执行可以设置路由策略分片
SpringcacheRedis实现缓存
这里借用我最近做的一个项目的代码举例功能是将访问频率较高的查询开通区域列表接口进行缓存并且在每天的凌晨1点实现缓存刷新更新信息
启用区域后删除已开通区域列表缓存当之后去查询开通区域列表时重新缓存最新的开通区域列表。
/*** 已开通服务区域列表** return 区域简略列表*/OverrideCacheable(value RedisConstants.CacheName.ZL_CACHE, key ACTIVE_REGIONS, cacheManager RedisConstants.CacheManager.FOREVER)public ListRegionSimpleResDTO queryActiveRegionListCache() {return queryActiveRegionList();}
xxl-job定时刷新缓存
foundations包下面的处理器handler类定义了xxl-job实现缓存同步任务的定时任务
删除缓存-查询开通区域列表进行缓存-遍历区域列表下的服务类型进行缓存
package com.zhilian.foundations.handler;import com.zhilian.api.foundations.dto.response.RegionSimpleResDTO;
import com.zhilian.foundations.constants.RedisConstants;
import com.zhilian.foundations.service.HomeService;
import com.zhilian.foundations.service.IRegionService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.List;/*** springCache缓存同步任务***/
Slf4j
Component
public class SpringCacheSyncHandler {Resourceprivate IRegionService regionService;Resourceprivate RedisTemplate redisTemplate;Resourceprivate HomeService homeService;/*** 已启用区域缓存更新* 每日凌晨1点执行*/XxlJob(value activeRegionCacheSync)public void activeRegionCacheSync() {log.info(开始进行缓存同步更新已启用区域);//删除缓存Boolean delete redisTemplate.delete(RedisConstants.CacheName.ZL_CACHE ::ACTIVE_REGIONS);//通过查询开通区域列表进行缓存ListRegionSimpleResDTO regionSimpleResDTOS regionService.queryActiveRegionList();//遍历区域对该区域下的服务类型进行缓存regionSimpleResDTOS.forEach(item - {//区域idLong regionId item.getId();//删除该区域下的首页服务列表String serve_list_key RedisConstants.CacheName.SERVE_ICON :: regionId;redisTemplate.delete(serve_list_key);homeService.queryServeIconCategoryByRegionIdCache(regionId);// 删除该区域下的服务类型列表缓存String serve_type_key RedisConstants.CacheName.SERVE_TYPE :: regionId;redisTemplate.delete(serve_type_key);homeService.queryServeTypeList(regionId);// 删除该区域下的服务类型列表缓存String serve_hot_list_key RedisConstants.CacheName.HOT_SERVE :: regionId;redisTemplate.delete(serve_hot_list_key);homeService.queryHotServeListByRegionIdCache(regionId);});}
}
代码写好了需要去xxl-job调度中心对该定时任务进行管理 填写任务信息 说明
调度类型CRON
固定速度指按固定的间隔定时调度
Cron通过Cron表达式实现更丰富的定时调度策略
Cron表达式是一个字符串通过它可以定义调度策略格式
{秒数}{分钟}{小时}{日期}{月份}{星期}{年份可为空}
xxl-job提供图形界面配置 运行模式BEAN和GLUEbean模式较常用就是在项目工程中编写执行器的任务代码GLUE是将任务代码编写在调度中心Bean模式通常有两种形式的实现类形式和方法形式此处使用的是方法形式即在方法上加上XxlJob注解
JobHandler即任务方法名填写任务方法上边XxlJob注解中的名称
任务配置完成下边启动任务 启动成功 我们在任务方法上打断点跟踪任务方法被执行如下图 关于springcache的具体使用可以看看这个文档SpringCache详解_spring cache-CSDN博客
关于xxl-job的原理、架构分析以及使用见这篇文档
3千字带你搞懂XXL-JOB任务调度平台-阿里云开发者社区