一图甜、概述
從Spring3.1版本開(kāi)始,Spring框架就支持顯式地將緩存添加到現(xiàn)有的Spring應(yīng)用程序中鳖眼。與事務(wù)支持類(lèi)似黑毅,緩存抽象允許一致地使用各種緩存解決方案,而對(duì)代碼的侵入最小具帮。 Spring緩存的實(shí)現(xiàn)在spring-context包,如果是基于springboot基礎(chǔ)框架編程,在spring-boot-autoconfige 中有很多默認(rèn)的配置和定義,能更大程度上讓用戶(hù)無(wú)感知開(kāi)啟cache能力,如果不需要三方套件提供的緩存能力,就不需要引入額外的依賴(lài)博肋。本篇的分析是基于springboot2.1.3版本。 緩存使用原理大致如下圖:二蜂厅、使用方式
Spring cache提供了開(kāi)箱即用的接入方式,只需要若干注解和緩存管理類(lèi)即可接入.
1.開(kāi)啟緩存能力
引入緩存依賴(lài):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
在應(yīng)用啟動(dòng)類(lèi)添加@EnableCaching注解:
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.使用緩存
在業(yè)務(wù)方法添加@Cacheable注解:
@Cacheable(cacheNames = {"task"})
public TaskInfoDTO getTask(String taskId) {
log.info("TestBuzz.getTask mock query from DB......");
TaskInfoDTO taskInfoDTO = new TaskInfoDTO();
taskInfoDTO.setTaskId(taskId);
taskInfoDTO.setApplicantId("system");
taskInfoDTO.setDescription("test");
return taskInfoDTO;
}
模擬請(qǐng)求:
@GetMapping("/test_cache")
public IResp<TaskInfoDTO> testCache() {
TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123");
return IResp.getSuccessResult(taskInfoDTO);
}
連續(xù)發(fā)送兩次查詢(xún)請(qǐng)求:
curl http://localhost:port/test_cache
從日志中看到只打印了一次DB調(diào)用,也就是說(shuō)明第二次走了緩存匪凡。就這么簡(jiǎn)單我們就開(kāi)啟并使用了spring的緩存能力。
三掘猿、源碼&原理解析
我們主要從spring-context和spring-boot-autoconfige兩個(gè)包的cache模塊分析cache原理病游。 開(kāi)啟緩存時(shí)序圖:1.開(kāi)啟緩存
@EnableCaching
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
//是否直接代理目標(biāo)類(lèi)
boolean proxyTargetClass() default false;
//通知模式,默認(rèn)是代理
AdviceMode mode() default AdviceMode.PROXY;
//順序
int order() default Ordered.LOWEST_PRECEDENCE;
}
這個(gè)注解和@EnableAsync注解特別像,說(shuō)明都是基于Aop和代理做了能力增強(qiáng),該類(lèi)導(dǎo)入了CachingConfigurationSelector類(lèi),我們看一下其實(shí)現(xiàn):
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
private static final String PROXY_JCACHE_CONFIGURATION_CLASS =
"org.springframework.cache.jcache.config.ProxyJCacheConfiguration";
private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.cache.aspectj.AspectJCachingConfiguration";
private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.cache.aspectj.AspectJJCacheConfiguration";
private static final boolean jsr107Present;
private static final boolean jcacheImplPresent;
static {
ClassLoader classLoader = CachingConfigurationSelector.class.getClassLoader();
jsr107Present = ClassUtils.isPresent("javax.cache.Cache", classLoader);
jcacheImplPresent = ClassUtils.isPresent(PROXY_JCACHE_CONFIGURATION_CLASS, classLoader);
}
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}
private String[] getProxyImports() {
List<String> result = new ArrayList<>(3);
result.add(AutoProxyRegistrar.class.getName());
result.add(ProxyCachingConfiguration.class.getName());
if (jsr107Present && jcacheImplPresent) {
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
}
return StringUtils.toStringArray(result);
}
private String[] getAspectJImports() {
List<String> result = new ArrayList<>(2);
result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
if (jsr107Present && jcacheImplPresent) {
result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
}
return StringUtils.toStringArray(result);
}
}
有個(gè)概念需要解釋一下,jsr107是關(guān)于java關(guān)于緩存的規(guī)范提案,使用的話(huà)需要引入javax.cache相關(guān)包,出場(chǎng)率不高。CachingConfigurationSelector類(lèi)的核心是selectImports方法,根據(jù)@EnableCaching配置的模式選擇不同的配置類(lèi)型,默認(rèn)是PROXY模式,導(dǎo)入AutoProxyRegistrar和ProxyCachingConfiguration兩個(gè)配置稠通。
2.緩存通知配置
父類(lèi)AbstractCachingConfiguration實(shí)現(xiàn):
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableCaching = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableCaching.class.getName(), false));
if (this.enableCaching == null) {
throw new IllegalArgumentException(
"@EnableCaching is not present on importing class " + importMetadata.getClassName());
}
}
@Autowired(required = false)
void setConfigurers(Collection<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException(configurers.size() + " implementations of " +
"CachingConfigurer were found when only 1 was expected. " +
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
CachingConfigurer configurer = configurers.iterator().next();
useCachingConfigurer(configurer);
}
protected void useCachingConfigurer(CachingConfigurer config) {
this.cacheManager = config::cacheManager;
this.cacheResolver = config::cacheResolver;
this.keyGenerator = config::keyGenerator;
this.errorHandler = config::errorHandler;
}
}
做了兩件事,首先把注解元數(shù)據(jù)屬性解析出來(lái),然后把用戶(hù)自定義的緩存組件裝配進(jìn)來(lái)(CacheManager,KeyGenerator和異常處理器)衬衬。 在看ProxyCachingConfiguration配置實(shí)現(xiàn):
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@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;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
return new AnnotationCacheOperationSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor() {
CacheInterceptor interceptor = new CacheInterceptor();
interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
interceptor.setCacheOperationSource(cacheOperationSource());
return interceptor;
}
}
ProxyCachingConfiguration復(fù)用了父類(lèi)的能力并且定了AOP的三個(gè)核心組件(Pointcut,Advice和Advisor),先看AnnotationCacheOperationSource(此時(shí)還不能被稱(chēng)作Pointcut): AnnotationCacheOperationSource繼承AbstractFallbackCacheOperationSource類(lèi)實(shí)現(xiàn)CacheOperationSource接口,實(shí)現(xiàn)getCacheOperations方法將目標(biāo)方法上緩存注解解析成緩存操作集合,AbstractFallbackCacheOperationSource.getCacheOperations:
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
else {
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
}
this.attributeCache.put(cacheKey, cacheOps);
}
else {
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return cacheOps;
}
}
cacheKey是由method和class構(gòu)造成的MethodClassKey,如果緩存中有緩存操作集合直接返回,否則調(diào)用computeCacheOperations計(jì)算:
private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
}
// Second try is the caching operation on the target class.
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
// Last fallback is the class of the original method.
opDef = findCacheOperations(method.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
}
return null;
}
該方法是從目標(biāo)類(lèi)和目標(biāo)方法上(優(yōu)先方法維度)解析緩存注解裝配成緩存操作(@Cacheable -> CacheableOperation),看子類(lèi) AnnotationCacheOperationSource實(shí)現(xiàn):
public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
}
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
}
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Method method) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}
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 = annOps;
}
else {
Collection<CacheOperation> combined = new ArrayList<>(ops.size() + annOps.size());
combined.addAll(ops);
combined.addAll(annOps);
ops = combined;
}
}
}
return ops;
}
AnnotationCacheOperationSource默認(rèn)構(gòu)造器使用的是SpringCacheAnnotationParser解析器,解析操作最終委托給SpringCacheAnnotationParser.parseCacheAnnotations,將注解分別解析成對(duì)應(yīng)的操作:
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
if (ops != null && ops.size() > 1) {
// 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) {
Collection<? extends Annotation> anns = (localOnly ?
AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
if (anns.isEmpty()) {
return null;
}
final Collection<CacheOperation> ops = new ArrayList<>(1);
anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
anns.stream().filter(ann -> ann instanceof CachePut).forEach(
ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
anns.stream().filter(ann -> ann instanceof Caching).forEach(
ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
return ops;
}
private CacheableOperation parseCacheableAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
CacheableOperation.Builder builder = new CacheableOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cacheable.cacheNames());
builder.setCondition(cacheable.condition());
builder.setUnless(cacheable.unless());
builder.setKey(cacheable.key());
builder.setKeyGenerator(cacheable.keyGenerator());
builder.setCacheManager(cacheable.cacheManager());
builder.setCacheResolver(cacheable.cacheResolver());
builder.setSync(cacheable.sync());
defaultConfig.applyDefault(builder);
CacheableOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
private CacheEvictOperation parseEvictAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cacheEvict.cacheNames());
builder.setCondition(cacheEvict.condition());
builder.setKey(cacheEvict.key());
builder.setKeyGenerator(cacheEvict.keyGenerator());
builder.setCacheManager(cacheEvict.cacheManager());
builder.setCacheResolver(cacheEvict.cacheResolver());
builder.setCacheWide(cacheEvict.allEntries());
builder.setBeforeInvocation(cacheEvict.beforeInvocation());
defaultConfig.applyDefault(builder);
CacheEvictOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
private CacheOperation parsePutAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
CachePutOperation.Builder builder = new CachePutOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cachePut.cacheNames());
builder.setCondition(cachePut.condition());
builder.setUnless(cachePut.unless());
builder.setKey(cachePut.key());
builder.setKeyGenerator(cachePut.keyGenerator());
builder.setCacheManager(cachePut.cacheManager());
builder.setCacheResolver(cachePut.cacheResolver());
defaultConfig.applyDefault(builder);
CachePutOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
private void parseCachingAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection<CacheOperation> ops) {
Cacheable[] cacheables = caching.cacheable();
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
}
CacheEvict[] cacheEvicts = caching.evict();
for (CacheEvict cacheEvict : cacheEvicts) {
ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
}
CachePut[] cachePuts = caching.put();
for (CachePut cachePut : cachePuts) {
ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
}
}
然后看通知CacheInterceptor: CacheInterceptor實(shí)現(xiàn)Advice接口并繼承了CacheAspectSupport,看一些實(shí)現(xiàn):
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
CacheOperationInvoker aopAllianceInvoker = () -> {
try {
return invocation.proceed();
}
catch (Throwable ex) {
throw new CacheOperationInvoker.ThrowableWrapper(ex);
}
};
try {
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
throw th.getOriginal();
}
}
}
當(dāng)攔截到調(diào)用時(shí),將調(diào)用封裝成CacheOperationInvoker并交給父類(lèi)執(zhí)行,父類(lèi)CacheAspectSupport實(shí)現(xiàn)了SmartInitializingSingleton接口,在單例初始化后容器會(huì)調(diào)用afterSingletonsInstantiated方法:
public void afterSingletonsInstantiated() {
if (getCacheResolver() == null) {
// Lazily initialize cache resolver via default cache manager...
Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect");
try {
setCacheManager(this.beanFactory.getBean(CacheManager.class));
}
catch (NoUniqueBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
"CacheManager found. Mark one as primary or declare a specific CacheManager to use.");
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. " +
"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
}
}
this.initialized = true;
}
檢查有沒(méi)有合適的CacheManager,并且將initialized設(shè)置為true,繼續(xù)看CacheAspectSupport.execute:
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
if (this.initialized) {
Class<?> targetClass = getTargetClass(target);
CacheOperationSource cacheOperationSource = getCacheOperationSource();
if (cacheOperationSource != null) {
Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, method,
new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
}
return invoker.invoke();
}
使用AnnotationCacheOperationSource目標(biāo)類(lèi)和方法上的緩存注解解析成操作集合,然后構(gòu)造CacheOperationContexts上下文并調(diào)用重載方法:
public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
Object[] args, Object target, Class<?> targetClass) {
this.contexts = new LinkedMultiValueMap<>(operations.size());
for (CacheOperation op : operations) {
//
this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
}
//
this.sync = determineSyncFlag(method);
}
將每個(gè)操作包裝對(duì)應(yīng)上下文映射關(guān)系,并檢查是否是同步操作(@Cacheable獨(dú)有屬性),繼續(xù)看execute:
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// Special handling of synchronized invocation
if (contexts.isSynchronized()) {
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
Cache cache = context.getCaches().iterator().next();
try {
return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
}
catch (Cache.ValueRetrievalException ex) {
throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
}
}
else {
return invokeOperation(invoker);
}
}
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new LinkedList<>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
if (cacheHit != null && !hasCachePut(contexts)) {
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// Invoke the method if we don't have a cache hit
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
}
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
該方法是緩存操作的核心邏輯,首先檢查是否是同步操作(@Cacheable特性),如果是且滿(mǎn)足條件調(diào)用緩存獲取邏輯并返回,否則返回業(yè)務(wù)邏輯免緩存調(diào)用invokeOperation,然后執(zhí)行@CacheEvict的前置清除(beforeInvocation=true),接著檢查@Cacheable是否命中緩存,如果沒(méi)有命中則放入需要執(zhí)行CachePutRequest列表暫存,然后檢查是否緩存命中且沒(méi)有需要更新的緩存,如果滿(mǎn)足則返回結(jié)果使用緩存結(jié)果,否則使用業(yè)務(wù)查詢(xún)結(jié)果作為返回結(jié)果,并且填充需要緩存的結(jié)果,然后收集@CachePut操作,把@CachePut和@Cacheable未命中的請(qǐng)求同步到緩存,最后清理@CacheEvict的緩存(beforeInvocation=false)。
3.緩存代理裝配
前邊講述了緩存配置和工作流程,那么上述的Aop配置什么時(shí)候生效?在哪里生效?如何生效改橘?接下來(lái)我們將從AutoProxyRegistrar作為切入點(diǎn)展開(kāi)分析緩存代理的裝配邏輯.
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
private final Log logger = LogFactory.getLog(getClass());
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
if (candidate == null) {
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
}
}
AutoProxyRegistrar實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口,registerBeanDefinitions會(huì)從啟用緩存注解@EnableCaching提取屬性,然后手動(dòng)注冊(cè)自動(dòng)代理創(chuàng)建器:
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
手動(dòng)注冊(cè)了InfrastructureAdvisorAutoProxyCreator到容器中,看一下InfrastructureAdvisorAutoProxyCreator繼承關(guān)系: public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
@Nullable
private ConfigurableListableBeanFactory beanFactory;
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
this.beanFactory = beanFactory;
}
@Override
protected boolean isEligibleAdvisorBean(String beanName) {
return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
InfrastructureAdvisorAutoProxyCreator繼承了AbstractAdvisorAutoProxyCreator類(lèi)實(shí)現(xiàn)了BeanFactory初始化和isEligibleAdvisorBean方法,繼續(xù)看父類(lèi)AbstractAdvisorAutoProxyCreator實(shí)現(xiàn):
public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
@Nullable
private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
}
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
protected boolean isEligibleAdvisorBean(String beanName) {
return true;
}
private class BeanFactoryAdvisorRetrievalHelperAdapter extends BeanFactoryAdvisorRetrievalHelper {
public BeanFactoryAdvisorRetrievalHelperAdapter(ConfigurableListableBeanFactory beanFactory) {
super(beanFactory);
}
@Override
protected boolean isEligibleBean(String beanName) {
return AbstractAdvisorAutoProxyCreator.this.isEligibleAdvisorBean(beanName);
}
}
}
AbstractAdvisorAutoProxyCreator定義了Advisor操作的工具方法,并且定義了Advisor提取適配器BeanFactoryAdvisorRetrievalHelperAdapter,委托給子類(lèi)isEligibleAdvisorBean方法實(shí)現(xiàn)(InfrastructureAdvisorAutoProxyCreator)滋尉。 重點(diǎn)在于AbstractAdvisorAutoProxyCreator父類(lèi)AbstractAutoProxyCreator實(shí)現(xiàn)的postProcessBeforeInstantiation方法,該方法在InstantiationAwareBeanPostProcessor接口定義,該方法在bean創(chuàng)建之前調(diào)用,如果該方法返回非null對(duì)象,那么bean的創(chuàng)建過(guò)程將會(huì)短路,此處的作用是為滿(mǎn)足BeanFactoryCacheOperationSourceAdvisor增強(qiáng)器切入邏輯的類(lèi)織入增強(qiáng)邏輯,也就是緩存能力,看一下方法實(shí)現(xiàn):
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
此處的邏輯和AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法很相似,都是攔截bean創(chuàng)建過(guò)程并織入增強(qiáng)邏輯,這里是自動(dòng)生成代理類(lèi)并且將緩存邏輯織入進(jìn)去。這里也是往上說(shuō)的自動(dòng)代理實(shí)現(xiàn)APC的核心邏輯飞主。該方法前半段是從緩存中獲取目標(biāo)類(lèi)是否被代理過(guò),如果被代理過(guò)直接把增強(qiáng)邏輯織入,避免重復(fù)創(chuàng)建代理,后半段就是生成代理的邏輯,創(chuàng)建代理過(guò)程我們之前分析過(guò),此處不再分析,重點(diǎn)分析一下從候選增強(qiáng)器中獲取增強(qiáng)邏輯的方法getAdvicesAndAdvisorsForBean:
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
該方法在子類(lèi)AbstractAdvisorAutoProxyCreator中實(shí)現(xiàn),接著調(diào)用了findEligibleAdvisors方法:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
先通過(guò)前邊定義的BeanFactoryAdvisorRetrievalHelper獲取候選增強(qiáng)器,然后調(diào)用findAdvisorsThatCanApply方法篩選出對(duì)當(dāng)前代理類(lèi)適用的增強(qiáng)器:
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
該方法將篩選邏輯委托為Aop工具類(lèi)的findAdvisorsThatCanApply方法處理:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
從ProxyCachingConfiguration中增強(qiáng)器的定義來(lái)看,BeanFactoryCacheOperationSourceAdvisor是PointcutAdvisor類(lèi)型,方法前半段IntroductionAdvisor邏輯跳過(guò),通過(guò)canApply檢查是否符合條件,如果符合則加入返回列表:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
直接進(jìn)入第二個(gè)條件分支,檢查PointcutAdvisor是否符合切入邏輯:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
這個(gè)方法也不復(fù)雜,其實(shí)就是檢查目標(biāo)類(lèi)和方法上是否有緩存相關(guān)注解(@Cacheable,@CachePut,@CacheEvict等)狮惜,如果有說(shuō)明增強(qiáng)器對(duì)目標(biāo)代理類(lèi)適用,然后找到合適的增強(qiáng)器列表在APC中調(diào)用createProxy創(chuàng)建代理:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
這里創(chuàng)建代理工廠,然后選擇是否需要直接代理目標(biāo)類(lèi),然后裝配增強(qiáng)器,然后調(diào)用JdkDynamicAopProxy或者CglibAopProxy創(chuàng)建代理。
4.緩存配置
在本篇第二節(jié)使用方式介紹的時(shí)候,我們引入了緩存依賴(lài)開(kāi)啟緩存能力就能直接使用緩存了,并沒(méi)有引入或者配的 其他的緩存組件,那么問(wèn)題就來(lái)了,為什么直接就能使用緩存了,如果應(yīng)用架構(gòu)基于Spring而不是Springboot,那么肯定是要自己配置CacheResolver或者CacheManager的,為什么這里不需要,這一切還是要?dú)w功于spring-boot-autoconfigure,我們使用Springboot作為基礎(chǔ)框架時(shí),一般都會(huì)顯式或者間接把其引入碌识。spring-boot-autoconfigure有個(gè)包叫cache,毫無(wú)以為這里就是springboot定義并自動(dòng)開(kāi)啟緩存配置的地方,該包下基本都是*Configuration類(lèi)型的類(lèi),也就是Springboot自帶的緩存相關(guān)配置,我們簡(jiǎn)單分析一下CacheAutoConfiguration碾篡、CacheConfigurations、GenericCacheConfiguration筏餐、NoOpCacheConfiguration开泽、SimpleCacheConfiguration、CaffeineCacheConfiguration和RedisCacheConfiguration這幾個(gè)配置類(lèi)魁瞪。 CacheConfigurations
final class CacheConfigurations {
private static final Map<CacheType, Class<?>> MAPPINGS;
static {
Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
MAPPINGS = Collections.unmodifiableMap(mappings);
}
public static String getConfigurationClass(CacheType cacheType) {
Class<?> configurationClass = MAPPINGS.get(cacheType);
Assert.state(configurationClass != null, () -> "Unknown cache type " + cacheType);
return configurationClass.getName();
}
public static CacheType getType(String configurationClassName) {
for (Map.Entry<CacheType, Class<?>> entry : MAPPINGS.entrySet()) {
if (entry.getValue().getName().equals(configurationClassName)) {
return entry.getKey();
}
}
throw new IllegalStateException(
"Unknown configuration class " + configurationClassName);
}
}
CacheConfigurations是一個(gè)緩存配置緩存,聽(tīng)起來(lái)比較拗口,這里定義了Springboot支持的緩存類(lèi)型,一共10種,基本覆蓋了所有流行的緩存類(lèi)型,主要mappings也放入了NoOpCacheConfiguration無(wú)緩存配置,當(dāng)不啟用緩存時(shí)使用該配置,可以理解為一種特殊的緩存穆律。 CacheAutoConfiguration
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
ObjectProvider<CacheManagerCustomizer<?>> customizers) {
return new CacheManagerCustomizers(
customizers.orderedStream().collect(Collectors.toList()));
}
@Bean
public CacheManagerValidator cacheAutoConfigurationValidator(
CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
return new CacheManagerValidator(cacheProperties, cacheManager);
}
@Configuration
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
protected static class CacheManagerJpaDependencyConfiguration
extends EntityManagerFactoryDependsOnPostProcessor {
public CacheManagerJpaDependencyConfiguration() {
super("cacheManager");
}
}
}
這個(gè)類(lèi)也是Springboot默認(rèn)緩存配置的入口,類(lèi)名上有很多注解,限制了改配置的啟動(dòng)條件和裝配規(guī)則等,@ConditionalOnClass(CacheManager.class)限制應(yīng)用類(lèi)路徑中必須有CacheManager實(shí)現(xiàn),@ConditionalOnBean(CacheAspectSupport.class)限制應(yīng)用容器中必須有CacheAspectSupport或者子類(lèi)實(shí)例,@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")限制應(yīng)用容器中不能有類(lèi)型為CacheManager切名稱(chēng)為cacheResolver的bean,如果用戶(hù)自定義了那么該配置就失效,@EnableConfigurationProperties(CacheProperties.class)是表示啟用緩存屬性配置,@AutoConfigureAfter限制該類(lèi)在CouchbaseAutoConfiguration、HazelcastAutoConfiguration佩番、HibernateJpaAutoConfiguration和RedisAutoConfiguration之后配置,@Import(CacheConfigurationImportSelector.class)引入了內(nèi)部定義的CacheConfigurationImportSelector配置;先看CacheConfigurationImportSelector:
/**
* {@link ImportSelector} to add {@link CacheType} configuration classes.
*/
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
該類(lèi)會(huì)導(dǎo)入CacheType中定義的所有支持的緩存類(lèi)型配置,CacheAutoConfiguration中還定義了幾個(gè)bean,CacheManagerCustomizers是CacheManager容器,CacheManagerValidator在調(diào)用時(shí)檢查CacheManager是否存在并給出自定義異常,CacheManagerJpaDependencyConfiguration是對(duì)CacheManager依賴(lài)Jpa相關(guān)屬性的定義和后置處理众旗。 接著我們對(duì)前邊所說(shuō)的幾種常用的緩存配置做下分析: NoOpCacheConfiguration
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class NoOpCacheConfiguration {
@Bean
public NoOpCacheManager cacheManager() {
return new NoOpCacheManager();
}
}
NoOpCacheConfiguration定義了NoOpCacheManager,實(shí)現(xiàn)CacheManager接口直接調(diào)用業(yè)務(wù)邏輯提取數(shù)據(jù),其實(shí)是假緩存。 SimpleCacheConfiguration
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {
private final CacheProperties cacheProperties;
private final CacheManagerCustomizers customizerInvoker;
SimpleCacheConfiguration(CacheProperties cacheProperties,
CacheManagerCustomizers customizerInvoker) {
this.cacheProperties = cacheProperties;
this.customizerInvoker = customizerInvoker;
}
@Bean
public ConcurrentMapCacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return this.customizerInvoker.customize(cacheManager);
}
}
SimpleCacheConfiguration定義了使用ConcurrentMap作為本地緩存的CacheManager趟畏。CaffeineCacheConfiguration也類(lèi)似,定義使用Caffeine作為本地緩存的bean贡歧。 GenericCacheConfiguration
@Configuration
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class GenericCacheConfiguration {
private final CacheManagerCustomizers customizers;
GenericCacheConfiguration(CacheManagerCustomizers customizers) {
this.customizers = customizers;
}
@Bean
public SimpleCacheManager cacheManager(Collection<Cache> caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return this.customizers.customize(cacheManager);
}
}
GenericCacheConfiguration注入了類(lèi)型為SimpleCacheManager的bean,其繼承AbstractCacheManager使用ConcurrentMap做本地緩存。 RedisCacheConfiguration
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
private final CacheProperties cacheProperties;
private final CacheManagerCustomizers customizerInvoker;
private final org.springframework.data.redis.cache.RedisCacheConfiguration redisCacheConfiguration;
RedisCacheConfiguration(CacheProperties cacheProperties,
CacheManagerCustomizers customizerInvoker,
ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration) {
this.cacheProperties = cacheProperties;
this.customizerInvoker = customizerInvoker;
this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable();
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
return this.customizerInvoker.customize(builder.build());
}
private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
ClassLoader classLoader) {
if (this.redisCacheConfiguration != null) {
return this.redisCacheConfiguration;
}
Redis redisProperties = this.cacheProperties.getRedis();
org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
.defaultCacheConfig();
config = config.serializeValuesWith(SerializationPair
.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
RedisCacheConfiguration注入了RedisCacheManager類(lèi)型的bean,該配置生效有幾個(gè)條件:
- 只有應(yīng)用引入了redis依賴(lài)并且定義了RedisConnectionFactory
- 沒(méi)有定義其他類(lèi)型的CacheManager
- spring.cache.type屬性為redis
- 在RedisAutoConfiguration之后配置
redis類(lèi)型的緩存配置稍微復(fù)雜,既然依賴(lài)了RedisAutoConfiguration配置,我們也簡(jiǎn)單看一下:
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisAutoConfiguration依賴(lài)redis,并且導(dǎo)入了LettuceConnectionConfiguration和JedisConnectionConfiguration連接配置(此處不展開(kāi)分析),定義了RedisTemplate和StringRedisTemplate兩個(gè)bean供RedisCacheManager使用赋秀。
5.默認(rèn)配置
前邊我們把緩存的原理積分都分析完了,那么在前邊的使用方式介紹中使用的是哪一種緩存呢利朵?也就是說(shuō)不做任何配置Springboot啟用的是哪一種類(lèi)型的緩存?回過(guò)頭來(lái)我們?cè)倏匆幌翪acheAutoConfiguration導(dǎo)入的配置CacheConfigurationImportSelector:
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
這里是從枚舉類(lèi)CacheType獲取支持的緩存類(lèi)型然后從緩存配置緩存CacheConfigurations中獲取對(duì)應(yīng)的配置并順序?qū)?看一下支持的緩存類(lèi)型:
public enum CacheType {
/**
* Generic caching using 'Cache' beans from the context.
*/
GENERIC,
/**
* JCache (JSR-107) backed caching.
*/
JCACHE,
/**
* EhCache backed caching.
*/
EHCACHE,
/**
* Hazelcast backed caching.
*/
HAZELCAST,
/**
* Infinispan backed caching.
*/
INFINISPAN,
/**
* Couchbase backed caching.
*/
COUCHBASE,
/**
* Redis backed caching.
*/
REDIS,
/**
* Caffeine backed caching.
*/
CAFFEINE,
/**
* Simple in-memory caching.
*/
SIMPLE,
/**
* No caching.
*/
NONE
}
緩存配置導(dǎo)入有三個(gè)條件:
- 如果依賴(lài)三方包,需要導(dǎo)入并定義相關(guān)bean,比如Caffeine和Redis
- 如果用戶(hù)自定義spring.cache.type,滿(mǎn)足該條件的優(yōu)先
- 自然順序?qū)?/li>
綜合我們的使用方式和緩存配置導(dǎo)入條件,很顯然導(dǎo)入的是GenericCacheConfiguration配置,注入的CacheManager類(lèi)型是SimpleCacheManager,使用的是ConcurrentMap類(lèi)型的本地緩存,至于怎么去驗(yàn)證本篇不再展開(kāi)介紹,感興趣的可以自己驗(yàn)證猎莲。
四绍弟、總結(jié)
Spring的緩存套件極大地方便了應(yīng)用引入緩存編程,只需要在啟動(dòng)類(lèi)開(kāi)啟緩存并定義相關(guān)組件能力即可使用,而Springboot對(duì)緩存的進(jìn)一步封裝和配置更是簡(jiǎn)化了我們的接入步驟和配置,只需要定義緩存開(kāi)啟類(lèi)型和三方組件的連接就可以使用緩存。 用戶(hù)如果有自定義訴求,只需要自己實(shí)現(xiàn)CacheManager即可,但是也會(huì)失去Springboot幫我們定義的能力,這一點(diǎn)需要具體場(chǎng)景具體評(píng)估著洼。 互聯(lián)網(wǎng)發(fā)展到今天,特別是電商場(chǎng)景,對(duì)于緩存的需求和要求更高,有些業(yè)務(wù)場(chǎng)景需要同時(shí)使用本地緩存和遠(yuǎn)程緩存(redis)來(lái)應(yīng)對(duì)突發(fā)流量和熱點(diǎn)數(shù)據(jù)問(wèn)題,也就是二級(jí)或者多級(jí)緩存,Springboot并沒(méi)有對(duì)于該場(chǎng)景給出比較好的支持,也算是缺憾之一吧,如果有類(lèi)似訴求可以基于Springboot的緩存套件基于其緩存配置規(guī)則做自定義擴(kuò)展和實(shí)現(xiàn)樟遣。