Saturday, August 17, 2013

Spring: injecting properties file values into Spring managed beans

In this post, we'll go through 3 different ways to configure (XML based) a Spring application, to inject properties file values into its managed beans.

Method 1: <util:properties> schema based configuration element

In the Spring configuration file, import the spring-util schema, then use the <util:properties> element to register a bean of type java.util.properties. This element expects both an id, and a location attribute. The former is necessary to determine from which Properties bean the value we want to inject comes from, while the latter is expected to define which properties file is to be loaded in the Properties bean.

Once a Properties bean has been defined, its values can be injected into Spring managed beans with the following annotation: @Value("#{properties_bean_id[property_name]}")

Example:

Content of the prop1.properties file:


message1=Message from prop1.properties


Spring configuration: 


<?xml version="1.0" encoding="UTF-8"?>
<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"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">

 <context:component-scan base-package="example1" />

 <util:properties id="propSource" location="classpath:/sample-properties/prop1.properties" />
</beans>

Property value injection into Spring managed bean:

@Value("#{propSource[message1]}")



Method 2: <context:property-placeholder> schema based configuration element


This time, in the Spring configuration file, import the spring-context schema, then use the <context:property-placeholder> element to register a bean of type org.springframework.beans.factory.config.PropertyPlaceholderConfigurer. Just like in the previous method, this element expects the location attribute that specifies the properties file to load.

To retrieve a property value, use the syntax as follows: ${property_name}

Example:

Content of the prop2.properties file:

message2=Message from prop2.properties

Spring configuration:


<?xml version="1.0" encoding="UTF-8"?>
<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.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

 <context:component-scan base-package="example2" />

 <context:property-placeholder location="classpath:/sample-properties/prop2.properties"/>
</beans>



Property value injection into Spring managed bean:

@Value("${message2}")


Method 3: registering bean of type PropertyPlaceholderConfigurer


In this last method, we just have to explicitly register a bean of type org.springframework.beans.factory.config.PropertyPlaceholderConfigurer. Then, we'll need to specify a location property that determines the properties file to be loaded.

The syntax used to retrieve property values is exactly the same as method 2, that is: ${property_name}

Example:

Content of prop3.properties file:

message3=Message from prop3.properties

Spring configuration:

<?xml version="1.0" encoding="UTF-8"?>
<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"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

 <context:component-scan base-package="example3" />
 
 <bean id="propSource" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="classpath:/sample-properties/prop3.properties" />
 </bean>
</beans>



Property value injection into Spring managed bean:

@Value("${message3}")

Extra example: defining multiple PropertiesPlaceholderConfigurer (PPC) beans + PPC with multiple properties files




 
 
 
  
  
 
 
 
  
   
    classpath:/sample-properties/prop2.properties
    classpath:/sample-properties/prop3.properties
   
  
 

To define a PropertyPlaceholderConfigurer bean to load multiples properties files, we use the locations property and set its value to a list of properties files.

Another thing we must pay attention to, is when we are defining multiple PropertyPlaceholderConfigurer beans within a same Spring context (in this example, we're defining 2 of them). In this case, we need to specify the ignoreUnresolvablePlaceholders property in the first PPC bean(s) with the value true. Otherwise, the application context will fail to load as Spring, at bean build time, tries to inject a value that resides in a properties file that is to be loaded in another PPC bean. 

More precisely, in this example: the Component4 class expects value injection from prop1.properties, prop2.properties and prop3.properties files. When trying to inject the values from prop2.properties and prop3.properties, the placeholders could not be resolved yet as they depends on the second PPC bean. Therefore, using the ignoreUnresolvablePlaceholders property on the first PPC bean will prevent exceptions from being thrown, and we'll just wait for another PPC bean to be able to inject these values.

Working source code

A sample project to test the different methods we've just went through is available here 

Sunday, April 7, 2013

Spring: implementing the Factory pattern

Although Spring, in itself, is already an example implementation of the Factory pattern (any Spring application's application context is just a giant Factory, right!), from time to time, we would like to implement this same pattern in our application logic just to keep our code clean and tidy. Let's see how we can do this, by a short example:

1 Defining an interface for the classes to be instantiated through the factory
Let's say we're working on a Spring application that handles document printing. For a given document, it is able to print it in either A4 or A5 format and either portrait and landscape layout. Each of these printing strategies extends a common interface :

package strategy;

import model.Document;

public interface IPrintStrategy {
 public void print(Document document);
}

Here are the concrete implementations for each printing strategy. To keep this example simple, each concrete printing strategy will only indicates what it is supposed to to:


package strategy;

import model.Document;

import org.springframework.stereotype.Component;

@Component("A4Landscape")
public class PrintA4LandscapeStrategy implements IPrintStrategy{

 @Override
 public void print(Document document) {
  System.out.println("Doing stuff to print an A4 landscape document");
 }

}


package strategy;

import model.Document;

import org.springframework.stereotype.Component;

@Component("A5Landscape")
public class PrintA5LandscapeStrategy implements IPrintStrategy{

 @Override
 public void print(Document document) {
  System.out.println("Doing stuff to print an A5 landscape document");
 }

}


package strategy;

package strategy;

import model.Document;

import org.springframework.stereotype.Component;

@Component("A4Portrait")
public class PrintA4PortraitStrategy implements IPrintStrategy{

 @Override
 public void print(Document document) {
  System.out.println("Doing stuff to print an A4 portrait document");
 }

}



package strategy;

import model.Document;

import org.springframework.stereotype.Component;

@Component("A5Portrait")
public class PrintA5PortraitStrategy implements IPrintStrategy{

 @Override
 public void print(Document document) {
  System.out.println("Doing stuff to print an A5 portrait document");
 }

}

For now, just note that each printing strategy interface is annotated as being a Spring component with a certain name. 

2 Defining the factory class interface
Now, we'll have to define an interface that is to be implemented by the factory class through which we will retrieve printing strategies:


package strategy;

public interface PrintStrategyFactory {
 
 IPrintStrategy getStrategy(String strategyName);
}

As you can see, the factory class will get a strategy name as input and will return an instance of IPrintStrategy.

3 Defining the Spring application configuration




 
 
  
  
 
 
 
 
 
 
 


In this configuration file:
  • we specify the package to scan for spring component detection (here, it's the "strategy" package)
  • we declaratively define a bean of type "ServiceLocatorFactoryBean" that will be the Factory.
  • we also define, and that's optional, name aliases, so that a same printing strategy could be retrieved either by the name specified in the @Component annotation, or by the alias.
4 Testing our example
With the following test class, we can check that everything works as expected:


import model.Document;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

import strategy.PrintStrategyFactory;


@ContextConfiguration(locations = {"classpath:/spring-config.xml"})
public class SpringFactoryPatternTest extends AbstractTestNGSpringContextTests{
 
 @Autowired
 private PrintStrategyFactory printStrategyFactory;
 
 @Test
 public void printStrategyFactoryTest(){
  Document doc = new Document();
  
  printStrategyFactory.getStrategy("DEFAULT").print(doc);
  printStrategyFactory.getStrategy("A5L").print(doc);
  printStrategyFactory.getStrategy("A5P").print(doc);
  printStrategyFactory.getStrategy("A5Portrait").print(doc);
 }
}

For instance, when we ask the factory class to give us the printing strategy answering to the name "DEFAULT", we'll end up with an instance of  "PrintA4PortraitStrategy" as "DEFAULT" is an alias for the spring component with name "A4Portrait". 

From there, I believe you got how it works. If not, feel free to checkout the source code and execute the test class, and you'll grasp the whole concept in the blink of an eye.

5 Source code
The whole source code of this example is available here (github)