2

I need to dynamically assign values of cacheResolver for @Cacheable in runtime because cacheResolver has the same value for @Cacheable in every method. Hence, I use Spring AOP to dynamically assign the value but then Spring does not recognize the newly added value for cacheResolver.

Seems that AOP load @Cacheable value at the beginning.

Anyone knows how to make it work?

My AOP code:

@Aspect
@Component
@Order(1)
public class CacheableAspect {

    @Pointcut("@annotation(org.springframework.cache.annotation.Cacheable)")
    public void cacheablePointCut() {}

    @Before("cacheablePointCut()")
    public void addCacheableResolver(JoinPoint joinPoint) {
        Annotation cacheableAnnotation = getCacheableAnnotation(joinPoint);
        Object handler = Proxy.getInvocationHandler(cacheableAnnotation);
        Field f;
        try {
            f = handler.getClass().getDeclaredField("memberValues");
        } catch (NoSuchFieldException | SecurityException e) {
            throw new IllegalStateException(e);
        }
        f.setAccessible(true);
        Map<String, Object> memberValues;
        try {
            memberValues = (Map<String, Object>) f.get(handler);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
        memberValues.put("cacheResolver", "cacheableResolver");
    }

    private Annotation getCacheableAnnotation(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        return method.getAnnotation(Cacheable.class);
    }
}

My @Cacheable code in which i want cacheResolver is dynamically assigned a value:

@Cacheable(value = "test")
public int test() {
    System.out.println("xxx");
    return 10;
}
kriegaex
  • 63,017
  • 15
  • 111
  • 202
futurexv
  • 41
  • 2

1 Answers1

0

OK, so you are trying to dynamically change an annotation representation in the JVM during runtime. Not only is that ugly, but it probably does not work as you hope it would. It seems you found out that specific annotations are represented by a dynamic proxy instance during runtime, then you are successfully manipulating one of its field values. But annotations are meant to be immutable, aber depending on when e.g. Spring scans the annotations while wiring the application, your approach to modify the proxy fields later, while being a nice try, just comes too late.

How about a more canonical approach to use multiple cache managers and/or a resolver which dynamically does what you need to begin with? As much as I love AOP, it is not the answer to everyhing.

By the way, even though your aspect is kind of useless in this case, at least we can use it as an example of how to bind annotation values to advice methods parameters, i.e. you do not need to fetch the annotation from the method by reflection next time you write an aspect:

@Pointcut("@annotation(cacheable)")
public void cacheablePointCut(Cacheable cacheable) {}

@Before("cacheablePointCut(cacheable)")
public void addCacheableResolver(JoinPoint joinPoint, Cacheable cacheable) {
  Object handler = Proxy.getInvocationHandler(cacheable);
  // (...)
}
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • thanks, seems that Spring proxy scan the @Cachable before i change the value. Thanks for sharing a new way to get Cacheable – futurexv Oct 25 '21 at 11:58
  • 1
    So if you feel like I answered your question correctly, please be so kind as to accept (click the grey checkmark, making it green) and upvote it. Thank you. – kriegaex Oct 28 '21 at 12:10