SpringBoot2-Fanwai: Глава 1: Простой анализ аннотаций Spring Cache

задняя часть база данных GitHub Spring

Дополнительная история? Вы думали, что это роман? Да, на мой взгляд, код — это роман, а каждый инженер — автор романа, общая схема аналогична, нужно только правильно доработать его в соответствии со своими идеями.

GitHub для этого проекта: https://github.com/pc859107393/Go2SpringBoot.git

Учащиеся, заинтересованные в общении со springboot для быстрой разработки, могут добавить следующую группу пингвинов.

行走的java全栈

Познакомьтесь с кэшем Spring

Технология кэширования Spring аналогична другим технологиям, связанным со Spring, с которыми мы сталкивались ранее.Здесь Spring предоставляет не реальную реализацию кэша, а абстракцию использования кэша. Нам нужно только использовать соответствующую аннотацию кеша, чтобы добиться эффекта кеша.

Кроме того, такой дизайн имеет хорошую масштабируемость, и мы можем положиться на эти настройки для достижения интеграции с профессиональным кешем (Ehcache).

Простая идея кэша

Что, если мы хотим реализовать простой кеш? Во-первых, нам не нужно, вообще говоря, это простое добавление, удаление, модификация, запрос и очистка всех кешированных интерфейсов и реализаций.

/**
 * 简单缓存接口
 * @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()
    }
}

Я считаю, что описанная выше простая обработка кеша должна использоваться всеми.В методе, если он будет признан пустым, он будет сохранен в методе и извлечен напрямую, если он не пуст.

А что плохого в таком кэше?

  • муфта слишком высокая
  • слишком агрессивный
  • Замена кэш-решения слишком дорогая
  • Высокие затраты на техническое обслуживание

Кеширующая абстракция Spring

Соответствующая абстракция кеша предоставляется нам в пакете Spring-context Полный путь:org.springframework.cache, давайте сначала посмотрим, что находится под этой сумкой.

  • org.springframework.cache

    • annotation
    • concurrent
    • config
    • interceptor
    • support
    • Cache.java(интерфейс кэша)
    • CacheManager.java(интерфейс CacheManager)

На этом мы можем наиболее интуитивно увидетьconcurrentа такжеsupportЭто может быть какая-то конкретная реализация кеша, ведь concurrent — это параллелизм, а support — это реализация.

существуетannotationПод этим пакетом мы видим, что естьCacheable,CacheConfig,CacheEvict,CachePut,Cachingа такжеEnableCachingЭти аннотации, они соответствуют своим собственным функциям.

Название аннотации основная функция Сфера использования использованная литература
EnableCaching Кэшировать весь bean-компонент, который можно использовать для кэширования службы Отметьте класс cacheConfig аннотацией Configuration. Аннотация Spring @EnableCaching
CacheConfig Предоставляет набор кэшированных настроек по умолчанию для всех методов отмеченного класса. Отмечен в классе, часто используется на сервисном уровне. 1️⃣ Использование аннотаций кеша Spring @Cacheable, @CachePut, @CacheEvict
Cacheable В основном на методе результат метода кэшируется Отметить метод, кэшировать результат метода; кэшировать класс, кэшировать результаты всех методов в классе 2️⃣ Использовать аннотацию кеша Spring @Cacheable, @CacheEvict, @CachePut
CachePut Кэш предоставляется, но реальный вызов запускается каждый раз, а результат кэшируется. Может синхронизировать результаты базы данных с кешем Часто используется для пометки метода и обновления результата в кеше (также можно кэшировать весь класс) 1️⃣ Использование аннотаций кеша Spring @Cacheable, @CachePut, @CacheEvict
CacheEvict Более чем достаточно, чтобы очистить кеш На нем помечен метод, а кеш может быть очищен при определенных условиях (можно также пометить класс) 1️⃣ Использование аннотаций кеша Spring @Cacheable, @CachePut, @CacheEvict
Caching Групповые аннотации для нескольких аннотаций кэша (разных или одного типа). И методы, и классы могут быть помечены Пожалуйста, проверьте официальную документацию весны

Тогда мы сможем увидеть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 предоставляет некоторые вещи, связанные с кэшем: 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 — это нечто большее, то, что мы видим сейчас, — это только угол.