How to use the Spring FactoryBean?

1. Overview

There are two kinds of beans in the Spring bean container: ordinary beans and factory beans. Spring uses the former directly, whereas latter can produce objects themselves, which are managed by the framework.

And, simply put, we can build a factory bean by implementing org.springframework.beans.factory.FactoryBean interface.

2. The Basics of Factory Beans

2.1. Implement a FactoryBean

Let’s look at the FactoryBean interface first:

public interface FactoryBean {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

Let’s discuss the three methods:

  • getObject() – returns an object produced by the factory, and this is the object that will be used by Spring container
  • getObjectType() – returns the type of object that this FactoryBean produces
  • isSingleton() – denotes if the object produced by this FactoryBean is a singleton

Now, let’s implement an example FactoryBean. We’ll implement a ToolFactory which produces objects of the type Tool:

public class Tool {

    private int id;

    // standard constructors, getters and setters
}

The ToolFactory itself:

public class ToolFactory implements FactoryBean<Tool> {

    private int factoryId;
    private int toolId;

    @Override
    public Tool getObject() throws Exception {
        return new Tool(toolId);
    }

    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    // standard setters and getters
}

As we can see, the ToolFactory is a FactoryBean, which can produce Tool objects.

2.2. Use FactoryBean With XML-based Configuration

Let’s now have a look at how to use our ToolFactory.

We’ll start constructing a tool with XML-based configuration – factorybean-spring-ctx.xml:

<beans ...>

    <bean id="tool" class="com.maixuanviet.factorybean.ToolFactory">
        <property name="factoryId" value="9090"/>
        <property name="toolId" value="1"/>
    </bean>
</beans>

Next, we can test if the Tool object is injected correctly:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:factorybean-spring-ctx.xml" })
public class FactoryBeanXmlConfigTest {
    @Autowired
    private Tool tool;

    @Test
    public void testConstructWorkerByXml() {
        assertThat(tool.getId(), equalTo(1));
    }
}

The test result shows we manage to inject the tool object produced by the ToolFactory with the properties we configured in the factorybean-spring-ctx.xml.

The test result also shows that the Spring container uses the object produced by the FactoryBean instead of itself for dependency injection.

Although the Spring container uses the FactoryBean‘s getObject() method’s return value as the bean, you can also use the FactoryBean itself.

To access the FactoryBean, you just need to add a “&” before the bean name.

Let’s try getting the factory bean and its factoryId property:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:factorybean-spring-ctx.xml" })
public class FactoryBeanXmlConfigTest {

    @Resource(name = "&tool")
    private ToolFactory toolFactory;

    @Test
    public void testConstructWorkerByXml() {
        assertThat(toolFactory.getFactoryId(), equalTo(9090));
    }
}

2.3. Use FactoryBean With Java-based Configuration

Use FactoryBean with Java-based configuration is a little different with XML-based configuration, you have to call the FactoryBean‘s getObject() method explicitly.

Let’s convert the example in the previous subsection into a Java-based configuration example:

@Configuration
public class FactoryBeanAppConfig {
 
    @Bean(name = "tool")
    public ToolFactory toolFactory() {
        ToolFactory factory = new ToolFactory();
        factory.setFactoryId(7070);
        factory.setToolId(2);
        return factory;
    }

    @Bean
    public Tool tool() throws Exception {
        return toolFactory().getObject();
    }
}

Then, we test if the Tool object is injected correctly:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FactoryBeanAppConfig.class)
public class FactoryBeanJavaConfigTest {

    @Autowired
    private Tool tool;
 
    @Resource(name = "&tool")
    private ToolFactory toolFactory;

    @Test
    public void testConstructWorkerByJava() {
        assertThat(tool.getId(), equalTo(2));
        assertThat(toolFactory.getFactoryId(), equalTo(7070));
    }
}

The test result shows the similar effect as the previous XML-based configuration test.

3. Ways to Initialize

Sometimes you need to perform some operations after the FactoryBean has been set but before the getObject() method is called, like properties check.

You can achieve this by implementing the InitializingBean interface or using @PostConstruct annotation.

More details about using these two solutions have been introduced in another article: Guide To Running Logic on Startup in Spring.

4. AbstractFactoryBean

Spring provides the AbstractFactoryBean as a simple template superclass for FactoryBean implementations. With this base class, we can now more conveniently implement a factory bean which creates a singleton or a prototype object.

Let’s implement a SingleToolFactory and a NonSingleToolFactory to show how to use AbstractFactoryBean for both singleton and prototype type:

public class SingleToolFactory extends AbstractFactoryBean<Tool> {

    private int factoryId;
    private int toolId;

    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }

    @Override
    protected Tool createInstance() throws Exception {
        return new Tool(toolId);
    }

    // standard setters and getters
}

And now the nonsingleton implementation:

public class NonSingleToolFactory extends AbstractFactoryBean<Tool> {

    private int factoryId;
    private int toolId;

    public NonSingleToolFactory() {
        setSingleton(false);
    }

    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }

    @Override
    protected Tool createInstance() throws Exception {
        return new Tool(toolId);
    }

    // standard setters and getters
}

Also, the XML config for these factory beans:

<beans ...>

    <bean id="singleTool" class="com.maixuanviet.factorybean.SingleToolFactory">
        <property name="factoryId" value="3001"/>
        <property name="toolId" value="1"/>
    </bean>

    <bean id="nonSingleTool" class="com.maixuanviet.factorybean.NonSingleToolFactory">
        <property name="factoryId" value="3002"/>
        <property name="toolId" value="2"/>
    </bean>
</beans>

Now we can test if the Worker objects’ properties are injected as we expect:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:factorybean-abstract-spring-ctx.xml" })
public class AbstractFactoryBeanTest {

    @Resource(name = "singleTool")
    private Tool tool1;
 
    @Resource(name = "singleTool")
    private Tool tool2;
 
    @Resource(name = "nonSingleTool")
    private Tool tool3;
 
    @Resource(name = "nonSingleTool")
    private Tool tool4;

    @Test
    public void testSingleToolFactory() {
        assertThat(tool1.getId(), equalTo(1));
        assertTrue(tool1 == tool2);
    }

    @Test
    public void testNonSingleToolFactory() {
        assertThat(tool3.getId(), equalTo(2));
        assertThat(tool4.getId(), equalTo(2));
        assertTrue(tool3 != tool4);
    }
}

As we can see from the tests, the SingleToolFactory produces singleton object, and the NonSingleToolFactory produces prototype object.

Note that there’s no need to set singleton property in SingleToolFactory because, in AbstractFactory, singleton property’s default value is true.

5. Conclusion

Using a FactoryBean can be a good practice to encapsulate complex construction logic or make configuring highly configurable objects easier in Spring.

So in this article, we introduced the basics of how to implement our FactoryBean, how to use it in both XML-based configuration and Java-based configuration, and some other miscellaneous aspects of FactoryBean, such as initialization of FactoryBean and AbstractFactoryBean.

As always, the complete source is over on GitHub.

Related posts:

Spring Boot - Rest Template
Apache Tiles Integration with Spring MVC
Spring Data Reactive Repositories with MongoDB
Handling URL Encoded Form Data in Spring REST
Java Program to Find k Numbers Closest to Median of S, Where S is a Set of n Numbers
Hướng dẫn sử dụng Java String, StringBuffer và StringBuilder
Chương trình Java đầu tiên
Java Program to Check Cycle in a Graph using Topological Sort
Java Program to Check whether Graph is a Bipartite using DFS
Java Program to Perform Searching Based on Locality of Reference
Java IO vs NIO
HttpClient 4 – Follow Redirects for POST
Java Program to Implement the Hungarian Algorithm for Bipartite Matching
Using a List of Values in a JdbcTemplate IN Clause
Java Program to Implement Dijkstra’s Algorithm using Priority Queue
Object cloning trong java
Java – Random Long, Float, Integer and Double
Java Program to Perform Polygon Containment Test
Giới thiệu Google Guice – Aspect Oriented Programming (AOP)
So sánh Array và ArrayList trong Java
Difference Between Wait and Sleep in Java
Java Program to Implement ArrayList API
Hướng dẫn Java Design Pattern – Command
Java Program to Give an Implementation of the Traditional Chinese Postman Problem
Java Program to Implement DelayQueue API
ArrayList trong java
Guide to the Synchronized Keyword in Java
Java Program to implement Bit Set
Remove the First Element from a List
Introduction to Spring Cloud Netflix – Eureka
Java Program to Generate Date Between Given Range
Rest Web service: Filter và Interceptor với Jersey 2.x (P2)