Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。它只是对缓存使用的抽象,通过它我们可以在不侵入业务代码的基础上让现有代码即刻支持缓存,具体的实现还要看去结合什么缓存,比如用redis来作缓存。
CacheManager是Spring定义的一个用来管理Cache的接口。Spring自身已经为我们提供了两种CacheManager的实现,一种是基于Java API的ConcurrentMap,另一种是基于第三方Cache实现——Ehcache。
Spring对Cache的支持也有基于注解和基于XML配置两种方式。
Spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。
@Cacheable(value={"users", "xxx"}, key="#user.name")
public User find(User user) {
return dao.find(user);
}
或者通过@EnableCaching
XML的实现
缓存由@EnableCaching注解开启
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
可以看到其中有几个属性
mode:指定AOP的模式,当值为AdviceMode.PROXY时表示使用Spring aop,当值为当值为AdviceMode.ASPECTJ时,表示使用AspectJ。
proxyTargetClass:属性值为false时,表示使用jdk代理,为true时则表示使用cglib代理
我们还可以看到该注解通过@Import注解引入CachingConfigurationSelector类
public class CachingConfigurationSelector extends AdviceModeImportSelector {
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}
private String[] getProxyImports() {
List 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);
}
}
通过spring知识可以知道由@Import引入的类会去执行selectImports方法,在这里面会选择使用什么代理。默认是spring aop,所以进入getProxyImports方法,可以看到就是将AutoProxyRegistrar和ProxyCachingConfiguration注入到IOC容器中。
首先来看AutoProxyRegistrar类
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
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;
}
}
}
}
}
进入registerAutoProxyCreatorIfNecessary()方法
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
是在IOC容器中注册一个InfrastructureAdvisorAutoProxyCreator,而该类是AbstractAutoProxyCreator的子类,也就是为我们创建代理对象的。而该类会为基础设施类型的Advisor自动创建代理对象。
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
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);
}
}
其中isEligibleAdvisorBean决定了会为哪些Advisor自动创建代理对象,所以只有role为BeanDefinition. ROLE_INFRASTRUCTURE的满足条件
在之前注入了AutoProxyRegistrar类,还注入了ProxyCachingConfiguration类
@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.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是一个配置类,它为IOC容器中注入了3个bean。我们看cacheAdvisor方法,在方法内去其中实例化了BeanFactoryCacheOperationSourceAdvisor它是一个Advisor。
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private CacheOperationSource cacheOperationSource;
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
@Nullable
protected CacheOperationSource getCacheOperationSource() {
return BeanFactoryCacheOperationSourceAdvisor.this.cacheOperationSource;
}
};
public BeanFactoryCacheOperationSourceAdvisor() {
}
public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
this.cacheOperationSource = cacheOperationSource;
}
public void setClassFilter(ClassFilter classFilter) {
this.pointcut.setClassFilter(classFilter);
}
public Pointcut getPointcut() {
return this.pointcut;
}
}
Advisor由Advice和Pointcut组成。Advice在ProxyCachingConfiguration中注入,而Pointcut则使用CacheOperationSource Pointcut。
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@Override
public boolean matches(Method method, Class> targetClass) {
if (CacheManager.class.isAssignableFrom(targetClass)) {
return false;
}
CacheOperationSource cas = getCacheOperationSource();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}
protected abstract CacheOperationSource getCacheOperationSource();
}
在getCacheOperationSource就是去获取标注了@Cacheable、@CachePut或@CacheEvict注解的方法。怎么获取到的呢?我们接下来可以看到创建好之后就会去在其中设值了cacheOperationSource(),就是通过这个方法设值进去的,而这个是调用下面的方法。在方法中创建了AnnotationCacheOperationSource。
AnnotationCacheOperationSource实现了CacheOperationSource接口,CacheOperationSource的作用是获得指定方法上的所有缓存操作。
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
@Override
@Nullable
protected Collection findCacheOperations(Method method) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}
}
通过CacheAnnotationParser找到所有的CacheOperation,CacheOperation是对缓存操作的抽象,比如它的实现类有CacheEvictOperation、CachePutOperation和CacheableOperation等。
进入解析方法parseCacheAnnotations,最终可以看到去解析方法上那些注解
private Collection 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 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;
}
我们继续看ProxyCachingConfiguration中创建的第三个类CacheInterceptor
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
public CacheInterceptor() {
}
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
CacheOperationInvoker aopAllianceInvoker = () -> {
try {
return invocation.proceed();
} catch (Throwable var2) {
throw new ThrowableWrapper(var2);
}
};
try {
return this.execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
} catch (ThrowableWrapper var5) {
throw var5.getOriginal();
}
}
}
这个就是将目标对象环绕增强的,可以看到会调用excute方法,其主要的实现在它的父类CacheAspectSupport中
public abstract class CacheAspectSupport extends AbstractCacheInvoker
implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {
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 operations = cacheOperationSource.getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, method,
new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
}
return invoker.invoke();
}
}
该方法将目标方法,目标方法的参数,目标类,目标方法上使用的缓存操作等封装到CacheOperationContexts中,并向下传递。它是CacheAspectSupport 的私有类。
private class CacheOperationContexts {
private final MultiValueMap, CacheOperationContext> contexts;
private final boolean sync;
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);
}
public Collection get(Class extends CacheOperation> operationClass) {
Collection result = this.contexts.get(operationClass);
return (result != null ? result : Collections.emptyList());
}
}
每个CacheOperationContext会封装一个CacheOperation.当调用get方法的时候,则会返回对应的CacheOperationContext列表。在excute方法里又调用了CacheAspectSupport的excute方法
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// 判断@Cacheable注解有sync属性,当sync为true时
if (contexts.isSynchronized()) {
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
//一个方法上只允许标注一个@Cacheable
if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
//如果@Cacheable的condition属性为空字符串,或者满足condition条件则执行以下方法
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
Cache cache = context.getCaches().iterator().next();
try {
//执行底层Cache的get方法,当Cache中得不到的会先执行目标方法,然后将结果保存到Cache中
return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
}
catch (Cache.ValueRetrievalException ex) {
// The invoker wraps any Throwable in a ThrowableWrapper instance so we
// can just make sure that one bubbles up the stack.
throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
}
}
else {
//执行目标方法
return invokeOperation(invoker);
}
}
// 第一次执行processCacheEvicts方法
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// 尝试从缓存中获得key对应的值
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
// 如果没有获取到就将CacheOperationContext封装到CachePutRequest中,并保存到cachePutRequests集合内
List cachePutRequests = new LinkedList<>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
//如果获得到了对应的值就将结果保存到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 {
// 如果获取不到就执行目标方法
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
}
// 解析@CachePut注解
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);
}
// 第二次执行
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
在它get方法中是会去实现Cache中拿的,我们以redis为例,最终会去连接redis,执行redis的命令
public synchronized T get(Object key, Callable valueLoader) {
ValueWrapper result = get(key);
if (result != null) {
return (T) result.get();
}
T value = valueFromLoader(key, valueLoader);
put(key, value);
return value;
}
在方法中调用了两次processCacheEvicts,前一次调用只有beforeInvocation为true时才执行清除缓存的操作,第二次调用只有beforeInvocation为false时才执行清除缓存的操作。
对于@Cacheable注解而言,首先执行findCachedItem方法,尝试从缓存中获得key对应的值,如果获取到了,就把结果保存到returnValue属性中;如果获取不到,则运行目标方法,结果保存到returnValue属性中,并将对应的CacheOperationContext封装为CachePutRequest,保存到cachePutRequests集合里(调用collectPutRequests方法)。
对于@CachePut,因为cachehit属性值恒为null,因此会运行目标方法,并将结果保存到returnValue变量中。