Given a bean with @Lookup, AopProxyUtils#ultimateTargetClass can't get real target class because @Lookup proxy does not have the necessary marker interface.

Comment From: jliu666

version 5.2.15.RELEASE.jar

Comment From: snicoll

Unfortunately, that's a bit too narrowed for us to investigate. You've described what you think is a problem but not how it actually occurs. If you want to pursue this, please share a small sample that we can run ourselves. You can do so by attaching a zip or pushing the code to a separate GitHub repository.

Comment From: sbrannen

Given a bean with @Lookup, AopProxyUtils#ultimateTargetClass can't get real target class because @Lookup proxy does not have the necessary marker interface.

That's by design.

As stated in the Javadoc for AopProxyUtils#ultimateTargetClass:

Parameters: candidate the instance to check (might be an AOP proxy) Returns: the ultimate target class (or the plain class of the given object as fallback; never null)

The class created for a @Lookup override is in fact generated by CGLIB; however, it is not a Spring AOP proxy.

A @Lookup override class is created by CglibSubclassingInstantiationStrategy.CglibSubclassCreator, not by a concrete implementation of org.springframework.aop.framework.AopProxy.

Consequently, the class generated by CGLIB does not implement TargetClassAware, Advised, or SingletonTargetSource.

So that's why AopProxyUtils#ultimateTargetClass cannot find the ultimate target class for a @Lookup override class.

Comment From: sbrannen

Please note that org.springframework.util.ClassUtils.getUserClass(Class<?>) might actually be what you're looking for.

Comment From: leeychee

Recently, I got this issue too:

ClassUtils.isCglibProxy(testService) == true but AopUtils.isCglibProxy(testService) == false

I know it's caused by what you mentioned before, but I have two questions:

  1. Is it correct behavior for AopUtils.isCglibProxy? Sometimes, I need to find the target class, but for this kind of Lookup annotated class, should I always process separatedly?
  2. Could you explain more about why not implement SpringProxy?

Here is the code. Thank you, @sbrannen

package org.fivej.bug.lookup_proxy;

import org.junit.jupiter.api.Test;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
public class NotSpringProxyWithLookupTest {

    @Configuration
    @ComponentScan
    public static class Config {
    }

    @Component
    public static class TestService {
        @Lookup
        public LookupService lookupService() {
            return null;
        }
    }

    @Component
    @Scope("prototype")
    public static class LookupService {
    }

    @Autowired
    TestService testService;

    @Test
    void testServiceIsProxy() {
        // FAILED
        assertThat(AopUtils.isCglibProxy(testService)).isTrue();
    }

    @Test
    void testServiceIsCglibProxy() {
        // PASSED
        assertThat(ClassUtils.isCglibProxy(testService)).isTrue();
    }
}