Skip to content

Initialize BeanFactoryPostProcessors as lazily as possible [SPR-596] #5324

Closed
@spring-projects-issues

Description

@spring-projects-issues

Matthew Sgarlata opened SPR-596 and commented

I was going to post a question about this on the user list, but after further testing I'm convinced this is a bug and not me being stupid :) The PropertyPlaceHolderConfigurer is working fine for most of my beans, but is not working for the special "messageSource" bean which, as you know, has special meaning in the Spring framework.

Before I get started, a little background: I am using the ReloadableResourceBundleMessageSource which I have subclassed and added a boolean property called "development" to. If the value is set to true, each request to the resource bundle will go to the hard drive. If set to false, the bundle is cached in memory.

Now that we have some background, let me describe how to reproduce the error. First, I setup a simple test class called ScoreboardCommandlineLauncher that was configured like this (see attached file for source):

<bean
id="scoreboardCommandlineLauncher"
class="com.spider.scoreboard.ScoreboardCommandlineLauncher">
<property name="development">
<value>${development}</value>
</property>
</bean>

<bean
id="messageSource"
class="com.spider.scoreboard.framework.ScoreboardMessageSource"
dependency-check="none">
<property name="basenames">
<list>
<value>file:c:/eclipse/workspace/scoreboard/web/WEB-INF/ScoreboardDefaults</value>
<value>file:c:/eclipse/workspace/scoreboard/web/WEB-INF/ScoreboardImageResources</value>
<value>file:c:/eclipse/workspace/scoreboard/web/WEB-INF/ScoreboardMessageResources</value>
</list>
</property>
<property name="development">
<value>true</value>

<!-- NOTE I AM NOT USING THE PropertyPlaceholderConfigurer SO THAT MY TEST WILL RUN SUCCESSFULLY
<value>${development}</value>
-->

</property>

</bean>

I use the launcher to launch the application, and then invoke itself with all of Spring's autowiring, etc. In the Java source, you will see I just do System.out.println(getDevelopment()); which prints out "true" as expected.

Now I remove the hardcoded "true" value to use the PropertyPlaceholderConfigurer just like the scoreboardCommandlineLauncher bean so my context looks like this:

<bean
id="scoreboardCommandlineLauncher"
class="com.spider.scoreboard.ScoreboardCommandlineLauncher">
<property name="development">
<value>${development}</value>
</property>
</bean>

<bean
id="messageSource"
class="com.spider.scoreboard.framework.ScoreboardMessageSource"
dependency-check="none">
<property name="basenames">
<list>
<value>file:c:/eclipse/workspace/scoreboard/web/WEB-INF/ScoreboardDefaults</value>
<value>file:c:/eclipse/workspace/scoreboard/web/WEB-INF/ScoreboardImageResources</value>
<value>file:c:/eclipse/workspace/scoreboard/web/WEB-INF/ScoreboardMessageResources</value>
</list>
</property>
<property name="development">

<!-- NOT HARDCODING ANYMORE
<value>true</value>
-->

    <value>${development}</value>
</property>

</bean>

Now I get an exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageSource' defined in URL [file:c:/eclipse/workspace/Scoreboard/web/WEB-INF/commandlineContext.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyAccessExceptionsException: PropertyAccessExceptionsException (1 errors); nested propertyAccessExceptions are: [org.springframework.beans.TypeMismatchException: Failed to convert property value of type [java.lang.String] to required type [boolean] for property 'development'; nested exception is java.lang.IllegalArgumentException: ${development}]
PropertyAccessExceptionsException (1 errors)
org.springframework.beans.TypeMismatchException: Failed to convert property value of type [java.lang.String] to required type [boolean] for property 'development'; nested exception is java.lang.IllegalArgumentException: ${development}
java.lang.IllegalArgumentException: ${development}
at sun.beans.editors.BoolEditor.setAsText(BoolEditor.java:43)
at org.springframework.beans.BeanWrapperImpl.doTypeConversionIfNecessary(BeanWrapperImpl.java:874)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:711)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:617)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:758)
at org.springframework.beans.BeanWrapperImpl.setPropertyValues(BeanWrapperImpl.java:785)
at org.springframework.beans.BeanWrapperImpl.setPropertyValues(BeanWrapperImpl.java:774)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:784)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:601)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:258)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:193)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:240)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:163)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByName(AbstractAutowireCapableBeanFactory.java:621)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:589)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:258)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:193)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:240)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:163)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByName(AbstractAutowireCapableBeanFactory.java:621)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:589)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:258)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:193)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:240)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:163)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.addBeanToResultMap(DefaultListableBeanFactory.java:204)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:163)
at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:526)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:338)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:286)
at org.springframework.web.context.support.XmlWebApplicationContext.refresh(XmlWebApplicationContext.java:131)
at com.spider.scoreboard.configuration.ScoreboardApplicationContext.refresh(ScoreboardApplicationContext.java:71)
at com.spider.scoreboard.framework.Launcher.launch(Launcher.java:25)
at com.spider.scoreboard.ScoreboardCommandlineLauncher.main(ScoreboardCommandlineLauncher.java:31)

As you can see, the post processors are trying to be invoked... but as part of that invocation they're instantiating the special messageSource bean, but that depends on the post processors... oh boy.

FYI, below is my definition of the PropertyPlaceholderConfigurer. The home: prefix is for the location is a special Resource type I defined, but as I demonstrated earlier, the PropertyPlaceholderConfigurer is definitely able to get to that Resource, because I was able to successfully print out "true" in my example.

<bean
id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
dependency-check="none">
<property name="locations">
<list>
<value>home:conf/scoreboardconfig.properties</value>
</list>
</property>

<!-- equivalent to load-on-startup in web.xml. Need to make sure this
post processor runs before any other BeanFactoryPostProcessors -->
<property name="order">
<value>1</value>
</property>
</bean>
To make things more interesting, I'm using autowiring by name in some contexts and not in others, and I have about 3 or 4 contexts. If you need more info or more of my context definitions let me know.


Affects: 1.1.3

Attachments:

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions