Wednesday, July 3, 2019

Dependency Injection Using factory-method in Spring

In Spring framework 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 and you want to use that class with in the Spring framework to ensure that only one instance is created which is a singleton within the scope of JVM not only with in the scope of Spring container. For such scenario you need to use the factory-method attribute of the bean element in the Spring framework.


factory-method and factory-bean attribute in Spring

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- Use factory-method attribute, specify the factory method that has to be used along with the factory-method attribute.
  • Non-static (instance) factory method- Use factory-bean attribute to specify the bean where factory method resides, use factory-method to specify the method.

Let us see an example of factory-method in Spring to have better understanding.

Spring factory-method with Singleton class Example

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 Spring 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.

Spring factory-method with static method Example

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 fields and a factory class that create instance of the class. You can also pass argument to the factory method.

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());
 }
}

Spring factory-method with non-static method Example

If you have instance factory method (non-static 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.

In this case, when you have a non-static factory method, you need to use factory-bean attribute along with factory-method attribute.

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>

Now there is definition for Factory class also in the configuration 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!

>>>Return to Spring Tutorial Page


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-

  1. Run Time Injection Using Spring Expression Language(SpEL)
  2. Using c-namespace in Spring
  3. Creating a Maven project in Eclipse
  4. static in Java
  5. Difference Between throw And throws in Java Exception Handling
  6. LinkedHashMap in Java
  7. Lambda expressions in Java 8
  8. ConcurrentHashMap in Java