Spring Framework An Introduction Part II – Object LifeCycle, Autowiring, Internationalization(i18n)

The part one of my post covered the basics of Spring Framework namely dependency injection, bean reuse. This post intends to take a little more deep dive into understanding the inner workings of Spring framework.

The Part I tutorial clearly explains the significance of the configuration file, how it achieves dependency injection and facilitates bean reuse. Now let’s look at other aspects of Spring Framework.

Spring Object Lifecycle

Objects created using Spring framework follow their own lifecycle; details of which will be explained hereon. Consider the following spring configuration file bean-lifecycle.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
           default-init-method="init" default-destroy-method="destroy">

  	<bean id="object" class="com.spring.bean.lifecycle.BeanObject">
  	</bean>
	
	<bean id="specialobject" class="com.spring.bean.lifecycle.SpecialBeanObject" 
		init-method="initObject" destroy-method="destroyObject">
	</bean>
		
</beans>

Here is the source code for the classes mentioned in the bean-lifecycle.xml file.

package com.spring.bean.lifecycle;

public class BeanObject {

	public void init() {
		System.out.println("Initialize");
	}

	public void destroy() {
		System.out.println("Destroy");
	}
}
package com.spring.bean.lifecycle;

public class SpecialBeanObject {

	public void initObject() {
		System.out.println("Initialize Object");
	}

	public void destroyObject() {
		System.out.println("Destroy Object");
	}
}

During an object creation it might be necessary for the developer to initialize the bean properly with default values or undertaking some specific housekeeping activities. Similarly before destroying the bean, the developer might need to clean up. Spring framework facilitates this by allowing definition of default-init-method and default-destroy-method within the beans element, refer line number 8. All beans whose method names matches the specified init or destroy method will participate in this lifecycle event. Alternatively Spring framework allows the definition of init and destroy method at bean level using init-method and destroy-method attribute within bean element, refer line number . To understand this refer the test class below:

package com.spring.test;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanLifeCycleTest {

	public static void main(String[] args) {

		ConfigurableApplicationContext ctx
			= new ClassPathXmlApplicationContext (new String[] {"bean-lifecycle.xml"});
		ctx.close();
	}
}

Note the destroy methods are invoked only if context.close() method is explicitly invoked as a part of the application program.

Spring framework defines a number of interfaces which will be invoked during the object’s lifecycle. To understand this consider the configuration file spring-bean-lifecycle.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
           default-init-method="initok" default-destroy-method="cleanok">

	<bean id="bean" class="com.spring.bean.lifecycle.SpringBeanLifecycle"
		init-method="initObj" destroy-method="cleanObj">
	</bean>
	
	<bean id="sample" class="com.spring.bean.lifecycle.SampleBean"/>		
</beans>

The source code of the Java beans configured in the xml is shown below:

package com.spring.bean.lifecycle;

public class SampleBean {

}
package com.spring.bean.lifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class SpringBeanLifecycle implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,
	InitializingBean, DisposableBean, BeanPostProcessor {

	public void init() {
		System.out.println("Init bean");
	}

	public void clean() {
		System.out.println("Clean bean");
	}

	public void initObj() {
		System.out.println("Init bean Object");
	}

	public void cleanObj() {
		System.out.println("Clean bean Object");
	}

	@Override
	public void setBeanName(String name) {
		//Method implementation for BeanNameAware interface
		System.out.println("Getting bean name: " + name);
	}

	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		//Method implementation for BeanClassLoaderAware interface
		System.out.println("Getting Bean Class loader.");
	}

	@Override
	public void setBeanFactory(BeanFactory fact) throws BeansException {
		//Method implementation for BeanFactoryAware interface
		System.out.println("Getting bean factory.");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		//Method implementation for InitializingBean interface
		System.out.println("Init");
	}

	@Override
	public void destroy() throws Exception {
		//Method implementation for DisposableBean interface
		System.out.println("Destory");
	}

	@Override
	public Object postProcessAfterInitialization(Object arg0, String arg1)
			throws BeansException {
		//Method implementation for BeanPostProcessor interface
		System.out.println("After init: Bean Name " + arg1);
		return arg0;
	}

	@Override
	public Object postProcessBeforeInitialization(Object arg0, String arg1)
			throws BeansException {
		//Method implementation for BeanPostProcessor interface
		System.out.println("Before init: Bean Name " + arg1);
		return arg0;
	}
}

Refer the SpringBeanLifecycle class. It contains two methods init and clean which are inline with the default-init-method and default-destroy-method defined in the beans element. The setBeanName method is an implementation of the BeanNameAware interface. The setBeanClassLoader method is an implementation of the BeanClassLoaderAware interface. The setBeanFactory method is an implementation of the BeanFactoryAware interface. The afterPropertiesSet method is an implementation of InitializingBean interface and destory method is an implementation of DisposableBean interface. The postProcessAfterInitialization and postProcessBeforeInitialization methods are implementation of the BeanPostProcessor interface.

Here’s the test class:

package com.spring.test;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

public class SpringBeanLifeCycleTest {

	public static void main(String[] args) {

		/*Resource resource = new FileSystemResource("src/spring-bean-lifecycle.xml");
	    ConfigurableBeanFactory beanFactory = new XmlBeanFactory(resource);
	    beanFactory.addBeanPostProcessor(new SpringBeanPostProcessor());
	    Object bean = beanFactory.getBean("bean");
	    beanFactory.destroyBean("bean", bean);*/

		ConfigurableApplicationContext ctx
			= new ClassPathXmlApplicationContext (new String[] {"spring-bean-lifecycle.xml"});
		System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%");
		ctx.getBean("bean");
		System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%");
		ctx.close();
	}
}

Refer the commented code in the test class for an alternative way of retrieving a bean object.

Here’s the output of the test class run(only relevant log entries are considered):

Getting bean name: bean
Getting Bean Class loader.
Getting bean factory.
Init
Init bean Object
Before init: Bean Name sample
After init: Bean Name sample
%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%
Destory
Clean bean Object

The invocation order of the Spring bean is as below:

  1. setBeanName method of BeanNameAware interface.
  2. setBeanClassLoader method of BeanClassLoaderAware interface.
  3. setBeanFactory method of BeanFactoryAware interface.
  4. afterPropertiesSet method of InitializingBean interface.
  5. init-method of bean element in the configuration xml.
  6. postProcessBeforeInitialization method of BeanPostProcessor interface.
  7. postProcessAfterInitialization method of BeanPostProcessor interface.
  8. destroy method of DisposableBean interface.
  9. destroy-method of bean element in the configuration xml

The default-*-method attributes defined in the beans element is overridden by the bean level definitions of init-method and destroy-method. Note that the BeanPostProcessor interface implementation does not act on SpringBeanLifecycle class but monitors the SampleBean class.

Autowiring

Up to now we have adopted the approach of creating a configuration file. Defining a bean, its default value and collaborators within the configuration file. The cross references between beans were explicitly defined within the configuration. However Spring also provides an alternative where it autowires or automatically selects the appropriate reference within the configuration file. Spring Framework supports the following autowiring modes:

  1. No autowiring
  2. By Name
  3. By Type
  4. Constructor

Earlier versions of Spring framework used to support an autodetect mode which is no longer supported in Spring 3.
The first mode ‘no autowiring’ is essentially what we have done so far. Actually defined the references between the various bean definitions in the configuration file.

The next one is By Name. This autowiring type tries to match a bean in the container whose id or name matches the name of the property. If no match is found, the property remains unwired. To understand this autowiring mode better let’s use an example. Consider the following classes Car, CarModel and Engine. Their source code is below:

package com.spring.autowire;

public class Car {

	private String makerName = null;
	private String model     = null;
	private String name      = null;

	private Engine engine    = null;

	public String getMakerName() {
		return makerName;
	}

	public void setMakerName(String makerName) {
		this.makerName = makerName;
	}

	public String getModel() {
		return model;
	}

	public void setModel(String model) {
		this.model = model;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Engine getEngine() {
		return engine;
	}

	public void setEngine(Engine engine) {
		this.engine = engine;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Car ###############");
		sb.append("\n");
		sb.append("Maker name: ");
		sb.append(this.makerName);
		sb.append("\n");
		sb.append("Name: ");
		sb.append(this.name);
		sb.append("\n");
		sb.append("Model: ");
		sb.append(this.model);
		sb.append("\n");
		if (this.engine != null) {
			sb.append(this.engine.toString());
		}
		sb.append("################");
		sb.append("\n");

		return sb.toString();
	}

}
package com.spring.autowire;

public class Engine {

	private String name  = null;
	private String model = null;
	private int capacity = 0;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getModel() {
		return model;
	}
	public void setModel(String model) {
		this.model = model;
	}
	public int getCapacity() {
		return capacity;
	}
	public void setCapacity(int capacity) {
		this.capacity = capacity;
	}

	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append("Engine ***************");
		sb.append("\n");
		sb.append("Name: ");
		sb.append(this.name);
		sb.append("\n");
		sb.append("Model: ");
		sb.append(this.model);
		sb.append("\n");
		sb.append("Capacity: ");
		sb.append(this.capacity);
		sb.append("\n");
		sb.append("**********************");
		sb.append("\n");

		return sb.toString();
	}
}

Create the following configuration xml beans-autowire.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
           default-autowire="byName">

	<bean id="makerName" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="Toyota" />
	</bean>

	<bean name="name" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="Camry" />
	</bean>

	<bean name="model" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="2011" />
	</bean>

	<bean name="capacity" class="java.lang.Integer" factory-method="valueOf">
		<constructor-arg value="2500" />
	</bean>

  	<bean id="camry" class="com.spring.autowire.Car">
  		<property name="makerName" ref="makerName" />
  		<property name="name" ref="name" />
  		<property name="model" ref="model" />
  	</bean>

  	<bean id="engine" class="com.spring.autowire.Engine">
  		<property name="name" ref="name" />
  		<property name="model" ref="model" />
  		<property name="capacity" ref="capacity" />
  	</bean>
	<!-- Please also note that it is not currently possible to autowire so-called simple properties 
		such as primitives, Strings, and Classes (and arrays of such simple properties)-->
</beans>

Refer line number 8. Here within the beans element tag the attribute default-autowire is set to ‘byName’. This informs the configuration to use byName mode for autowiring. Here’s the test class to validate:

package com.spring.test;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.autowire.Car;

public class BeanAutowireTest {

	public static void main(String[] args) {

		ConfigurableApplicationContext ctx
			= new ClassPathXmlApplicationContext (new String[] {"beans-autowire.xml"});
		Car car = (Car)ctx.getBean("camry");
		System.out.println(car);
		ctx.close();
	}
}

On running the test class we observe that even though the xml did not define any explicit association between the Car’s engine property and Engine bean, due to autowiring the property has been automatically associated with the engine bean. This is because the property name within Car and bean id within the configuration xml is the same. Do note that we have explicitly wired the other primitive attributes like makerName, model etc. This is because simple attributes such as primitives, Strings and classes (and arrays of such simple properties) are not supported byName autowiring. This apparently is a feature!! An obvious limitation of byName autowiring type is that the id/name should match the property name. The developer will have to be careful in naming bean’s properties to ensure that there is no conflict. In a medium to large size project, this constraint could prove to be a serious limitation.

Moving on let’s look at autowiring byType mode. In this mode, the auto wiring is done by comparing the bean definition type and the type of the bean property. If both match, the property is autowired to the bean. Look at the following configuration xml beans-autowire-type.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
           default-autowire="byType">

	<bean id="makerName" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="Toyota" />
	</bean>

	<bean name="name" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="Camry" />
	</bean>

	<bean name="model" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="2011" />
	</bean>

	<bean name="capacity" class="java.lang.Integer" factory-method="valueOf">
		<constructor-arg value="2500" />
	</bean>

  	<bean id="camry" class="com.spring.autowire.Car">
  		<property name="makerName" ref="makerName" />
  		<property name="name" ref="name" />
  		<property name="model" ref="model" />
  	</bean>

  	<bean id="engine" class="com.spring.autowire.Engine">
  		<property name="name" ref="name" />
  		<property name="model" ref="model" />
  		<property name="capacity" ref="capacity" />
  	</bean>
	<!-- Please also note that it is not currently possible to autowire so-called simple properties 
		such as primitives, Strings, and Classes (and arrays of such simple properties)-->
</beans>

In the beans element declaration the attribute default-autowire value is changed to ‘byType’. Like byName, byType also does not support primitive or wrapper classes autowiring. For our example only wiring of the engine property of Car takes place using the byType autowiring mode. Here’s the test code for autowire byType.

package com.spring.test;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.autowire.Car;

public class BeanAutowireTypeTest {

	public static void main(String[] args) {

		ConfigurableApplicationContext ctx
			= new ClassPathXmlApplicationContext (new String[] {"beans-autowire-type.xml"});
		Car car = (Car)ctx.getBean("camry");
		System.out.println(car);
		ctx.close();
	}

}

The final autowiring type is by constructor. It is similar to byType automode but applies to constructor arguments. If the constructor argument’s type matches the bean definition type, the constructor argument is autowired to bean. Refer the source code below of CarModel class.

package com.spring.autowire;

public class CarModel {

	private Engine engine = null;

	public CarModel(Engine engine) {
		this.engine = engine;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Car Model ###############");
		sb.append("\n");
		if (this.engine != null) {
			sb.append(this.engine.toString());
		}
		sb.append("################");
		sb.append("\n");

		return sb.toString();
	}
}

The class defines a constructor with Engine as its argument. Refer to the configuration xml beans-autowire-constructor.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
           default-autowire="constructor">

	<bean id="makerName" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="Toyota" />
	</bean>

	<bean name="name" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="Camry" />
	</bean>

	<bean name="model" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="2011" />
	</bean>

	<bean name="capacity" class="java.lang.Integer" factory-method="valueOf">
		<constructor-arg value="2500" />
	</bean>

  	<bean id="camry" class="com.spring.autowire.CarModel">
  	</bean>

  	<bean id="engine" class="com.spring.autowire.Engine">
  		<property name="name" ref="name" />
  		<property name="model" ref="model" />
  		<property name="capacity" ref="capacity" />
  	
  	</bean>
</beans>

The default-autowire attribute in the xml is changed to constructor. There is a bean with id ‘camry’ and type as CarModel. Using this autowiring mode, the engine bean is associated with the CarModel constructor argument. To validate refer the test class below:

package com.spring.test;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.autowire.CarModel;

public class BeanAutowireConstructorTest {

	public static void main(String[] args) {

		ConfigurableApplicationContext ctx
			= new ClassPathXmlApplicationContext (new String[] {"beans-autowire-constructor.xml"});
		CarModel carModel = (CarModel)ctx.getBean("camry");
		System.out.println(carModel);
		ctx.close();
	}

}

Internationalization(i18n)

A common requirement of enterprise application is to support internationalization(i18n). Spring framework also supports internationalization. I have created three properties file namely text.properties, text_fr_FR.properties and text_en_GB.properties.

Contents of text.properties

firstname=Bill
lastname=Smith
greeting=Howdy

Contents of text_fr_FR.properties

firstname=Bill
lastname=Smith
greeting=bonjour

Contents of text_en_GB.properties

firstname=Bill
lastname=Smith
greeting=Hello

To achieve i18n, the following configuration xml i18n.xml is used.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 	
  	<bean id="messageSource"
		class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basename">
			<value>text</value>
		</property>
	</bean>
</beans>

To define i18n configuration we create a bean with id ‘messageSource’ and provide property named ‘basename’. The value defined for the property basename is the property file name ‘text’. The extensions file such as text_fr_FR and text_en_GB need not be mentioned. To gain access to the property file values refer the test code below:

package com.spring.test;

import java.util.Locale;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class I18NTest {

	public static void main(String[] args) {
		ConfigurableApplicationContext ctx 
			= new ClassPathXmlApplicationContext (new String[] {"i18n.xml"});
		String firstName = ctx.getMessage("firstname", new Object[0], Locale.US);
		String lastName = ctx.getMessage("lastname", new Object[0], Locale.US);
		String greeting = ctx.getMessage("greeting", new Object[0], Locale.US);
		System.out.println("Message: " + greeting + " " + firstName + " " + lastName);

		firstName = ctx.getMessage("firstname", new Object[0], Locale.UK);
		lastName = ctx.getMessage("lastname", new Object[0], Locale.UK);
		greeting = ctx.getMessage("greeting", new Object[0], Locale.UK);
		System.out.println("Message: " + greeting + " " + firstName + " " + lastName);

		firstName = ctx.getMessage("firstname", new Object[0], Locale.FRANCE);
		lastName = ctx.getMessage("lastname", new Object[0], Locale.FRANCE);
		greeting = ctx.getMessage("greeting", new Object[0], Locale.FRANCE);
		System.out.println("Message: " + greeting + " " + firstName + " " + lastName);
		
	}

}

The Spring context object provides access to the property file values using the getMessage method. The test code to retrieve locale specific values is illustrated in the test code.

That’s all for the moment.

Advertisements

3 thoughts on “Spring Framework An Introduction Part II – Object LifeCycle, Autowiring, Internationalization(i18n)

  1. Hi..
    Thanks for your nice article,I am a new bee to Spring and was trying to understand the concept of autowiring.Even though i have gone through your article i couldnt make out the difference between autowiring byName and byType.Can you please let me know what is the exact difference between these two?I havent seen any difference other than in the xml file configuration file default-autowire=”byType”/”byName” .Is any other changes are there??

  2. Hi Sonu,

    Sorry for not making the difference explicit. When you autowire byName, it is in your interest to ensure that the name of each bean definition within the config xml is unique. Basically I can have only one bean defined with the name ‘camry’. When the xml size is large, naming can become a challenge.

    In case you choose to use autowire byType, then you can define a bean with a given type only once. That means say I am creating an application which holds information of cars of all the brands. I can define bean type as ‘Car’ only once. A serious limitation.

    Generally in spite of the developer ease, it is recommended that you explicitly create bean definition and avoid auto wiring feature.

    I hope I have clearly explained the difference.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s