笔者简单看了一下《Spring实战》中的demo,然后就应用到业务代码中了,本以为如此简单的事情,竟然在代码提交后的1个周,被同事发现。selectByTaskId()方法查出来的数据总是过时的。
代码如下:
@Cacheable("taskParamsCache")
List<TaskParams> selectByTaskId(Long taskId);
// ...
// ...
@CacheEvict("taskParamsCache")
int deleteByTaskId(Long taskId);
想要的效果是当程序调用selectByTaskId()方法时,把结果缓存下来,然后在调用deleteByTaskId()方法时,将缓存清空。
经过数据库数据对比之后,把问题排查的方向定位在@CacheEvict注解失效了。
下面是笔者通过源码跟踪排查问题的过程:
- 在deleteByTaskId()方法的调用出打断点,跟进代码到spring生成的代理层。
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proce