Search code examples
javaspringkotlinreflectionspring-aop

How do you figure out whether a CLASS is a spring proxy?


In a nutshell

In the AopUtils, we have

    /**
     * Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
     * <p>This method additionally checks if the given object is an instance
     * of {@link SpringProxy}.
     * @param object the object to check
     * @see #isJdkDynamicProxy
     * @see #isCglibProxy
     */
    public static boolean isAopProxy(@Nullable Object object) {
        return (object instanceof SpringProxy && (Proxy.isProxyClass(object.getClass()) ||
                object.getClass().getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)));
    }

In now want to check whether a bean class is proxied without instantiating the bean (i.e. just with its class) in a BeanFactoryPostProcessor.

I thought I could just "translate" above method:

    private fun <T> isAopProxyClass(candidate: Class<T>): Boolean {
        return SpringProxy::class.java.isAssignableFrom(candidate)
            && (
            Proxy.isProxyClass(candidate)
                || candidate.name.contains(CGLIB_CLASS_SEPARATOR)
            )
    }

But this does not detect proxies because SpringProxy::class.java.isAssignableFrom(candidate) is false even for obviously proxied classes.

How do I make this work?

Full picture

I'm in a BeanFactoryPostProcessor and I need the un-proxied bean classes to access certain annotated methods by reflection.

Access happens in a lambda function that will first use the ApplicationContext to retrieve the bean for the class. The bean must not be forcibly instantiated in this BeanFactoryPostProcessor (and in fact should throw an exception if it does because some beans are session-scoped).

screenshot of misbehaving code


Solution

  • Interesting question. 😀

    The three classes highlighted in your screenshot are CGLIB proxies but not AOP proxies. Look at their class names: They are all Spring configuration classes. But that does not make them normal Spring proxies, especially not AOP proxies. For the difference between @Component and @Configuration, also with regard to proxying and self-invocation behaviour, please read my answer here.

    Consequently, a Spring @Configuration class also does not implement SpringProxy like normal Spring proxies.

    So basically your solution works just fine, no need to worry, as far as I can see.

    P.S.: I am a Java guy, not a Kotlin person. So I re-implemented your code from the screenshot in Java, so I could debug into it and reproduce your situation. But even in Kotlin I would have had to re-type everything. Please next time publish the code as copyable text, not just as an image.


    Update: If you check something like the content of

    beanClasses.stream()
      .filter(aClass -> 
        aClass.getName().contains(CGLIB_CLASS_SEPARATOR) && 
          aClass.getSuperclass().getAnnotation(Configuration.class) == null
      )
      .collect(Collectors.toList())
    

    you should see an empty collection, whereas

    beanClasses.stream()
      .filter(aClass -> 
        aClass.getName().contains(CGLIB_CLASS_SEPARATOR) && 
          aClass.getSuperclass().getAnnotation(Configuration.class) != null
      )
      .collect(Collectors.toList())
    

    should yield the same list of classes as simply

    beanClasses.stream()
      .filter(aClass -> aClass.getName().contains(CGLIB_CLASS_SEPARATOR))
      .collect(Collectors.toList())
    

    I.e. all remaining CGLIB proxies in beanClasses should in fact be configurations, not normal Spring proxies.