Java › Annotations
How would you design a custom annotation-driven caching mechanism (similar to Spring's @Cacheable) using a dynamic proxy combined with reflection?
AdvancedAnswer
Define a RUNTIME-retention @Cacheable annotation for methods, then use java.lang.reflect.Proxy (for interface-based classes) or a bytecode-generation library like ByteBuddy/CGLIB (for concrete classes) to create a dynamic proxy wrapping the real object -- the proxy's invocation handler checks for the @Cacheable annotation via reflection before delegating to the real method, returning a cached result from a backing Map if a matching cache key already exists, otherwise invoking the real method and storing its result, which is conceptually how Spring's caching abstraction and AOP-based cross-cutting concerns are implemented under the hood.
public class CachingInvocationHandler implements InvocationHandler {
private final Object target;
private final Map<String, Object> cache = new ConcurrentHashMap<>();
public CachingInvocationHandler(Object target) { this.target = target; }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isAnnotationPresent(Cacheable.class)) {
String key = method.getName() + Arrays.toString(args);
return cache.computeIfAbsent(key, k -> {
try { return method.invoke(target, args); }
catch (Exception e) { throw new RuntimeException(e); }
});
}
return method.invoke(target, args);
}
}Real-world example A lightweight in-house caching layer wraps a data-access interface in a dynamic proxy that reads a custom @Cacheable annotation, transparently caching results for annotated methods without the calling code being aware caching is happening at all, illustrating the same proxy-based AOP pattern Spring uses internally.