Description
Marc Vanbrabant opened SPR-14781 and commented
Since upgrading from 4.1 to 4.3 we are seeing the following exception thrown in CacheAspectSupport.getCaches when a call to a @Cacheable
method occurs:
throw new IllegalStateException("No cache could be resolved for '" +
context.getOperation() + "' using resolver '" + cacheResolver +
"'. At least one cache should be provided per cache operation.");
As far as I have been able to trace, it looks like the refactoring from #18054 (59c88eb) might be the cause.
It seems to fail when the @CacheConfig
annotation is not on the Impl class of the interface, but on the interface itself.
@CacheConfig( cacheNames = "userCache")
public interface UserService
{
@Cacheable(key = "('username:' + #username).toLowerCase()")
User getUserByUsername( String username );
}
public class UserServiceImpl implements UserService {
@Override
public User getUserByUsername( String username ) {
return userRepository.findByUsername( username );
}
}
We observe the same error if the @Cachable
annotation is placed on the UserServiceImpl class:
@CacheConfig( cacheNames = "userCache")
public interface UserService
{
User getUserByUsername( String username );
}
public class UserServiceImpl implements UserService {
@Override
@Cacheable(key = "('username:' + #username).toLowerCase()")
public User getUserByUsername( String username ) {
return userRepository.findByUsername( username );
}
}
Moving down the @CacheConfig
down to the UserServiceImpl class circumvents the issue.
The difference compared to 4.1 (as far as I was manage to debug), seem to be that SpringCacheAnnotationParser.parseCacheAnnotations() now uses AnnotatedElementUtils.findAllMergedAnnotations(ae, CacheEvict.class)
In 4.1 SpringCacheAnnotationParser.parseCacheAnnotations() would return null when called with the UserServiceImpl.getUserByUsername as second argument. It would then return null all the way up to AbstractFallbackCacheOperationSource.computeCacheOperations() where it would fallback onto the following check:
if (specificMethod != method) {
// Fallback is to look at the original method.
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
// Last fallback is the class of the original method.
opDef = findCacheOperations(method.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
}
The method here being the interface method.
In 4.3 it seems that SpringCacheAnnotationParser.parseCacheAnnotations() does not return null and that the defaultConfig of the interface is not merged with parsed annotations from the Impl method.
Note:
If this is indeed a bug, the workaround is to put the @CacheConfig
on the implementing classes, or specify the cacheNames in the @Cacheable
annotations
Affects: 4.3.2
Issue Links:
- Support @Cache* as merged composed annotations [SPR-13475] #18054 Support
@Cache
* as merged composed annotations - Cache annotation lookup in 4.3 finds more annotations than in 4.2 [SPR-14801] #19367 Cache annotation lookup in 4.3 finds more annotations than in 4.2
Referenced from: commits 08972ef, 3cca57a
0 votes, 5 watchers