O'Reilly logo

Just Spring by Madhusudhan Konda

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Advanced Concepts

The Spring framework comes with lots of bells and whistles. There are at times we wish to inject Java Collections such as Maps or Lists. There are also instances we wish to alter the bean configurations for our own customizations . Configuring such collections outside of the application in a properties file or customizing the beans to our own needs is easily done in Spring. While we learned the basics in the first three chapters, we discuss the more mature and advanced concepts in this chapter.

Bean Scopes

Did you wonder how many instances will be created when the Spring’s container is loads up the bean config file during the application startup? Do we get the same instance whenever you query the container for the same bean? If we have a case where one and only one bean (such as a service or a factory) is to be created irrespective of the number of times you call the container, how is this achieved? Or for every call, how can we fetch a brand new instance? How does Spring achieve this?

Well, it turns out to be a simple config tag that dictates these types—the scope tag with values of singleton and prototype.

Singleton Scope

When you need one and only one instance of a bean (TrainFactory in this case), you should set the scope tag to singleton, as shown here:

<bean name="trainFactory" scope="singleton"
     class="com.madhusudhan.jscore.fundamentals.scope.TrainFactory">
</bean>

However, the default scope is always singleton. Hence, you can ignore the setting of scope to singleton as shown here:

<bean name="trainFactory"
     class="com.madhusudhan.jscore.fundamentals.scope.TrainFactory">
</bean>

Every time a trainFactory is injected into another bean or queried using the getBean() method, the same instance is returned.

Note that there is a subtle difference between the instance obtained using the Java Singleton pattern and Spring Singleton. Spring Singleton is a singleton per context or container, whereas the Java Singleton is per process and per class loader.

Prototype Scope

Prototype scope creates a new instance every time a call is made to fetch the bean. For example, we define a Train object that gets created every time it gets instantiated.

Because our requirement is to create a new Train everytime, the scope has to be set to prototype in our config file. Spring would then create a new Train object for every invocation.

<bean name="train" 
  scope="prototype"
  class="com.madhusudhan.jscore.fundamentals.scope.Train">
  <property name="trainName" value="London to Paris Special"/>
</bean>

As you may have observed, nothing in the class definitions would indicate the scope of the classes. There seems to be no difference in the code base, whether using singleton or prototype. The scope is managed declaratively, hence giving you the option of turning it on or off according to the requirement.

For completeness, let’s execute a test class that prints out the Train and TrainFactory’s hashCode values. The following is the test client:

public class TrainClient {
  private static ApplicationContext context = null;
  private Train train = null;
  private TrainFactory trainFactory = null;

  public void init() {
    context = new ClassPathXmlApplicationContext("fundamentals-beans.xml");
  }
  
  private void checkTrainInstance() {
    for (int i = 0; i < 10; i++) {
     train = context.getBean("train", Train.class);
            System.out.println("Train instance: " + train.getInstance());
    }
  }
  
  private void checkTrainFactoryInstance() {
    for (int i = 0; i < 10; i++) {
      trainFactory = context.getBean("trainFactory", TrainFactory.class);
      System.out.println("TrainFactory instance: " + trainFactory.getInstance());
    }
  }

  public static void main(String[] args) {
    TrainClient client = new TrainClient();
    client.init();
    client.checkTrainInstance();
    client.checkTrainFactoryInstance();
  }
}

We loop through 10 times to check the instance’s hashCode value—obviously we expect the same hashCode for singleton and different hashCode for prototype beans. When the test is executed, the output is:

Train instance: 1443639316
Train instance: 975740206
Train instance: 1080513750
...
TrainFactory instance: 2074631480
TrainFactory instance: 2074631480
TrainFactory instance: 2074631480
...

As you can see the Train instance is different everytime while the TrainFactory is the same.

There are other types of scopes such as session, request, and global-session—they are used in the Spring’s web applications.

Property Files

When defining the FileReader or FtpReader, for example, we have set the hard-coded properties in the XML file. The filename or the ftp hostname/credentials were all hard wired which is definitely not a good thing.

If we need to change the properties from one environment to another, we will have to modify this XML file. This is definitely not a good practice. Spring gives us another way of injecting these properties.

What we need to do is to define a property file with name-value pairs and let Spring’s PropertyPlaceholderConfigurer read it. This class loads up a properties file specified by a location and resolves the properties.

Let’s see how we can achieve this.

Create a JobSearchAgent class with two properties—location and type. The location attribute indicates the candidate’s preferred job location while the type attribute indicates the type of job to search (permanent, temporary, etc.).

public class JobSearchAgent {
  private String location = null;
  private String type = null;
  // Setters and Getters for these properties
  // ...
}

We want these two properties to be loaded from an external properties file—the rational being that each candidate may have an associaed agent with him/her. So, the next step is to create a property file called fundamentals-beans.properties in our resources area and add the following properties and their values:

job.location = /users/mkonda/dev/js;
job.type = permanent;

Did you notice the convention that I’m following when naming the properties file? Usually, I prefer naming the properties file exactly as the bean definitions file except the extension (.properties in the former case).

The last bit of the puzzle is the config meta data of the JobSearchAgent class.

Edit the fundamentals-beans.xml file to add the framework’s class named PropertyPlaceholderConfigurer. This class has a property called location, which should be pointing to our properties file:

<bean 
 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="classpath:fundamentals-beans.properties"/>
</bean> 

The PropertyPlaceholderConfigurer is a central class in resolving the properties. The responsibility of this PropertyPlaceholderConfigurer is to search for the property files in the classpath and load them into the application.

The last step is to parameterize the JobSearchAgent class’s properties in the config file:

<bean name="jobSearchAgent"
      class="com.madhusudhan.jscore.fundamentals.properties.JobSearchAgent">
  <property name="type" value="${job.type}"/>
  <property name="location" value="${job.location}"/>
</bean>

The ${job.type} and ${job.location} properties resolves to the name-value pairs defined in the fundamentals-beans.properties. They are replaced appropriately during the runtime of the application thus allowing us to create environment-specific properties.

The format of the property attributes follow the famous Ant style—${name}. However, Framework provides a way of customizing the prefix and suffixes.

For example, if we want to call the properties as #[name] instead of following the default ${} style, we need to set two properties placeholderPrefix and placeholderSuffix with our choice. See the following snippet for an example:

<bean 
 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="classpath:fundamentals-beans.properties"/>
  <property name="placeholderPrefix" value="#["/>
  <property name="placeholderSuffix" value="]"/>
</bean>

Of course, the bean properties should reflect these changes—see the values in bold:

<bean name="jobSearchAgent"
    class="com.madhusudhan.jscore.fundamentals.properties.JobSearchAgent">
  <property name="type" value="#[job.type]"/>
  <property name="location" value="#[job.location]"/>
</bean> 

Should you wish to read properties from multiple locations, set the locations property with the list of property files as shown here:

<bean 
 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations">
    <list>
      <value>classpath:fundamentals-beans.properties</value>
      <value>classpath:fundamentals-beans_1.properties</value>
   </list>
 </property>
</bean>

Property Editors

When we set the property values in the Spring config file, from the declaration, it sounds like the properties were just Strings or Java primitives. However, there will be instances when we need to pass in an externally initialized collection, object references, or other values. Spring follows the Java Bean style property editor mechanism to facilitate such requirements. We can inject any Java Collection such as List, Set, Map by declaring them in the config XML file. We can also provide java.util.Properties. We’ll see them in action here.

If the property is another bean reference (a dependency), the actual type of the bean is resolved when injecting the dependency.

Injecting Java Collections

Injecting an initialized collection such as a List, Set, or Map couldn’t be any easier in Spring. There is a specific syntax to follow in the config file so the collection is initialized before injecting.

Using lists, sets and maps

Lists are one of the frequent collections used heavily in Java programs. We may have instances of creating a pre-populated lists such as list of currencies, list of countries or list of week days, and so on.

The following CapitalCitiesManager class defines a variable called countriesList which holds the list of Countries as Strings. Obviously this is a static list that can be initialized outside the program in the config XML.

public class CapitalCitiesManager {
  private List<String> countriesList = null;
  
  // getters and setters
  public List<String> getCountriesList() {
    return countriesList;
  }
  public void setCountriesList(List<String> countriesList) {
    this.countriesList = countriesList;
  }
}

In order to use Lists, declare the property countryList (the property name should match to the variable name) with a list element. This list element will then hold individual value tags enclosing the values.

The following config metadata shows the usage of java.util.List in our CapitalCitiesManager class.

<bean name="capitalCitiesManager" 
 class="com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager">
  <property name="countriesList">
    <list>
      <value>United Kingdom</value>
      <value>United States Of America</value>
      <value>India</value>
      <value>Japan</value>
    </list>
  </property>
</bean>

Similarly, if you have to create list of countries by using a Set (remember, java.util.Set will not accept duplicates) values, swap the list tag shown in the above snippet with this set tag:

<bean name="capitalCitiesManager" 
 class="com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager">
  <property name="countriesSet">
  <set>
    <value>United Kingdom</value>
    <value>United Kingdom</value>
    <value>United States Of America</value>
    <value>United States Of America</value>
    <value>India</value>
    <value>Japan</value>
  </set>
</property>

Purposely, I have created the duplicate values for the above Set element. When you run the test client, you will see the Set populated with four unique countries!

Coming to Maps, as expected, we need to use the java.util.Map implementation as shown in the following code snippet:

public class CapitalCitiesManager {
  private Map<String,String> countriesCapitalsMap = null;
  
  //Getters and Setters 
  public Map<String, String> getCountriesCapitalsMap() {
    return countriesCapitalsMap;
  }
  public void setCountriesCapitalsMap(Map<String, String> countriesCapitalsMap) {
    this.countriesCapitalsMap = countriesCapitalsMap;
  }
}

However, when configuring Maps, there are certain subtle differences. The element in a map is made of an Entry node, which has name-value pairs defined as key and value attributes:

<bean name="capitalCitiesManager" 
 class="com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager">
  <property name="countriesCapitalsMap">
    <map>
      <entry key="England" value="London"/>
      <entry key="USA" value="Washington DC"/>
    </map>
  </property>
</bean>                  

Values can also refer to any other object references—for example, the entry UK has a reference to UnitedKingdomUnion bean. See the snippet here here:

<bean name="capitalCitiesManager" class=
 "com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager">
  <property name="countriesCapitalsRefMap">
    <map>
      <entry key="UK" value-ref="ukUnion"/>
    </map>
  </property>
</bean>

<bean name="ukUnion" 
  class="com.madhusudhan.jscore.fundamentals.propertyeditor.UnitedKingdomUnion"/>

Note that the ukUnion bean is referenced using the value-ref tag instead of value tag as we did in the earlier snippet.

Injecting Java Properties

The java.util.Properties object represents a name-value pair. The following code snippet defines the Properties object in our CapitalCitiesManager class:

public class CapitalCitiesManager {
  private Properties countryProperties = null;
  public Properties getCountryProperties() {
    return countryProperties;
  }
  public void setCountryProperties(Properties countryProperties) {
    this.countryProperties = countryProperties;
  }
}

The next step is wiring up the bean in the config.The countriesProperties variable should be declared as a property to the bean.

See the XML config here:

<bean name="capitalCitiesManager" 
 class="com.madhusudhan.jscore.fundamentals.propertyeditor.CapitalCitiesManager">
  <property name="countryProperties">
    <props>
      <prop key="UK">London</prop>
      <prop key="USA">Washington DC</prop>
    </props>
  </property>
</bean>

The Properties object is represented by props element as you can see above. Each of the element in the Properties has a prop attribute that will have our key and values.

Bean Post Processors

Spring allows us to poke into the bean’s life cycle by implementing bean post processors. In these post processors, we get a chance to alter the configuration, set a custom value, or implement sophisticated init processes.

The Framework provides a BeanPostProcessor interface which is used to exploit this functionality. This interface has two callback methods: postProcessBeforeInitialization and postProcessAfterInitialization. The BeanPostProcessor is set per container only—all the beans in that container will be processed by this post processor. We can have multiple post processors doing various bits if we like.

As the names indicate, the postProcessBeforeInitialization method is invoked just before calling your init-method or afterPropertiesSet method on the bean. Our bean is just about to be implemented, but we have been given a chance to do something before the final call! Similarly, the postProcessAfterInitialization method is called just after the initialization of the bean is completed.

Let’s take a simple case—any bean declared in a particular container should have a pre-set variable componentName. If the componentName is not set (is empty), then the bean should be set with a default value, say, LONDON-DEFAULT-COMP. The easiest way to achieve this requirement is to create a class that implements the BeanPostProcessor interface and modify the value of componentName variable.

First, we’ll create a processor, in this case we call it DefaultComponentNamePostProcessor:

public class DefaultComponentNamePostProcessor implements BeanPostProcessor{
  public Object postProcessBeforeInitialization(Object o, String string) 
  throws BeansException {
    System.out.println("PostProcess BEFORE init: " +o);
    if(o instanceof PostProcessComponent){
      ((PostProcessComponent)o).setComponentName("LONDON-DEFAULT-COMP");
    }
   return o;
  }

  public Object postProcessAfterInitialization(Object o, String string) 
  throws BeansException {
    System.out.println("PostProcess AFTER init: " +o);
      if(o instanceof PostProcessComponent){
        System.out.println("Default Property: 
    "+((PostProcessComponent)o).getComponentName());
      }
    return o;
  }  
}

As you can see, the DefaultComponentNamePostProcessor class implements two superclass’s methods (highlighted in bold). Any bean that’s been created in this container will be processed by this post processor. What we are doing here is setting a property componentName during the postProcessBeforeInitialization method invocation while simply printing it out during the postProcessAfterInitialization method (and returning the unmodified instance).

To complete our discussion, we need to create a simple POJO PostProcessComponent that has a variable named componentName which will not be set yet.

Here’s the code snippet of PostProcessComponent class:

public class PostProcessComponent {
  private String componentName = null;
  public String getComponentName() {
    return componentName;
  }
  public void setComponentName(String componentName) {
    this.componentName = componentName;
  }
}

The last thing we need to do is to declare the post processor and the bean in the XML config file:

<bean name="compNamePostProcessor" 
  class="com...containers.postprocessor.DefaultComponentNamePostProcessor"/>
<bean name="postProcessComponent" 
  class="com.madhusudhan.jscore.containers.postprocessor.PostProcessComponent"/>
    

We do not have to define the bean as a post processor anywhere in the config, just declare them as normal beans. The container will automatically find all the post processors and load them for further work.

When you run the example, you can see the following output:

PostProcess BEFORE init: PostProcessComponent{componentName=null}
PostProcess AFTER init: PostProcessComponent{componentName=LONDON-DEFAULT-COMP}

If you have several beans defined in the container config file with componentName not set, all of them will then be pre-proceessed by the DefaultComponentNamePostProcessor to set a default componentName.

We need to tell the framework that we’re using post processors. If the container is BeanFactory, we have to invoke addBeanPostProcessor (our processor) on the BeanFactory in order to set the post processor instance.

However, if ApplicationContext is our container, defining the post processor in the config file is adequate. The container automatically looks at the class to see if it’s a post processor and then invokes the appropriate methods during the instantiation of the bean.

Note that the BeanPostProcessors are applied per container while the bean init or destroy methods are applied per bean.

Parent-Child Bean Definitions

Spring allows us to declare abstract and implemented classes in the configuration. If AbstractKlass is an abstract class and Klass is a sub-class of it, they can be configured as shown here:

<bean name="abstractKlass" 
  class="com.madhusudhan.jscore.fundamentals.inherit.AbstractKlass" 
  abstract="true"/>
<bean name="klass" 
  class="com.madhusudhan.jscore.fundamentals.inherit.Klass" 
  parent="abstractKlass"/>

When we declare an abstract class, we need decorate it with abstract="true" as shown in bold in the above snippet. The subclass should be defined with parent attribute refering to this abstract class.

Summary

This chapter completes our journey into core Spring. It explains the fundamentals of containers and their usage. It then delves into using autowiring beans where you do not have to set properties on the beans. It then explains various ways of instantiating the beans, such as static methods or factory methods. We have also seen event handling supported by the framework.

One important aspect of Spring is its support for enterprise features such as Spring JMS and Database. The next chapter explains the simplification Spring has brought into the Java messaging world.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required