I wish there was out-of-the-box support for @Cacheable on feign clients.
Here's how I made it work:
import feign.Capability;
import feign.InvocationHandlerFactory;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.Target;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.stereotype.Component;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.Map;
import static java.util.stream.Collectors.toMap;
/**
* Allows Spring's @Cache* annotations to be declared on the feign client's methods
*/
@Component
public class CachingCapability implements Capability {
private final CacheInterceptor cacheInterceptor;
public CachingCapability(CacheInterceptor cacheInterceptor) {
this.cacheInterceptor = cacheInterceptor;
}
@Override
public InvocationHandlerFactory enrich(InvocationHandlerFactory invocationHandlerFactory) {
return (target, dispatch) -> invocationHandlerFactory.create(target, withCacheProxy(target, dispatch));
}
private Map<Method, MethodHandler> withCacheProxy(Target<?> target, Map<Method, MethodHandler> dispatch) {
return dispatch.entrySet().stream()
.map(e -> Map.entry(e.getKey(), (MethodHandler) argv -> cacheInterceptor.invoke(new MethodInvocation() {
@Override
public Method getMethod() {
return e.getKey();
}
@Override
public Object[] getArguments() {
return argv;
}
@Override
public Object proceed() throws Throwable {
return e.getValue().invoke(argv);
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return e.getKey();
}
}))).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
}
}
Also see: https://github.com/OpenFeign/feign/issues/1513