-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Description
Dave Blumenfeld opened SPR-2983 and commented
When scoping a FactoryBean at any scope other than singleton or prototype (using aop:scopedProxy/), Spring attempts to access the FactoryBean's target object instance from its backing scope when creating the scoped proxy - i.e. when initialising the WebApplicationContext (assuming that the proxy is injected into any singletons - e.g. a controller or interceptor). The same problem does not occur when the scoped bean is a standard bean instance, rather than an object created by a FactoryBean.
The stack trace is:
2006-12-27 17:21:04,628|ERROR|org.springframework.web.servlet.DispatcherServlet||Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testInterceptor' defined in ServletContext resource [/WEB-INF/test-servlet.xml]: Cannot resolve reference to bean 'myBean' while setting bean property 'myBean'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myBean': Initialization of bean failed; nested exception is java.lang.NullPointerException
Caused by:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myBean': Initialization of bean failed; nested exception is java.lang.NullPointerException
Caused by:
java.lang.NullPointerException
at org.springframework.aop.scope.ScopedProxyFactoryBean.setBeanFactory(ScopedProxyFactoryBean.java:91)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1020)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:420)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:246)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:128)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:955)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:729)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:416)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:290)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:348)
at org.springframework.web.context.support.AbstractRefreshableWebApplicationContext.refresh(AbstractRefreshableWebApplicationContext.java:156)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:308)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:252)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:221)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:115)
at javax.servlet.GenericServlet.init(GenericServlet.java:211)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1105)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:932)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3915)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4176)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1012)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1012)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)
at org.apache.catalina.core.StandardService.start(StandardService.java:450)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:700)
at org.apache.catalina.startup.Catalina.start(Catalina.java:551)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:275)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
If you turn on debugging, the underlying cause is revealed to be:
2006-12-27 17:21:04,612|DEBUG|org.springframework.beans.factory.support.DefaultListableBeanFactory||Ignoring bean creation exception on FactoryBean type check
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.myBean': Scope 'request' is not active; nested exception is java.lang.IllegalStateException: No thread-bound request: use RequestContextFilter
Caused by:
java.lang.IllegalStateException: No thread-bound request: use RequestContextFilter
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:61)
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:40)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:279)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
at org.springframework.beans.factory.support.AbstractBeanFactory.getTypeForFactoryBean(AbstractBeanFactory.java:1197)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:563)
at org.springframework.beans.factory.support.AbstractBeanFactory.getType(AbstractBeanFactory.java:396)
at org.springframework.aop.scope.ScopedProxyFactoryBean.setBeanFactory(ScopedProxyFactoryBean.java:90)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1020)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:420)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:246)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:128)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:955)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:729)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:416)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:290)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:348)
at org.springframework.web.context.support.AbstractRefreshableWebApplicationContext.refresh(AbstractRefreshableWebApplicationContext.java:156)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:308)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:252)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:221)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:115)
at javax.servlet.GenericServlet.init(GenericServlet.java:211)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1105)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:932)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3915)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4176)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1012)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1012)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)
at org.apache.catalina.core.StandardService.start(StandardService.java:450)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:700)
at org.apache.catalina.startup.Catalina.start(Catalina.java:551)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:275)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
The problem is that the call to beanFactory.getType() on line 90 of ScopedProxyFactoryBean results in the bean factory trying to retrieve the FactoryBean's target object from the backing scope in order to identify its type. I'm not sure of the exact reason for this, as my FactoryBean implements the getObjectType() method, so I would have thought that a throw-away instance could be created to call this method quite easily. Perhaps this was done to improve performance or to allow FactoryBeans to dynamically alter the type of returned objects?
Whatever the underlying reason, it is surely wrong for Spring to attempt to access a request-based scope at web application start-up when no current request exists. Note that Tomcat users have a work-around available for this issue as long as they are only using the built-in request and / or session scopes (see the description of additional problems with custom scopes below), which is to lazy-load the WebApplicationContext by not loading the DispatcherServlet at startup. But this does not work on Websphere (haven't tried any other servers), even if you use the RequestContextListener or RequestContextFilter, because Websphere calls the servlet's init() method before invoking either the RequestContextListener.requestInitialized() or RequestContextFilter.doFilter() methods. The only solution I've been able to come up with for Websphere is to override the DispatcherServlet / FrameworkServlet initialisation routines to disable context initialisation until the first request is actually being processed.
The problem is even worse when using a custom scope (e.g. I have a scope which operates at the level of a Tiles ComponentContext - roughly equivalent to page scope in a JSP). This scope is not active until the view rendering phase, so even the solutions discussed above cause a failure. In order to get a FactoryBean to work with the custom scope, I have to avoid throwing any exceptions if the scope is accessed when it is not yet active, and I have to create and return a throw-away instance of the scoped FactoryBean object (without storing it in the backing scope) so that Spring can do its type check to create the proxy at context initialisation.
Sounds like a lot of work doesn't it? Surely there is some way the bean factory can handle this for us.
Affects: 2.0.1
Attachments:
- testScope.war (2.76 MB)