What we have learned about how IoC container manages Objects, we are going to try out in this article.
To follow below examples, setup a Maven project with following dependencies, spring-context and spring-beans (as of writing this article 5.3.13 version is the latest version for these jars).
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.13</version>
</dependency>
<!-- Lombok plugin is for reducing Java Boilerplate code,
in below example it is used only to generate Setter and Getter methods of POJO -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
Example 1
In the first step, create a simple Java POJO class Person with couple of fields firstName and lastName.
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Person {
String firstName;
String lastName;
}
In the next step, create an XML-based configuration metadata file beans.xml under resources and provide bean definition,
i.e. bean id, fully qualified class name and its properties that we want to set to the Person object.
<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.xsd">
<bean id="tom" class="io.cscode.spring.beans.Person">
<property name="firstName" value="Tom"/>
<property name="lastName" value="Cruise"/>
</bean>
</beans>
Now, create a class App to test above bean and XML config and see how spring's
IoC container creates the Person bean Object and make it readily available to the application code.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
try {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person person = applicationContext.getBean(Person.class);
System.out.println("First Name : " +person.getFirstName() + " , Last Name : " +person.getLastName());
} catch (Exception e){
e.printStackTrace();
}
}
}
When you run above program it prints below output and as you can see we did not create Person Object, but Spring framework created it and made it available for the application code.
First Name : Tom , Last Name : Cruise
Explanation:
- In the above code, we created an ApplicationContext using ,
this class loads the XML configuration and initializes the beans defined inside it.
- Once Beans are initialized, they are available to the application to use, to access the Person object we are using , this returns the Person object created in the Spring's IoC container.
Since there is only once Bean defined with class, it just returned that.
In case if there are more than one Person objects need to be created, then we have to provide distinct ID's for them, otherwise Spring throw an
<bean id="tom" class="io.cscode.spring.beans.Person">
<property name="firstName" value="Tom"/>
<property name="lastName" value="Cruise"/>
</bean>
<bean id="chris" class="io.cscode.spring.beans.Person">
<property name="firstName" value="Chris"/>
<property name="lastName" value="Gayle"/>
</bean>
If there are more then one bean with same type in the application, then to get the Bean object, we have to pass its ID as argument to
Person person = applicationContext.getBean("tom", Person.class);
This code returns Person object from IoC container using id as tom.
Example 2
Spring IoC container manages life cycle of a Bean created and we can provides hooks to get called during Bean's life cycle phases. Below are two important hooks.
- Bean initialization - Right after bean gets created, this phase will get called.
- Bean destroy - This is the final phase of Bean's life cycle.
For both of these initialization and destroy phases, we can provides hooks so that we can take control some of the code if we want to execute some additional logic such resources initialization or cleanup etc.
To specify which methods should get invoked during Bean initialization and destroy, we need to provide them in the XML bean definition using tags init-method and destroy-method as shown in below example.
<bean id="tom" class="io.cscode.spring.beans.Person" init-method="customInit" destroy-method="customDestroy">
<property name="firstName" value="Tom"/>
<property name="lastName" value="Cruise"/>
</bean>
Modify the POJO bean class to include these additional methods and .
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Person {
String firstName;
String lastName;
public void customInit() {
System.out.println("inside custom initialization");
}
public void customDestroy() {
System.out.println("inside custom destroy");
}
}
Now, when you run below test program, you can notice that is called during Bean initialization and is called during final phase of Bean, which invoked when context is closed using when method.
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person person = applicationContext.getBean("tom", Person.class);
System.out.println("First Name : " +person.getFirstName() + " , Last Name : " +person.getLastName());
applicationContext.close();
Output:
inside custom initialization
First Name : Tom , Last Name : Cruise
inside custom destroy
Above examples source code can be found at
GitHub link for Java code
and JUnit tests can be found at
GitHub link for Unit tests code.