2

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

User1291
  • 7,664
  • 8
  • 51
  • 108
  • Why would a `Class` **object** be a Spring proxy? Do you mean *can* the class instances be proxied by Spring? You'd have to check whether they're annotated with `@Component` for example I guess. Or tie into the Spring internals to detect which classes it will scan. – Kayaman Sep 23 '20 at 11:32
  • You mean like this? https://stackoverflow.com/questions/8121551/is-it-possible-to-unproxy-a-spring-bean – Kayaman Sep 23 '20 at 11:35
  • @Kayaman Let me try again ... I want to figure out whether Spring WILL proxy the component WITHOUT creating the bean to check it, just using the class objects. The question you linked to requires an instantiated bean. – User1291 Sep 23 '20 at 11:37
  • The `Class` will never be instanceof or assignable to anything "useful". Only the beans coming out of it (or non-beans with e.g. `.newInstance()`) would respond to those. So you have access to basically the metadata only. – Kayaman Sep 23 '20 at 11:40
  • @Kayaman Are you quite sure about that? Because I thought a bit more about your comment and it sounds wrong to me. `Class::isAssignableFrom` checks, quote "if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter". So if AopUtils checks `object instanceof SpringProxy`, then its class should satisfy this check, no? – User1291 Sep 23 '20 at 11:55
  • Well that depends on if it has been proxied already or not. If it's the `Class>` from `Original.class` then it won't pass the check, if it's from `SpringCreatedProxy.class`, then it will. But since Spring creates the proxy at runtime, are you at the time where Spring has already created the proxy, and do you have **that** `Class` object? I love classloading. – Kayaman Sep 23 '20 at 11:58
  • @Kayaman As mentioned in the post, I'm in a `BeanFactoryPostProcessor`. That's specifically BEFORE the beans have been created. And as I also mentioned, I cannot instantiate the beans yet. But the classes I'm checking are from the `ConfigurableListableBeanFactory` and the relevant names contain `$$EnhancerBySpringCGLIB$$`, so yes, I'd argue I have the right `Class` objects. – User1291 Sep 23 '20 at 12:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/221937/discussion-between-user1291-and-kayaman). – User1291 Sep 23 '20 at 12:44

2 Answers2

1

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.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Please notice that I have just updated the answer with some explanatory sample code in order to prove my statement. – kriegaex Sep 24 '20 at 04:15
0

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

This task seems impossible to me since there are 100500 ways to proxy bean at runtime (bean post processor, advice, etc). Technically you can use random to decide whether to proxy some bean. You can have two instances of a given class (e.g. with different qualifiers), one proxied, other not.

Kirill Gamazkov
  • 3,277
  • 1
  • 18
  • 22