- 
                Notifications
    You must be signed in to change notification settings 
- Fork 38.8k
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