Дополнительная история? Вы думали, что это роман? Да, на мой взгляд, код — это роман, а каждый инженер — автор романа, общая схема аналогична, нужно только правильно доработать его в соответствии со своими идеями.
GitHub для этого проекта: https://github.com/pc859107393/Go2SpringBoot.git
Учащиеся, заинтересованные в общении со springboot для быстрой разработки, могут добавить следующую группу пингвинов.
Познакомьтесь с кэшем 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 — это нечто большее, то, что мы видим сейчас, — это только угол.