这里写自定义目录标题
注:禁止转载,谢谢!
1、结论
话不多说,先放几个结论:
1、使用cache,默认在类ConcurrentMapCache的ConcurrentMap中存储,存在JVM内存中,重启服务缓存丢失。
2、如果集成了redis,需要配置cacheManager,使用redis存储,重启service服务缓存不丢失。
3、使用本地缓存,默认使用ConcurrentMapCacheManager的来管理;如果使用redis,使用redisCacheManager来管理
4、基本原理:基于代理模式,在代理方法中,执行对应方法前先会判断是否添加了@cacheable,如果没有,则直接调用方法返回结果。如果有,则会执行到缓存拦截链(CacheInterceptor),进入CacheAspectSupport.execute(),执行具体的缓存逻辑。
2、三种缓存注解的意思
@Cacheable:执行 @Cacheable 标注的方法前先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,执行该方法并将方法返回值放进缓存。
@CachePut:每次方法的返回值都会更新缓存, 主要用于数据新增和更新方法
@CacheEvict:方法执行成功后会从缓存中移除相应数据。
3、看代码
伪代码如下,每个方法缓存配置了name,指定了key
@RestController
public class TestController {
@Autowired
private TestService testService ;
@GetMapping("/getName")
public String getName(Integer age) {
return testService.getName(age);
}
@GetMapping("/getAddress")
public String getAddress(String name) {
return testService.getAddress(name);
}
}
@Service
public class TestServiceImpl implements TestService {
@Cacheable(value = "name", key = "#age")
@Override
public String getName(Integer age) {
String uuid = UUID.randomUUID().toString();
System.out.println("查询名称:"+uuid);
return uuid;
}
@Cacheable(value = "address", key = "#name")
@Override
public String getAddress(String name) {
String uuid = UUID.randomUUID().toString();
System.out.println("查询地址:"+uuid);
return uuid;
}
}
controller调用service,实际调用的是service实例的代理,进入代理JdkDynamicAopProxy的invoke方法:
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
.......省略部分代码........
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
/** 获得方法的调用链,如果是添加了缓存注解的,会获得CacheInterceptor的调用链,
如果没有,chain就是空,下面判断chain为空就直接执行目标方法*/
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 直接执行
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行拦截链,返回retVal结果
retVal = invocation.proceed();
}
// 返回结果类型检查
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
retVal = invocation.proceed(); 进入ReflectiveMethodInvocation.proceed()
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
...........省略............
else {
// 进入,执行CacheInterceptor.invoke,里面再执行CacheAspectSupport.execute()
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
现在进入到CacheAspectSupport.execute(…)
@Nullable
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// 判断是否已经初始化完成
if (this.initialized) {
Class<?> targetClass = getTargetClass(target);
CacheOperationSource cacheOperationSource = getCacheOperationSource();
if (cacheOperationSource != null) {
//获取CacheOperation封装了@cacheable的注解信息
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();
}
/**
缓存逻辑的核心方法,这里查找缓存,如果不存在,就执行目标方法获取结果,并把结果缓存起来.
这里也看到了@cachable @CachePut @CacheEvict三种注解的不同处理方式
*/
@Nullable
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) {
// 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 {
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
}
// 判断@CacheEvict,是否有需要执行beforeInvocation,提前清除
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// 根据CacheableOperation去查缓存,在CacheableOperation中封装了注解@cacheable的信息的,如果配置了condition,要看是否匹配;如果配置了key,根据key去查找。(下方有解析)
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)) {
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// 执行目标方法
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
}
// 收集被@CachePut修饰的方法放到cachePutRequests中,因为@CachePut修饰的方法需要每次都更新缓存值
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// 更新缓存值,里面的doPut()方法,如果本地存储,就存在map中,如果redis,就连接存到redis中
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// 执行被@CacheEvict修饰的,需要缓存剔除的值
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
//根据注解信息,查询缓存中是否有值
@Nullable
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
for (CacheOperationContext context : contexts) {
//过滤condition条件是否成立
if (isConditionPassing(context, result)) {
//根据注解的key生成查询的key
Object key = generateKey(context, result);
//根据配置,在本地或者redis中查找缓存
Cache.ValueWrapper cached = findInCaches(context, key);
if (cached != null) {
return cached;
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
}
}
}
}
return null;
}
4、小结
以上对缓存执行原理,过程代码进行了简单说明,自己跟着跑一遍会更清晰。
另外,以上过程是在看源码时的感悟,没有另外查资料,如果不准可以指出!