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;
}
}