Hi, I'm from Atlassian DC team. Our products are using Gemini Blueprint that integrates Spring into OSGi framework. I'm working on adding support for Spring 6.2 and I detected regression caused by https://github.com/spring-projects/spring-framework/issues/31304

Gemini defines proxies without target, instead it adds several mix-ins by adding custom advices implementing DelegatingIntroductionInterceptor. That interceptor registers all interfaces defined by the implementing subclass. Those interfaces are later registered in ProxyFactory. New code validates registered interfaces in ProxyFactory with interfaces from all registered IntroductionAdvisor with use of a method org.springframework.aop.framework.AdvisedSupport#hasUserSuppliedInterfaces. Effectively that method returns true only if ProxyFactory defines at least one interface that isn't defined by one of the registered IntroductionAdvisor.

In some cases the requested interfaces may be the same as defined in registered IntroductionAdvisor. Method hasUserSuppliedInterfaces will return false and ProxyFactory will follow with default pattern, resulting in an exception:

org.springframework.aop.framework.AopConfigException: TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.

The update linked above effectively changed the definition of an "user interface". It used to be any interface that is not SpringProxy. Right now it is an interface that isn't SpringProxy and isn't one of the interfaces defined by IntroductionAdvisor. It caused a regression in Gemini use case. It looks like if there is no target but there are any interfaces, ProxyFactory should still try to use JdkDynamicAopProxy. That change would maintain behvaviour existing for many years before the recent update.

Example Code:

public static void main(String[] args) {
        final ProxyFactory factory = new ProxyFactory();

        // register `IntroductionAdvisor` that delegates calls to `ImportedOsgiServiceProxy`
        factory.addAdvice(new DelegatingInterceptor());
        // request proxy to implement `ImportedOsgiServiceProxy`
        factory.addInterface(ImportedOsgiServiceProxy.class);

        // should return proxy
        // instead throws exception suggesting that either an interface or a target is required
        Object proxy = factory.getProxy();
    }

    private static class DelegatingInterceptor extends DelegatingIntroductionInterceptor implements ImportedOsgiServiceProxy {
        @Override
        public ServiceReferenceProxy getServiceReference() {
            // implementation detail
            return null;
        }
    }