Be sure to perform dependency validation using the Spring container’s initialization callback mechanism

A Spring Container In The Real World

Ala JavaSpecialists’ newsletter, this post comes to you from the beautiful Kuils River region of greater Cape Town, also known as the Kuils Riviera. Here you are reminded that to some “life is not a beach”, in particular when passing by the local township where dismal poverty and a hand to mouth existance reigns.

On that note, lets dive straight into the subject matter of this post, performing the most basic validation in your Spring beans to quickly detect stupid mistakes.

Now, as a Spring user you have a number of options when it comes to configuring dependencies you wish to have injected into a given bean. You can use autowiring, which itself has further options, such as autowiring by type or name, or you can use xml configuration with explicit bean references. Regardless of what you use, it pays to use the Spring container’s initialization callback mechanism to avoid or easily detect basic mistakes that can easily eat up hours of development time. This entails validating your dependencies, at the very least in terms of whether they are null or not, in a consistent fashion in any given project.

Lets first get onto an example of such a basic mistake, in which a suspected wiring configuration error, which turned out to be a programmer error, cost precious time, and then move onto what do to, consistently, to avoid future similar wasteage.

Finally, we present two ways of detecting wiring configuration errors. The first is suboptimal and does not use Spring 3 optimally. The second approach uses Spring 3 in the recommended fashion.

BEFORE

Herewith a class under test, note the @Autowired bean propertyService.

public class ConfigServiceImpl implements ConfigService {

    @Autowired
    @Qualifier("propertyServiceBean")
    protected PropertyService propertyService;

    // NO INIT METHOD

...
}

Here is the stupid mistake.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/com/mayloom/activiti/config/spring-context.xml"})
public class ConfigServiceTest {

   @Autowired
   private ConfigService configService;

   @Test
   public void updateConfiguration() {

      ConfigService configService = new ConfigServiceImpl(); // This line must be removed

      Properties props = configService.getProperties();

The mistake is the programmer is autowiring the configService yet also instatiating it. The latter renders Autowiring of dependencies in configService as useless, the entire line must be removed. It could be that this was a class that was later turned into a Spring bean, so just an oversight.

This the the result of the mistake, or at least a snippet of the surefire report showing the offending NullPointerException.

-------------------------------------------------------------------------------
Test set: com.mayloom.config.ConfigServiceTest
-------------------------------------------------------------------------------
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.282 sec <<< FAILURE!
updateConfiguration(com.mayloom.config.ConfigServiceTest)  Time elapsed: 0.266 sec  <<< ERROR!
java.lang.NullPointerException
	at com.mayloom.config.ConfigServiceImpl.getProperties(ConfigServiceImpl.java:72)

Now, if you had been making various Spring configuration changes in your project, and you see the above, one could start thinking that a wiring configuration error is the cause and waste hours reconfiguring and basically looking in the wrong place, even though the mistake made is quite obvious to us now.

AFTER

To quickly rule out wiring errors as a cause, in this case, and in general, all one has to do is to check that wired dependencies are not null as shown in the Default initialization and destroy methods section of the Spring manual.

So, we would end up with the pertitent section of our spring configuration looking as as shown below, with the default-init-method being what we want to include.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
		   http://www.springframework.org/schema/context/spring-context-3.0.xsd" default-init-method="init">

Next, we’ll update ConfigServiceImpl as shown below.

public class ConfigServiceImpl implements ConfigService {

  @Autowired
  @Qualifier("propertyServiceBean")
  protected PropertyService propertyService;

  // this is (unsurprisingly) the initialization callback method
  public void init() {
    	log.info("==================== ConfigServiceImpl =========================");
        if (this.propertyService == null) {
            throw new IllegalStateException("The [propertyService] property must be set.");
        }
    }

Thats pretty much it, no advanced concurrency lesson here, no generics to marvel at, but it may save you time.

SUPERIOR APPROACH – UPDATE 03 Dec 2013

The techniques listed above are suboptimal in terms of how Spring is used. A better approach is to annotate bean initialization methods with @PostConstruct combined with a configuration instruction for Spring to process this annotation (add <context:annotation-config/> to your Spring application context configuration file). Then, one would use a RequiredAnnotationBeanPostProcessor and this would be done by annotation setter methods with @Required. The end result of using the @Required annotation on the setters of dependencies is that Spring will enforce @Required properties being set and this will be done before beans are made available for use, that is, the enforcement will occur during the bean post processing phase of the application context initialization lifecycle.

Advertisements