重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
小编给大家分享一下Spring Boot缓存源码是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
成都创新互联-专业网站定制、快速模板网站建设、高性价比青海网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式青海网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖青海地区。费用合理售后完善,10多年实体公司更值得信赖。
项目里面要增加一个应用缓存,原本想着要怎么怎么来整合ehcache和springboot,做好准备配置这个配置那个,结果只需要做三件事:
pom依赖
写好一个ehcache的配置文件
在boot的application上加上注解@EnableCaching.
这就完事了,是不是很魔幻。
pom依赖
net.sf.ehcache ehcache 2.10.5
配置文件
应用上加上EnableCaching注解
@SpringBootApplication @EnableCaching public class EhCacheApplication { public static void main(String[] args) { SpringApplication.run(EhCacheApplication.class, args); } }
然后就可以在代码里面使用cache注解了,像这样。
@CachePut(value = "fish-ehcache", key = "#person.id") public Person save(Person person) { System.out.println("为id、key为:" + person.getId() + "数据做了缓存"); return person; } @CacheEvict(value = "fish-ehcache") public void remove(Long id) { System.out.println("删除了id、key为" + id + "的数据缓存"); } @Cacheable(value = "fish-ehcache", key = "#person.id") public Person findOne(Person person) { findCount.incrementAndGet(); System.out.println("为id、key为:" + person.getId() + "数据做了缓存"); return person; }
很方便对不对。下面,我们就来挖一挖,看看spring是怎么来做到的。主要分成两部分,一是启动的时候做了什么,二是运行的时候做了什么,三是和第三方缓存组件的适配
启动的时候做了什么、
这个得从@EnableCaching标签开始,在使用缓存功能时,在springboot的Application启动类上需要添加注解@EnableCaching,这个标签引入了
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({CachingConfigurationSelector.class}) public @interface EnableCaching { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default 2147483647; }
引入了CachingConfigurationSelector类,这个类便开启了缓存功能的配置。这个类添加了AutoProxyRegistrar.java,ProxyCachingConfiguration.java两个类。
AutoProxyRegistrar : 实现了ImportBeanDefinitionRegistrar接口。这里看不懂,还需要继续学习。
ProxyCachingConfiguration : 是一个配置类,生成了BeanFactoryCacheOperationSourceAdvisor,CacheOperationSource,和CacheInterceptor这三个bean。
CacheOperationSource封装了cache方法签名注解的解析工作,形成CacheOperation的集合。CacheInterceptor使用该集合过滤执行缓存处理。解析缓存注解的类是SpringCacheAnnotationParser,其主要方法如下
/** 由CacheOperationSourcePointcut作为注解切面,会解析 SpringCacheAnnotationParser.java 扫描方法签名,解析被缓存注解修饰的方法,将生成一个CacheOperation的子类并将其保存到一个数组中去 **/ protected CollectionparseCacheAnnotations(SpringCacheAnnotationParser.DefaultCacheConfig cachingConfig, AnnotatedElement ae) { Collection ops = null; //找@cacheable注解方法 Collection cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class); if (!cacheables.isEmpty()) { ops = this.lazyInit(ops); Iterator var5 = cacheables.iterator(); while(var5.hasNext()) { Cacheable cacheable = (Cacheable)var5.next(); ops.add(this.parseCacheableAnnotation(ae, cachingConfig, cacheable)); } } //找@cacheEvict注解的方法 Collection evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class); if (!evicts.isEmpty()) { ops = this.lazyInit(ops); Iterator var12 = evicts.iterator(); while(var12.hasNext()) { CacheEvict evict = (CacheEvict)var12.next(); ops.add(this.parseEvictAnnotation(ae, cachingConfig, evict)); } } //找@cachePut注解的方法 Collection puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class); if (!puts.isEmpty()) { ops = this.lazyInit(ops); Iterator var14 = puts.iterator(); while(var14.hasNext()) { CachePut put = (CachePut)var14.next(); ops.add(this.parsePutAnnotation(ae, cachingConfig, put)); } } Collection cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class); if (!cachings.isEmpty()) { ops = this.lazyInit(ops); Iterator var16 = cachings.iterator(); while(var16.hasNext()) { Caching caching = (Caching)var16.next(); Collection cachingOps = this.parseCachingAnnotation(ae, cachingConfig, caching); if (cachingOps != null) { ops.addAll(cachingOps); } } } return ops; }
解析Cachable,Caching,CachePut,CachEevict 这四个注解对应的方法都保存到了Collection
执行方法时做了什么
执行的时候,主要使用了CacheInterceptor类。
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable { public CacheInterceptor() { } public Object invoke(final MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() { public Object invoke() { try { return invocation.proceed(); } catch (Throwable var2) { throw new ThrowableWrapper(var2); } } }; try { return this.execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments()); } catch (ThrowableWrapper var5) { throw var5.getOriginal(); } } }
这个拦截器继承了CacheAspectSupport类和MethodInterceptor接口。其中CacheAspectSupport封装了主要的逻辑。比如下面这段。
/** CacheAspectSupport.java 执行@CachaEvict @CachePut @Cacheable的主要逻辑代码 **/ private Object execute(final CacheOperationInvoker invoker, Method method, CacheAspectSupport.CacheOperationContexts contexts) { if (contexts.isSynchronized()) { CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next(); if (this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) { Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT); Cache cache = (Cache)context.getCaches().iterator().next(); try { return this.wrapCacheValue(method, cache.get(key, new Callable
上面的代码片段比较核心,均是cache的内容,对于aop的源码,这里不详细展开,应该单起一篇文章进行研究。主要的类和接口都在spring的context中,org.springframework.cache包中。
和第三方缓存组件的适配
通过以上的分析,知道了spring cache功能的来龙去脉,下面需要分析的是,为什么只需要maven声明一下依赖,spring boot 就可以自动就适配了.
在上面的执行方法中,我们看到了cachePutRequest.apply(cacheValue),这里会操作缓存,CachePutRequest是CacheAspectSupport的内部类。
private class CachePutRequest { private final CacheAspectSupport.CacheOperationContext context; private final Object key; public CachePutRequest(CacheAspectSupport.CacheOperationContext context, Object key) { this.context = context; this.key = key; } public void apply(Object result) { if (this.context.canPutToCache(result)) { //从context中获取cache实例,然后执行放入缓存的操作 Iterator var2 = this.context.getCaches().iterator(); while(var2.hasNext()) { Cache cache = (Cache)var2.next(); CacheAspectSupport.this.doPut(cache, this.key, result); } } } }
Cache是一个标准接口,其中EhCacheCache就是EhCache的实现类。这里就是SpringBoot和Ehcache之间关联的部分,那么context中的cache列表是什么时候生成的呢。答案是CacheAspectSupport的getCaches方法
protected Collection extends Cache> getCaches(CacheOperationInvocationContextcontext, CacheResolver cacheResolver) { Collection extends Cache> caches = cacheResolver.resolveCaches(context); if (caches.isEmpty()) { throw new IllegalStateException("No cache could be resolved for '" + context.getOperation() + "' using resolver '" + cacheResolver + "'. At least one cache should be provided per cache operation."); } else { return caches; } }
而获取cache是在每一次进行进行缓存操作的时候执行。可以看一下调用栈
貌似有点跑题,拉回来... 在spring-boot-autoconfigure包里,有所有自动装配相关的类。这里有个EhcacheCacheConfiguration类 ,如下
@Configuration @ConditionalOnClass({Cache.class, EhCacheCacheManager.class}) @ConditionalOnMissingBean({CacheManager.class}) @Conditional({CacheCondition.class, EhCacheCacheConfiguration.ConfigAvailableCondition.class}) class EhCacheCacheConfiguration { ...... static class ConfigAvailableCondition extends ResourceCondition { ConfigAvailableCondition() { super("EhCache", "spring.cache.ehcache", "config", new String[]{"classpath:/ehcache.xml"}); } } }
这里会直接判断类路径下是否有ehcache.xml文件
以上是Spring Boot缓存源码是什么的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!