Tuesday, July 16, 2013

Spring @Bean and PropertyPlaceHolderConfigurer

I was recently stumped by what I thought was going to be a fairly straightforward implementation - Consider the following Spring Java based bean definition file (@Configuration):

package root;

...

@Configuration
@PropertySource("classpath:root/test.props")
public class SampleConfig {
 
 @Value("${test.prop}")
 private String attr;
  
 @Bean
 public SampleService sampleService() {
  return new SampleService(attr);
 }
}

Here a bean `sampleService` is being defined, this bean is initialized with an attribute that is populated using a @Value annotation using a property placeholder string ${test.prop}.

The test for this is the following:

@ContextConfiguration(classes=SampleConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ConfigTest {
 @Autowired
 private SampleService sampleService;
 @Test
 public void testConfig() {
  assertThat(sampleService.aMethod(), is("testproperty"));
 }
}


which with the current implementation of SampleConfig fails, as the place holder ${test.prop} is not resolved at all. The standard fix for this is to register a PropertySourcesPlaceholderConfigurer which is a BeanFactoryPostProcessor responsible for scanning through all the registered bean definitions and injecting in the resolved placeholders. With this change in place the @Configuration file looks like this:

@Configuration
@PropertySource("classpath:root/test.props")
public class SampleConfig { 
 @Value("${test.prop}")
 private String attr;
 
 @Bean
 public SampleService sampleService() {
  return new SampleService(attr);
 }
 
 @Bean
 public PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
 }
}

However, after adding in the Property resolver, the test still fails, this time the value returned by the sampleService is null, not even the placeholder value!

The reason for the issue it turns out, is that with @Configuration which internally uses annotations like @Autowired, @Value, and @PostConstruct, any BeanFactoryPostProcessor beans have to be declared with a static, modifier. Otherwise the containing @Configuration class is instantiated very early and the BeanPostProcessors responsible for resolving annotations like @Value, @Autowired etc, cannot act on it.

This fix is well documented in the javadoc of @Bean, further a message is also logged which provides the workaround:

WARN : org.springframework.context.annotation.ConfigurationClassEnhancer - @Bean method RootConfig.placeHolderConfigurer is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean Javadoc for complete details


So with this fix the new working configuration is the following:

@Configuration
@PropertySource("classpath:root/test.props")
public class SampleConfig { 
 @Value("${test.prop}")
 private String attr;
 
 @Bean
 public SampleService sampleService() {
  return new SampleService(attr);
 }
 
 @Bean
 public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
 }
}


References:

Monday, July 8, 2013

AbstractAnnotationConfigDispatcherServletInitializer - Long and short of it

AbstractAnnotationConfigDispatcherServletInitializer is a newly(3.2+ versions of Spring) introduced Template Method based base class that makes Spring pure java based web application Configuration(@Configuration) without using a web.xml, easier.

The use of this class can be better appreciated using an example. Prior to AbstractAnnotationConfigDispatcherServletInitializer, the way to configure a web.xml less Servlet 3 web application is by implementing a WebApplicationInitializer:

public class CustomWebAppInitializer implements WebApplicationInitializer {

 @Override
 public void onStartup(ServletContext container) {
  AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
  rootContext.register(RootConfiguration.class);
  container.addListener(new ContextLoaderListener(rootContext));
  AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
  webContext.register(MvcConfiguration.class);
  DispatcherServlet dispatcherServlet = new DispatcherServlet(webContext);
  ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", dispatcherServlet);
  dispatcher.addMapping("/");
 }
}

A little more detail of how WebApplicationInitializer works internally is described here

With the introduction of AbstractAnnotationConfigDispatcherServletInitializer, this configuration is now much simpler and far less error prone.

public class CustomWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
 @Override
 protected Class<?>[] getRootConfigClasses() {
  return new Class<?>[]{RootConfiguration.class};
 }

 @Override
 protected Class<?>[] getServletConfigClasses() {
  return new Class<?>[]{MvcConfiguration.class};
 }

 @Override
 protected String[] getServletMappings() {
  return new String[]{"/"};
 }
}


Indeed a terribly long name, but the class serves to make the initialization code much more concise and clear.