(1). 前言
在前面对Spring+Redis有了一个简单的案例入门,在这里将对Spring Cache部份的源码进行剖析.
(2). Spring Cache类图
CacheManager固名思义:就是对Cache进行管理,可以管理多个Cache.
Cache是对缓存的一些基本操作(添加数据到缓存/剔除缓存中的数据/清空整个缓存…)
CacheResolver在AOP时会需要根据注解,解析出Cache对象.
KeyGenerator用于缓存时自定义KEY策略,注意,与注解上的key是互斥的.
(3). Spring Cache是如何进行AOP拦截的?
Spring Cache的入口程序就在注解上:@EnableCaching.
// 相当于spring xml中的<import resource="xxxx.xml"/>
// 1. 导入相关的bean
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
}// end EnableCaching
public class CachingConfigurationSelector
extends AdviceModeImportSelector<EnableCaching> {
// ... ...
// 2. Spring会回调该方法.
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}// end
// 3. 向Spring中注册bean
private String[] getProxyImports() {
List<String> result = new ArrayList<>(3);
result.add(AutoProxyRegistrar.class.getName());
// ************************************************************
// 这是重点,向Spring中注册了一个Bean
// ************************************************************
result.add(ProxyCachingConfiguration.class.getName());
if (jsr107Present && jcacheImplPresent) {
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
}
return StringUtils.toStringArray(result);
}// end getProxyImports
// ... ...
}// end CachingConfigurationSelector
(4). ProxyCachingConfiguration的作用是什么
- ProxyCachingConfiguration是一个配置类(@Configuration).
- 创建CacheInterceptor,它属于AOP进行动态代理的核心.
- 创建BeanFactoryCacheOperationSourceAdvisor,这个属于定义要拦截的point.
@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();
// 设置point
advisor.setCacheOperationSource(cacheOperationSource());
// 切面的逻辑代码
advisor.setAdvice(cacheInterceptor());
if (this.enableCaching != null) {
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
}
return advisor;
}
// 对方法上的注解进行解析,并返回方法上的注解信息.
// Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass);
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
return new AnnotationCacheOperationSource();
}
// ******************************************************************
// CacheInterceptor类是重点,业务代码请求时,如果配置缓存注解,都会进入该类的invoke方法.
// ******************************************************************
@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;
}
}
(5). CacheInterceptor初始化做了什么?
- 创建:CacheResolver,固名思义:这个类就是读取注解上的内容(转换成:CacheOperationInvocationContext),并获得真实的Cache.
- 从Spring容器中(BeanFactory),获得CacheManager,并Hold住.
所以,你只要自己实现:CacheManager,并交给Spring即可(切记,当有多个实例的时候,要配一个@Primary),从代码上来看,它是不支持多个的.
public class CacheInterceptor
// *********************************************************************
// 1. 继承父类CacheAspectSupport
// *********************************************************************
extends CacheAspectSupport
implements MethodInterceptor, Serializable {
}// end CacheInterceptor
public abstract class CacheAspectSupport
extends AbstractCacheInvoker
implements BeanFactoryAware,
InitializingBean,
// **************************************************************
// 2. 实现了spring的org.springframework.beans.factory.SmartInitializingSingleton类
// Spring初始化完之后,会调用:SmartInitializingSingleton.afterSingletonsInstantiated
// **************************************************************
SmartInitializingSingleton {
private CacheOperationSource cacheOperationSource;
private SingletonSupplier<KeyGenerator> keyGenerator = SingletonSupplier.of(SimpleKeyGenerator::new);
private SingletonSupplier<CacheResolver> cacheResolver;
private BeanFactory beanFactory;
private boolean initialized = false;
public void afterSingletonsInstantiated() {
if (getCacheResolver() == null) {
// ... ...
// **********************************************************************
// 3. 从Spring容器中,获得:CacheManager,并Hold住.
// **********************************************************************
setCacheManager(this.beanFactory.getBean(CacheManager.class));
// ... ...
}
this.initialized = true;
}// end afterSingletonsInstantiated
public void setCacheManager(CacheManager cacheManager) {
// *********************************************************
// 4. 调用setCacheManager时,会通过:SimpleCacheResolver包裹着:CacheManager
// 所以,也能猜得到了:CacheInterceptor内部,会调用:SimpleCacheResolver驱动CacheManager了.
// *********************************************************
this.cacheResolver = SingletonSupplier.of(new SimpleCacheResolver(cacheManager));
}// end setCacheManager
}// end CacheAspectSupport
(6). Spring Cache 如何避免缓存穿透
@Cacheable(cacheNames = “users” , sync = true),当对相同的key进行操作时,会运用Java的synchronized关键字,在同一个进程里,只会允许一个线程去访问后面的业务代码.
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// 在注解上是否开启了sync=true
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,
// *************************************************************************
// 看方法签名,get方法需要一个回调函数,这个回调函数,才会真正的触发业务代码的调用.
// cache.get(Object key, Callable<T> valueLoader)
// *************************************************************************
cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker)))
);
}
catch (Cache.ValueRetrievalException ex) {
throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
}
} else {
return invokeOperation(invoker);
}
}
// ... ...
} // end execute
(7). 查看Cache.get签名
// ***************************************************
// 注意看,这里有synchronized关键字.
// ***************************************************
public synchronized <T> T get(Object key, Callable<T> valueLoader) {
ValueWrapper result = get(key);
if (result != null) {
return (T) result.get();
}
// Callable是个回调,在这里才会真正的调用:目标类上的方法.
T value = valueFromLoader(key, valueLoader);
put(key, value);
return value;
} // end
(8). 总结
Spring Cache用到了哪些设计模式?
- 组合模式(CompositeCacheManager),话说这东西没有太大的用处哈.
- 策略模式(KeyGenerator).
- 解释器模式(CacheResolver).
- 代理模式(CacheInterceptor).
总体来说,Spring Cache的源码有一点点的乱,而且,可扩展性方面做得真的是一般般.