Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@Configuration processing fails to handle AbstractFactoryBean.getObject() calls [SPR-15275] #19840

Closed
spring-issuemaster opened this issue Feb 21, 2017 · 5 comments

Comments

@spring-issuemaster
Copy link
Collaborator

commented Feb 21, 2017

Dave Syer opened SPR-15275 and commented

...
Caused by: java.lang.IllegalArgumentException: interface com.example.InjectApplication$Bar is not visible from class loader
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:581) ~[na:1.8.0_60]
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557) ~[na:1.8.0_60]
	at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230) ~[na:1.8.0_60]
	at java.lang.reflect.WeakCache.get(WeakCache.java:127) ~[na:1.8.0_60]
	at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419) ~[na:1.8.0_60]
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719) ~[na:1.8.0_60]
	at org.springframework.beans.factory.config.AbstractFactoryBean.getEarlySingletonInstance(AbstractFactoryBean.java:167) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	at org.springframework.beans.factory.config.AbstractFactoryBean.getObject(AbstractFactoryBean.java:148) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	at com.example.InjectApplication.another(InjectApplication.java:16) [classes/:na]
	at com.example.InjectApplication$$EnhancerBySpringCGLIB$$60c0613f.CGLIB$another$1(<generated>) ~[classes/:na]
	at com.example.InjectApplication$$EnhancerBySpringCGLIB$$60c0613f$$FastClassBySpringCGLIB$$b0b49c40.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:356) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	at com.example.InjectApplication$$EnhancerBySpringCGLIB$$60c0613f.another(<generated>) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_60]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_60]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_60]
	at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_60]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	... 18 common frames omitted

Affects: 4.3.6

Reference URL: spring-projects/spring-framework-issues#150

Referenced from: commits a48a956, 7fb0ad3

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 21, 2017

Juergen Hoeller commented

I would only expect a single ClassLoader to be involved here, so this looks rather odd... Stéphane Nicoll, could you please check which ClassLoader is being used as a bean class loader versus the proxy class loader in the FactoryBean in that repro app?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 21, 2017

Dave Syer commented

It's null (I already checked that).

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 21, 2017

Juergen Hoeller commented

Hmm, so it looks the issue is in the getObject() call arriving at a incompletely initialized AbstractFactoryBean which doesn't have the bean class loader set yet. Since you're calling getObject() on an @Bean return value there, it's even a special FactoryBean proxy exposed by configuration class processing. And this makes AbstractFactoryBean go into its "early singleton proxy" mode, which you probably don't even intend to trigger...

Does it work if you switch to an inline implementation of the plain FactoryBean interface instead of extending AbstractFactoryBean?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 21, 2017

Dave Syer commented

Yes, indeed, an inline FactoryBean (no abstract base class) works fine.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 22, 2017

Juergen Hoeller commented

The root of the problem here is AbstractFactoryBean's final getObject() method which our enhanced FactoryBean proxy for such @Bean calls fails to intercept, with CGLIB silently dispatching to the uninitialized proxy instance. This is the case with all FactoryBean classes which happen to be final or have a final getObject() method.

I've revised this for 4.3.7 now, detecting that case upfront and either falling back to an interface proxy (if the return type signature allows for it) or to the raw (but at least fully initialized) FactoryBean instance. In the latter case, there won't be any post-processing of the getObject() result and no state management either; however, for many FactoryBean implementations this doesn't make a difference since they internally manage the exposed object and keep returning the same instance from getObject() anyway.

So there are still unfortunate cases in 4.3.7 but we are handling them in a smarter fashion now and prevent calls on uninitialized instances to begin with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.