日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring源码分析之cache注解

發布時間:2025/4/5 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring源码分析之cache注解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Spring 3.1 引入了激動人心的基于注釋(annotation)的緩存(cache)技術,它本質上不是一個具體的緩存實現方案(例如EHCache 或者 OSCache),而是一個對緩存使用的抽象,通過在既有代碼中添加少量它定義的各種 annotation,即能夠達到緩存方法的返回對象的效果。

Spring 的緩存技術還具備相當的靈活性,不僅能夠使用 SpEL(Spring Expression Language)來定義緩存的 key 和各種 condition,還提供開箱即用的緩存臨時存儲方案,也支持和主流的專業緩存例如 EHCache 集成。

其特點總結如下:

  • 通過少量的配置 annotation 注釋即可使得既有代碼支持緩存
  • 支持開箱即用 Out-Of-The-Box,即不用安裝和部署額外第三方組件即可使用緩存
  • 支持 Spring Express Language,能使用對象的任何屬性或者方法來定義緩存的 key 和 condition
  • 支持 AspectJ,并通過其實現任何方法的緩存支持
  • 支持自定義 key 和自定義緩存管理者,具有相當的靈活性和擴展性

1.EnableCaching

開啟spring注解驅動的cache管理能力,類似于spring xml命名空間<cache:*>的支持。將會和org.springframework.context.annotation.Configuration一起使用,示例:

@Configuration@EnableCachingpublic class AppConfig {@Beanpublic MyService myService() {// configure and return a class having @Cacheable methodsreturn new MyService();}@Beanpublic CacheManager cacheManager() {// configure and return an implementation of Spring's CacheManager SPISimpleCacheManager cacheManager = new SimpleCacheManager();cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("default")));return cacheManager;}}

相應的xml配置如下:

<beans><cache:annotation-driven/><bean id="myService" class="com.foo.MyService"/><bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"><property name="caches"><set><bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"><property name="name" value="default"/></bean></set></property></bean></beans>

  在上述的場景中,@EnableCaching和<cache:annotation-driven/>負責注冊spring所需的管理注解驅動的cache管理組件,如org.springframework.cache.interceptor.CacheInterceptor,當org.springframework.cache.annotation.Cacheable方法觸發時基于proxy-或者基于AspectJ的advice 會將interceptor織入調用棧中。

  必須注冊org.springframework.cache.CacheManager類型的bean,原因是框架沒有默認的可用。不同點在于<cache:annotation-driven>元素假定了一個名稱為cacheManager的bean,而@EnableCaching通過類型檢索一個cache manage bean。因此cache manager bean方法的命名不是顯式的。

  如果希望使用一種@EnableCaching與cache manager bean更直接的方式,spring提供了CachingConfigurer回調接口的實現,注意實現語句和@override注解方法:

@Configuration@EnableCachingpublic class AppConfig extends CachingConfigurerSupport {@Beanpublic MyService myService() {// configure and return a class having @Cacheable methodsreturn new MyService();}@Bean@Overridepublic CacheManager cacheManager() {// configure and return an implementation of Spring's CacheManager SPISimpleCacheManager cacheManager = new SimpleCacheManager();cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));return cacheManager;}@Bean@Overridepublic KeyGenerator keyGenerator() {// configure and return an implementation of Spring's KeyGenerator SPIreturn new MyKeyGenerator();}}

?注意,上述示例中的keyGenerator方法,它允許自定義一個cache key產生策略。每個spring的org.springframework.cache.interceptor.KeyGenerator spi,通常將配置spring的org.springframework.cache.interceptor.SimpleKeyGenerator,一個key生成器必須顯示指明。如果不需要定制,則直接返回new SimpleKeyGenerator()。

1.1 方法

boolean proxyTargetClass() default false;

表明是創建基于子類代理(cglib)還是基于接口的標準java代理,默認是false。當且僅當mode()設置成Advice#PROXY時其作用。

注意,這個屬性設置為true時會影響到所有spring管理bean的代理,而不僅僅是被標識為@Cacheable的接口或者類。例如,spring標注的其它bean如@Transactional將會同時升級到子類代理。這種方式實際上沒有負面影響,除非顯示期望一種代理類型,例如測試。

1.2 源碼

1.2.1 xml源碼解析

CacheNamespaceHandler.java

public class CacheNamespaceHandler extends NamespaceHandlerSupport {static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager";static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager";static String extractCacheManager(Element element) {return (element.hasAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE) ? element.getAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE): CacheNamespaceHandler.DEFAULT_CACHE_MANAGER_BEAN_NAME);}static BeanDefinition parseKeyGenerator(Element element, BeanDefinition def) {String name = element.getAttribute("key-generator");if (StringUtils.hasText(name)) {def.getPropertyValues().add("keyGenerator", new RuntimeBeanReference(name.trim()));}return def;}@Overridepublic void init() {registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser());registerBeanDefinitionParser("advice", new CacheAdviceParser());} }

1.2.1.1 注冊了AnnotationDrivenCacheBeanDefinitionParser

/*** Parses the '{@code <cache:annotation-driven>}' tag. Will* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary* register an AutoProxyCreator} with the container as necessary.*/@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {String mode = element.getAttribute("mode");if ("aspectj".equals(mode)) {// mode="aspectj" registerCacheAspect(element, parserContext);}else {// mode="proxy" AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;}

aspectJ方式

/*** Registers a* <pre class="code">* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">* <property name="cacheManager" ref="cacheManager"/>* <property name="keyGenerator" ref="keyGenerator"/>* </bean>* </pre>*/private void registerCacheAspect(Element element, ParserContext parserContext) {if (!parserContext.getRegistry().containsBeanDefinition(AnnotationConfigUtils.CACHE_ASPECT_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition();def.setBeanClassName(AnnotationConfigUtils.CACHE_ASPECT_CLASS_NAME);def.setFactoryMethodName("aspectOf");parseCacheManagerProperty(element, def);CacheNamespaceHandler.parseKeyGenerator(element, def);parserContext.registerBeanComponent(new BeanComponentDefinition(def, AnnotationConfigUtils.CACHE_ASPECT_BEAN_NAME));}}

aop方式:

/*** Inner class to just introduce an AOP framework dependency when actually in proxy mode.*/private static class AopAutoProxyConfigurer {public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);if (!parserContext.getRegistry().containsBeanDefinition(AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME)) {Object eleSource = parserContext.extractSource(element);// Create the CacheOperationSource definition.RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.cache.annotation.AnnotationCacheOperationSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);// Create the CacheInterceptor definition.RootBeanDefinition interceptorDef = new RootBeanDefinition(CacheInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);parseCacheManagerProperty(element, interceptorDef);CacheNamespaceHandler.parseKeyGenerator(element, interceptorDef);interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName));String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);// Create the CacheAdvisor definition.RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheOperationSourceAdvisor.class);advisorDef.setSource(eleSource);advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);advisorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);if (element.hasAttribute("order")) {advisorDef.getPropertyValues().add("order", element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME, advisorDef);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),eleSource);compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME));parserContext.registerComponent(compositeDef);}}

1.2.1.2 注冊了CacheAdviceParser,處理子注解

@Overrideprotected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {builder.addPropertyReference("cacheManager", CacheNamespaceHandler.extractCacheManager(element));CacheNamespaceHandler.parseKeyGenerator(element, builder.getBeanDefinition());List<Element> cacheDefs = DomUtils.getChildElementsByTagName(element, DEFS_ELEMENT);if (cacheDefs.size() >= 1) {// Using attributes source.List<RootBeanDefinition> attributeSourceDefinitions = parseDefinitionsSources(cacheDefs, parserContext);builder.addPropertyValue("cacheOperationSources", attributeSourceDefinitions);}else {// Assume annotations source.builder.addPropertyValue("cacheOperationSources",new RootBeanDefinition("org.springframework.cache.annotation.AnnotationCacheOperationSource"));}}

?

調用解析方法,包括

cacheable, cache-evict, cache-put, method, caching private static final String CACHEABLE_ELEMENT = "cacheable";private static final String CACHE_EVICT_ELEMENT = "cache-evict";private static final String CACHE_PUT_ELEMENT = "cache-put";private static final String METHOD_ATTRIBUTE = "method";private static final String DEFS_ELEMENT = "caching";private RootBeanDefinition parseDefinitionSource(Element definition, ParserContext parserContext) {Props prop = new Props(definition);// add cacheable first ManagedMap<TypedStringValue, Collection<CacheOperation>> cacheOpMap = new ManagedMap<TypedStringValue, Collection<CacheOperation>>();cacheOpMap.setSource(parserContext.extractSource(definition));List<Element> cacheableCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHEABLE_ELEMENT);for (Element opElement : cacheableCacheMethods) {String name = prop.merge(opElement, parserContext.getReaderContext());TypedStringValue nameHolder = new TypedStringValue(name);nameHolder.setSource(parserContext.extractSource(opElement));CacheableOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheableOperation());op.setUnless(getAttributeValue(opElement, "unless", ""));Collection<CacheOperation> col = cacheOpMap.get(nameHolder);if (col == null) {col = new ArrayList<CacheOperation>(2);cacheOpMap.put(nameHolder, col);}col.add(op);}List<Element> evictCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_EVICT_ELEMENT);for (Element opElement : evictCacheMethods) {String name = prop.merge(opElement, parserContext.getReaderContext());TypedStringValue nameHolder = new TypedStringValue(name);nameHolder.setSource(parserContext.extractSource(opElement));CacheEvictOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheEvictOperation());String wide = opElement.getAttribute("all-entries");if (StringUtils.hasText(wide)) {op.setCacheWide(Boolean.valueOf(wide.trim()));}String after = opElement.getAttribute("before-invocation");if (StringUtils.hasText(after)) {op.setBeforeInvocation(Boolean.valueOf(after.trim()));}Collection<CacheOperation> col = cacheOpMap.get(nameHolder);if (col == null) {col = new ArrayList<CacheOperation>(2);cacheOpMap.put(nameHolder, col);}col.add(op);}List<Element> putCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_PUT_ELEMENT);for (Element opElement : putCacheMethods) {String name = prop.merge(opElement, parserContext.getReaderContext());TypedStringValue nameHolder = new TypedStringValue(name);nameHolder.setSource(parserContext.extractSource(opElement));CachePutOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CachePutOperation());op.setUnless(getAttributeValue(opElement, "unless", ""));Collection<CacheOperation> col = cacheOpMap.get(nameHolder);if (col == null) {col = new ArrayList<CacheOperation>(2);cacheOpMap.put(nameHolder, col);}col.add(op);}RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchCacheOperationSource.class);attributeSourceDefinition.setSource(parserContext.extractSource(definition));attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpMap);return attributeSourceDefinition;}

?其中,

    • @Cacheable 主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存
    • @CachePut 主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存,和 @Cacheable 不同的是,它每次都會觸發真實方法的調用
    • @CachEvict?主要針對方法配置,能夠根據一定的條件對緩存進行清空

?

@Cacheable、@CachePut、@CacheEvict 注釋介紹

通過上面的源碼,我們可以看到 spring cache 主要使用兩個注釋標簽,即 @Cacheable、@CachePut 和 @CacheEvict,我們總結一下其作用和配置方法。

表 1. @Cacheable 作用和配置方法
@Cacheable 的作用主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存
@Cacheable 主要的參數
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=”#userName.length()>2”)
表 2. @CachePut 作用和配置方法
@CachePut 的作用主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存,和 @Cacheable 不同的是,它每次都會觸發真實方法的調用
@CachePut 主要的參數
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=”#userName.length()>2”)
表 3. @CacheEvict 作用和配置方法
@CachEvict 的作用主要針對方法配置,能夠根據一定的條件對緩存進行清空
@CacheEvict 主要的參數
value緩存的名稱,在 spring 配置文件中定義,必須指定至少一個例如:
@CachEvict(value=”mycache”) 或者?
@CachEvict(value={”cache1”,”cache2”}
key緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合例如:
@CachEvict(value=”testcache”,key=”#userName”)
condition緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才清空緩存例如:
@CachEvict(value=”testcache”,
condition=”#userName.length()>2”)
allEntries是否清空所有緩存內容,缺省為 false,如果指定為 true,則方法調用后將立即清空所有緩存例如:
@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation是否在方法執行前就清空,缺省為 false,如果指定為 true,則在方法還沒有執行的時候就清空緩存,缺省情況下,如果方法執行拋出異常,則不會清空緩存例如:
@CachEvict(value=”testcache”,beforeInvocation=true)

小結

  總之,注釋驅動的 spring cache 能夠極大的減少我們編寫常見緩存的代碼量,通過少量的注釋標簽和配置文件,即可達到使代碼具備緩存的能力。且具備很好的靈活性和擴展性。但是我們也應該看到,spring cache 由于基于 spring AOP 技術,尤其是動態的 proxy 技術,導致其不能很好的支持方法的內部調用或者非 public 方法的緩存設置,當然這都是可以解決的問題,通過學習這個技術,我們能夠認識到,AOP 技術的應用還是很廣泛的,如果有興趣,我相信你也能基于 AOP 實現自己的緩存方案。

參考文獻

【1】https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/

轉載于:https://www.cnblogs.com/davidwang456/p/5703037.html

總結

以上是生活随笔為你收集整理的spring源码分析之cache注解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。