Spring FactoryBean with Example

In Spring Framework, the FactoryBean interface is used to define a custom factory for creating and configuring the bean instances. This is useful when we need to create beans that cannot be created using ‘new‘ keyword and involve complex initialization or configuration.

1. What is a FactoryBean in Spring?

In Spring, typically a bean is created using one of its constructors using the ‘new‘ keyword. It is a challenging task to create and inject beans that need to be created using other methods, such as static factory methods, although this is not always the case. To overcome this problem, Spring provides the FactoryBean interface that acts as an adapter for objects that cannot be created and managed using the standard Spring semantics

Conceptually, a FactoryBean is a bean that acts as a factory for other beans. When Spring IoC container uses the FactoryBean to satisfy a dependency or lookup request, it does not return a FactoryBean instance, rather it invokes the FactoryBean.getObject() method and and returns the result of that invocation.

2. How to Create a FactoryBean?

To create a FactoryBean, all we have to do is implement the FactoryBean interface by the bean class which will create the actual other beans.

In this example, we’ll create a simple MyBeanFactory that generates instances of a MyBean class.

import org.springframework.beans.factory.FactoryBean;

public class MyBeanFactory implements FactoryBean<MyBean> {

    @Override
    public MyBean getObject() throws Exception {
        // Your custom logic to create and configure the bean
        MyBean myBean = new MyBean();
        myBean.setMessage("Hello, FactoryBean!");
        return myBean;
    }

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

    @Override
    public boolean isSingleton() {
        return true; // Return 'true' if you want a singleton bean, 'false' for a prototype bean
    }
}

Or to keep it simple, we can extend the AbstractFactoryBean class.

By extending the AbstractFactoryBean class, your factory bean can simply override the createInstance() method to create the target bean instance. In addition, you have to return the target bean’s type in the getObjectType() method for the auto-wiring feature to work properly.

import org.springframework.beans.factory.config.AbstractFactoryBean;

public class MyBeanFactory extends AbstractFactoryBean<MyBean> {

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

    @Override
    protected MyBean createInstance() throws Exception {
        // Your custom logic to create and configure the bean
        MyBean myBean = new MyBean();
        myBean.setMessage("Hello, AbstractFactoryBean!");
        return myBean;
    }
}

3. Why do we use a FactoryBean?

In Spring framework, factory beans are mostly used to implement the framework facilities. Here are some examples:

  • When looking up an object (such as a DataSource) from JNDI, we can use JndiObjectFactoryBean.
  • When using classic Spring AOP to create a proxy for a bean, we can use ProxyFactoryBean.
  • When creating a Hibernate SessionFactory in the IoC container, we can use LocalSessionFactoryBean.

In most cases, we rarely have to write any custom factory beans, because they are framework-specific and are not of much use outside the scope of the Spring IoC container.

4. Spring FactoryBean Example

In this example, we are creating a FactoryBean to instantiate MessageDigest objects. In Java, the MessageDigest class provides cryptographic features. It is an abstract class that cannot be instantiated with the new keyword. We must invoke the MessageDigest.getInstance() method and pass in the name of the digest algorithm we want to use.

MessageDigest md5 = MessageDigest.getInstance("MD5");

To create a bean of type MessageDigest which is managed by Spring application context, we need to use a FactoryBean. The following is a sample implementation. In runtime, Spring calls the getObject() method to retrieve the object and uses the FactoryBean as a collaborator.

The MessageDigestFactoryBean passes a clone of the stored MessageDigest instance that is created in the InitializingBean.afterPropertiesSet() callback.

import org.springframework.beans.factory.FactoryBean;
import java.security.MessageDigest;

public class MessageDigestFactoryBean 
	implements FactoryBean<MessageDigest>, InitializingBean {

  private String algorithmName = "MD5";
  private MessageDigest messageDigest = null;

  @Override
  public MessageDigest getObject() throws Exception {
    return messageDigest;
  }
  @Override 
  public Class<MessageDigest> getObjectType() {
    return MessageDigest.class;
  }
  @Override 
  public boolean isSingleton() {
      return true;
  }
  @Override 
  public void afterPropertiesSet() throws Exception {
    messageDigest = MessageDigest.getInstance(algorithmName);
  }
  public void setAlgorithmName(String algorithmName) {
    this.algorithmName = algorithmName;
  }
}

To use the MessageDigest, we create a utility bean MessageDigester that utilizes it.

import java.security.MessageDigest;

public class MessageDigester {

  private MessageDigest digest;

  public void setDigest(MessageDigest digest) {
    this.digest = digest;
  }

  public String digest(String msg) {
    return digest(msg, digest);
  }

  private String digest(String msg, MessageDigest digest) {
    digest.reset();
    byte[] bytes = msg.getBytes();
    byte[] out = digest.digest(bytes);
    return new String(out);
  }
}

The following configuration class defines two beans of types MessageDigest and MessageDigester.

@Configuration
public class Config {

  @Bean
  public MessageDigestFactoryBean shaDigest(){
    MessageDigestFactoryBean shaDigest =  new MessageDigestFactoryBean();
    shaDigest.setAlgorithmName("SHA1");
    return shaDigest;
  }

  @Bean
  public MessageDigester digester() throws Exception {
    MessageDigester messageDigester = new MessageDigester();
    messageDigester.setDigest(shaDigest().getObject());
    return messageDigester;
  }
}

Now we can test the FactoryBean setup and how to use it, as follows;

public class Main {

  public static void main(String[] args) {

    var ctx = new AnnotationConfigApplicationContext(Config.class);
    MessageDigester digester = ctx.getBean(MessageDigester.class);
    String message = digester.digest("Hello World!");
    System.out.println(message);   //Prints the digested message
    ctx.close();
  }

5. Getting the FactoryBean Instance Itself

When needed, accessing the FactoryBean instance is simple. If you want to get an instance of MessageDigestFactoryBean itself, add an “&” before the bean name.

var ctx = new AnnotationConfigApplicationContext(Config.class);
MessageDigestFactoryBean factoryBean = (MessageDigestFactoryBean) ctx.getBean("&shaDigest");
try {
  MessageDigest shaDigest = factoryBean.getObject();
  System.out.println("Explicit use digest bean: " + shaDigest.digest("Hello world".getBytes()));
} catch (Exception ex) {
  System.out.println("Could not find MessageDigestFactoryBean " + ex);
}
ctx.close();

The Program output:

Explicit use digest bean: [B@2898ac89

6. Conclusion

If you work with objects that are created by using a factory method and you want to use these classes in a Spring application, create a FactoryBean to act as an adapter, allowing your classes to take full advantage of Spring’s IoC capabilities.

Drop me your queries in the comments section.

Happy Learning !!

Source Code on Github

Comments

Subscribe
Notify of
guest
3 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode