Monday, 13 June 2016

Dependency Injection using factory-method in Spring

As you all know Spring uses dependency injection to create beans and the beans are by default singleton in scope with in the Spring container. Now if you have a Singleton Java class, or a factory class with a static method how will you ensure that only one instance is created which is a singleton within the scope of JVM?

Answer is using factory-method attribute of the bean element.

There may be a case when in a factory class the method which creates an instance of the class is not static in that case you need to use factory-bean attribute along with factory-method attribute.
So we have two scenarios here -

  • Static factory method
  • Non-static (instance) factory method.

Let us see an example to have better understanding.

Example with Singleton class

Let's create a singleton class which lazily loads the class instance and configure it in Spring using factory-method attribute.

Singleton class

package spring_core;

public class Admin {
 // private constructor
 private Admin(){  
 }
 private static class AdminHolder(){
  private static final Admin INSTANCE = new Admin();
 }
 public static Admin getInstance(){
  return AdminHolder.INSTANCE;
 }
}

XML 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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    <bean id="adminBean" class="org.netjs.prog.Admin" factory-method="getInstance"/>
    
 
</beans>

You can use the following Java program to run it -

import org.netjs.prog.Admin;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {

 public static void main(String[] args) {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
  Admin bean = (Admin) context.getBean("adminBean");
                System.out.println("Values " + bean);
       
                context.close();
 }
}

Output

creating instance of class Admin
Values org.netjs.prog.Admin@343f4d3d

Just to make it clearer that factory-method will help you in creating just a single instance let us have one more example. You must be knowing that Spring uses reflection to instantiate objects and it will invoke constructor regardless of constructor's visibility. Even if there is a private constructor Spring will create an instance of the class. Now you must be wondering then what does factory method do?

If we have the same class as above and in XML configuration we define 4 beans of the same class 2 using factory-method attribute and 2 beans without it.

XML 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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    <bean id="adminBean" class="org.netjs.prog.Admin" />
    
    <bean id="adminBean1" class="org.netjs.prog.Admin" />
    
    <bean id="adminBean2" class="org.netjs.prog.Admin" factory-method="getInstance"/>
    <bean id="adminBean3" class="org.netjs.prog.Admin" factory-method="getInstance"/>
    
 
</beans>

Java Class

import org.netjs.prog.Admin;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

 public static void main(String[] args) {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
  Admin bean = (Admin) context.getBean("adminBean");
                System.out.println("Values " + bean);
                Admin bean1 = (Admin) context.getBean("adminBean1");
                System.out.println("Values " + bean1);
        
                Admin bean2 = (Admin) context.getBean("adminBean2");
                System.out.println("Values " + bean2);
        
                Admin bean3 = (Admin) context.getBean("adminBean3");
                System.out.println("Values " + bean3);
       
                context.close();

 }
}

Output

creating instance of class Admin
creating instance of class Admin
Values org.netjs.prog.Admin@343f4d3d
Values org.netjs.prog.Admin@53b32d7
Values org.netjs.prog.Admin@5442a311
Values org.netjs.prog.Admin@5442a311

If you have noticed first two bean objects are different even if it is a Singleton class and notice the other two objects which are created using factory-method attribute they are same! That's why you need to use factory-method attribute if you want to ensure that your object is singleton.

Example of Factory with static method

As mentioned it's not only with singleton class but also factory with static method where factory-method attribute can be used.
In this example we have a class with 2 arguments and a factory class that create instance of the class.

Test Bean class

public class TestBean {
 private int num;
 private String name;
 public int getNum() {
  return num;
 }
 public void setNum(int num) {
  this.num = num;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }

}

TestBean Factory class

public class TestBeanFactory {
  public static TestBean createTestBean(int num){
   TestBean testBean = new TestBean();
   testBean.setNum(num);
          return testBean;
  }
}

XML 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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    <bean id="testBean" class="org.netjs.prog.TestBeanFactory" factory-method="createTestBean">
        <constructor-arg value = "10" />
        <property name="name" value = "test" />
    </bean>

</beans>

You can run this program using the following Java class -

import org.netjs.prog.TestBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

 public static void main(String[] args) {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml"); 
          TestBean testBean = (TestBean) context.getBean("testBean");
          System.out.println("Values " + testBean);
          System.out.println("Name - " + testBean.getName());
 }
}

Example of Factory with non-static method

If you have instance factory method then you can't just use the factory-method attribute as it works with the static method.
As example if I remove the static keyword from the method in the factory class used above -

public class TestBeanFactory {
  public TestBean createTestBean(int num){
   TestBean testBean = new TestBean();
   testBean.setNum(num);
          return testBean;
  }
}

And then try to run it, it will give the following error -

Error creating bean with name 'testBean' defined in class path resource [appcontext.xml]: 
No matching factory method found: factory method 'createTestBean(String)'. 
Check that a method with the specified name and arguments exists and that it is static.

What you need to do in this case is to use factory-bean attribute also along with factory-method attribute.

TestBeanFactory class

public class TestBeanFactory {
  public TestBean createTestBean(int num){
   TestBean testBean = new TestBean();
   testBean.setNum(num);
          return testBean;
  }
}

XML 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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    
    <bean id="testBeanFactory" class="org.netjs.prog.TestBeanFactory"/>
    
    <bean id="testBean" class="org.netjs.prog.TestBean" factory-bean="testBeanFactory" factory-method="createTestBean">
        <constructor-arg value = "10" />
        <property name="name" value = "test" />
    </bean>

</beans>

Note that there is definition for Factory class also now. And both factory-bean and factory-method attribute are used.

That's all for this topic Dependency Injection using factory-method in Spring. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. What is Dependency Injection in Spring
  2. Constructor-based dependency injection in Spring
  3. How to inject prototype scoped bean in singleton bean
  4. Different bean scopes in Spring
  5. Autowiring using XML configuration in Spring

You may also like -

>>>Go to Spring tutorial page

1 comment: