SpringBoot2-番外:第一章:Spring Cache註解簡單分析

番外篇?覺得是小說嗎?沒錯,在我看來代碼就是小說,每一個工程師都是小說做者,大致框架是相似的咱們只是要適當的去按照本身的想法去完善就好了。html

本項目的GitHub:https://github.com/pc859107393/Go2SpringBoot.gitjava

有興趣交流springboot進行快速開發的同窗能夠加一下下面的企鵝羣。git

行走的java全棧

認識Spring cache

Spring cache技術和咱們前面遇到的其餘Spring相關的技術相似,這裏Spring並無提供真正的cache的實現,而是提供了對緩存使用的抽象。咱們只須要使用對應的cache註解就能實現cache的效果。github

一樣的,這樣的設計有良好的可擴展性,咱們能夠依賴這些設置實現和專業緩存(Ehcache)的集成。spring

簡單的cache構思

若是咱們要實現一個簡單緩存該怎麼作呢?首先咱們根據也無須要,通常來講就是簡單的增刪改查和清理全部緩存的接口和實現。數據庫

/** * 簡單緩存接口 * @author cheng */
interface Cache<T> {
    fun add(key: Any, @Nullable value: T?)

    fun get(key: Any): T?

    fun remove(key: Any)

    fun update(key: Any, @Nullable value: T?)

    fun clear()
}

/** *簡單的緩存,使用的時候初始化就行 */
class MapCache<T> : Cache<T> {
    private var hashMap: HashMap<Any, T?> = hashMapOf()

    override fun add(key: Any, value: T?) {
        hashMap[key] = value
    }

    override fun get(key: Any): T? = hashMap[key]

    override fun update(key: Any, value: T?) {
        hashMap[key] = value
    }

    override fun remove(key: Any) {
        hashMap.remove(key)
    }

    override fun clear() {
        hashMap.clear()
    }
}
複製代碼

我相信上面這種簡單的緩存處理,你們應該都是會使用的,在方法裏面作一下判斷爲空就存入不爲空直接取出。segmentfault

可是這樣的緩存有什麼缺陷呢?緩存

  • 耦合過高
  • 侵入性過高
  • 更換緩存方案代價過高
  • 維護成本極高

Spring的緩存抽象

在Spring-context包下面給咱們提供了對應的緩存抽象,完整的路徑爲org.springframework.cache,咱們先看看這個包下面有哪些東西。springboot

  • org.springframework.cache

    • annotation
    • concurrent
    • config
    • interceptor
    • support
    • Cache.java(Cache接口)
    • CacheManager.java(CacheManager接口)

在這上面,咱們最直觀的能夠看到concurrentsupport多是cache的一些具體實現,畢竟concurrent表明的是併發,support表明的是實現。併發

annotation這個包下面,咱們能夠看到有CacheableCacheConfigCacheEvictCachePutCachingEnableCaching這些註解,他們對應都有各自的功能。

註解名稱 主要做用 使用範圍 參考案例
EnableCaching 緩存整個bean,可用於緩存某個service 配合Configuration註解標記某個cacheConfig類 Spring的@EnableCaching註解
CacheConfig 提供一個緩存默認設置的集合給某個被標記類的全部方法 標記在某個類上,經常使用於service層 1️⃣Spring緩存註解@Cacheable,@CachePut , @CacheEvict使用
Cacheable 主要做用在方法上,對該方法結果進行緩存 標記在某個方法上,緩存該方法的結果;緩存在某個類上,緩存該類下面全部方法的結果 2️⃣Spring緩存註解@Cacheable、@CacheEvict、@CachePut使用
CachePut 提供緩存,可是每次都會觸發真實調用,並將結果緩存。能夠同步數據庫結果到緩存中 經常使用於標記某個方法,並更新結果到緩存中(也可緩存整個類) 1️⃣Spring緩存註解@Cacheable,@CachePut , @CacheEvict使用
CacheEvict 更夠清空緩存 某個方法上面標記,能夠在必定條件下清空緩存(也能夠標記某個類) 1️⃣Spring緩存註解@Cacheable,@CachePut , @CacheEvict使用
Caching 對多個緩存註釋(不一樣或相同類型)的組註釋。 方法和類都可進行標記 請查看spring官方文檔

緊接着咱們能夠看看annotation這個包下面的其餘資源,具體闡述以下:

  • AbstractCachingConfiguration -> 一個抽象的公共緩存管理功能
/** * 提供公共的緩存管理功能 */
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {

	@Nullable
	protected AnnotationAttributes enableCaching;   

	@Nullable
	protected CacheManager cacheManager;    //緩存管理器

	@Nullable
	protected CacheResolver cacheResolver;  //緩存解析器

	@Nullable
	protected KeyGenerator keyGenerator;    //key生成器

	@Nullable
	protected CacheErrorHandler errorHandler; //錯誤Handler


	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {//代碼省略···}

	@Autowired(required = false)
	void setConfigurers(Collection<CachingConfigurer> configurers) {//代碼省略···}

	/** * 導入CachingConfigurer,並拆箱後賦值給cacheManager、cacheResolver、keyGenerator、errorHandler */
	protected void useCachingConfigurer(CachingConfigurer config) {//代碼省略···}
}
複製代碼
  • AnnotationCacheOperationSource -> CacheOperationSource接口的實現,用於註解標記的緩存元數據
/** * CacheOperationSource接口的實現,用於註解標記的緩存元數據。 */
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {

	private final boolean publicMethodsOnly; //是否只支持公開方法

	private final Set<CacheAnnotationParser> annotationParsers; //註解解析器


	/** * Create a default AnnotationCacheOperationSource, supporting public methods * that carry the {@code Cacheable} and {@code CacheEvict} annotations. * 構造一個默認的AnnotationCacheOperationSource,只支持那些被@Cacheable@CacheEvict標記的公開方法。 */
	public AnnotationCacheOperationSource() {
		this(true);
	}
    

	@Override
	@Nullable
	protected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) {
		return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
	}

	@Override
	@Nullable
	protected Collection<CacheOperation> findCacheOperations(final Method method) {
		return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
	}
	
	//上面的兩個findCacheOperations方法,結合構造函數的註釋來看,也能夠找到這裏對@Cacheable和@CacheEvict的註解做用域的一個可能描述


	/** * 肯定緩存操做,經過對全局的註解解析器的遍歷,獲取對應的CacheOperation集合並返回 */
	@Nullable
	protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
		Collection<CacheOperation> ops = null;
		for (CacheAnnotationParser annotationParser : this.annotationParsers) {
			Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
			if (annOps != null) {
				if (ops == null) {
					ops = new ArrayList<>();
				}
				ops.addAll(annOps);
			}
		}
		return ops;
	}
	
	/** * Callback interface providing {@link CacheOperation} instance(s) based on * a given {@link CacheAnnotationParser}. */
	@FunctionalInterface
	protected interface CacheOperationProvider {

		/** * Return the {@link CacheOperation} instance(s) provided by the specified parser. * @param parser the parser to use * @return the cache operations, or {@code null} if none found */
		@Nullable
		Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser);
	}
	
}
複製代碼
  • CachingConfigurer接口和CachingConfigurerSupport是CachingConfigurer接口的實現類,CachingConfigurer提供了cache相關的一些東西入:CacheManager、CacheResolver、KeyGenerator和CacheErrorHandler。

  • CachingConfigurationSelector是CachingConfiguration的選擇器,根據配置選擇Proxy或AspectJ。

  • ProxyCachingConfiguration 緩存的代理管理

/** * 首先標記爲設置類,繼承AbstractCachingConfiguration,說明這是抽閒緩存管理的具體實現 */
@Configuration
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
    
    //全部的BeanDefinition.ROLE_INFRASTRUCTURE 都是spring內部指明的後臺工做的bean
	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
		BeanFactoryCacheOperationSourceAdvisor advisor =
				new BeanFactoryCacheOperationSourceAdvisor();
		advisor.setCacheOperationSource(cacheOperationSource());
		advisor.setAdvice(cacheInterceptor());
		if (this.enableCaching != null) {
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}
    
    //值得注意的是Spring中的bean默認都是單例的,因此這裏屢次調用的本方法實際是同一個
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheOperationSource cacheOperationSource() {
		return new AnnotationCacheOperationSource();
	}
    
    //建立緩存攔截器
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheInterceptor cacheInterceptor() {
		CacheInterceptor interceptor = new CacheInterceptor();
		interceptor.setCacheOperationSources(cacheOperationSource());
		if (this.cacheResolver != null) {
			interceptor.setCacheResolver(this.cacheResolver);
		}
		else if (this.cacheManager != null) {
			interceptor.setCacheManager(this.cacheManager);
		}
		if (this.keyGenerator != null) {
			interceptor.setKeyGenerator(this.keyGenerator);
		}
		if (this.errorHandler != null) {
			interceptor.setErrorHandler(this.errorHandler);
		}
		return interceptor;
	}
}
複製代碼
  • SpringCacheAnnotationParser Spring的緩存註解解析器
//片斷一
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
    @Override
	@Nullable
	public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {//省略代碼···}

	@Override
	@Nullable
	public Collection<CacheOperation> parseCacheAnnotations(Method method) {//省略代碼···}

    //上面兩個parseCacheAnnotations方法分別是根據類和方法的註解來調用下面這個parseCacheAnnotations
    
    //這裏再調用下面的parseCacheAnnotations,實現具體的某個
	@Nullable
	protected Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
		Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
		if (ops != null && ops.size() > 1 && ae.getAnnotations().length > 0) {
			// More than one operation found -> local declarations override interface-declared ones...
			Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
			if (localOps != null) {
				return localOps;
			}
		}
		return ops;
	}

	@Nullable
	private Collection<CacheOperation> parseCacheAnnotations( DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
			    //這裏根據註解的不一樣實現不一樣的操做
	}
	
	CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
	    //Cacheable操做的註解解析信息組裝
	}
    //下面還有相似的parseEvictAnnotation、parsePutAnnotation、parseCachingAnnotation幾個方法,對應了本身的註解信息組裝

    static class DefaultCacheConfig {

		@Nullable
		private final String[] cacheNames;

		@Nullable
		private final String keyGenerator;

		@Nullable
		private final String cacheManager;

		@Nullable
		private final String cacheResolver;
	    
	    //省略構造函數代碼
	    
	    //應用默認緩存的設置
	    public void applyDefault(CacheOperation.Builder builder) {//省略代碼···}
	}
}
複製代碼

在整個org.springframework.cache.annotation的包下面,咱們能夠看到是緩存操做的註解以及相關的解析和緩存設置的一些資源。這時候能夠猜想一下大概思路是根據配置生成對應的註解配置,再掃描註解後利用註解配置指定的緩存框架來實現緩存操做。


到此爲止,基本把註解包下面的東西分析了一下,其實思路仍是比較清晰的,首先看那幾個註解,接着看抽象類和對應實現,最後考慮接口和實現就好了。

其實真真的springcache遠遠不止這些,咱們如今看到的僅僅是一角而已。

相關文章
相關標籤/搜索