@CacheEvict注解失效的经历及解决方法是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
排查@CacheEvict注解失效
我简单看了一下《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).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
通过getInterceptorsAndDynamicInterceptionAdvice获取到当前方法的拦截器,里面包含了CacheIneterceptor,说明注解被spring检测到了。
进入CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()方法内部
org.springframework.aop.framework.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) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }@CacheEvict注解失效的经历及解决方法是什么