In fact, from the name you can see that BeanFactory is Factory and FactoryBean is a Bean, let’s look at the summary:

  • BeanFactory is one of the core interfaces of the Spring Framework for managing and obtaining instances of beans in an application. It is an implementation of the Factory pattern and is responsible for creating, configuring, and managing Bean objects.BeanFactory is the foundation of the Spring IoC container, which reads Bean definitions from configuration metadata (e.g., an XML file) and instantiates and delivers those Beans when needed.
  • FactoryBean is a special bean that is a factory object used to create and manage instances of other beans; the FactoryBean interface defines a way to create beans that allows developers to do more customization in the bean creation process. By implementing the FactoryBean interface, developers can create complex instances of a bean or perform some additional logical processing before the bean is instantiated.

The difference is that a BeanFactory is the core interface of the Spring Framework that manages and provides instances of a bean, while a FactoryBean is a special bean that creates and manages instances of other beans.FactoryBean provides more customization during the creation of a bean, allowing for additional logical processing.

1. BeanFactory

BeanFactory See the name to know that this is a bean factory, Spring IoC container to help us complete the creation of beans, management and other operations, so these operations are inseparable from the BeanFactory.

Let’s take a quick look at the BeanFactory code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public interface BeanFactory {
 String FACTORY_BEAN_PREFIX = "&";
 Object getBean(String name) throws BeansException;
 <T> T getBean(String name, Class<T> requiredType) throws BeansException;
 Object getBean(String name, Object... args) throws BeansException;
 <T> T getBean(Class<T> requiredType) throws BeansException;
 <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
 <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
 <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
 boolean containsBean(String name);
 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
 boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
 @Nullable
 Class<?> getType(String name) throws NoSuchBeanDefinitionException;
 @Nullable
 Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
 String[] getAliases(String name);
}

The role of all these methods can be seen by their names.

  • FACTORY_BEAN_PREFIX: This variable actually says that if the current bean is not a normal bean like User or Book, but a FactoryBean, then add an & prefix to the name of the bean, which I will demonstrate in the second subsection.
  • getBean: This method looks up the bean by its name, type, etc. It can be used to retrieve the bean from the factory;
  • getBeanProvider: this method can get an ObjectProvider, ObjectProvider is an interface in the Spring framework, used to get the instance of the bean object. It provides a way to delay loading a bean and dynamically obtain a bean instance when needed (lazy loading).
  • containsBean: determine whether a bean is contained.
  • isSingleton: determines if a Bean is singleton.
  • isPrototype: determine if a Bean is a multiple instance.
  • isTypeMatch: Determines if a Bean is of a given type.
  • getType: Get the type of a Bean.
  • getAliases: Get the aliases of a Bean.

As you can see, many of them are common and commonly used methods in our daily development.

Many people are just starting to get in touch with Spring, will use an object ClassPathXmlApplicationContext, which is actually a subclass of BeanFactory. Let’s look at the inheritance diagram of BeanFactory:

BeanFactory inheritance diagram

Inheritance class is more, I say a few you may be more familiar with:

  • ClassPathXmlApplicationContext: this is the Spring container startup, from the current class path to load the XML configuration file, the parameter is the classpath XML file path.
  • FileSystemXmlApplicationContext: this is the Spring container startup, from the file system to load the XML configuration file, the parameter is an absolute path.
  • AnnotationConfigApplicationContext: this is if we use Java code to do the Spring container configuration, through the configuration class to load the Java configuration class.
  • DefaultListableBeanFactory: this default implementation of the ListableBeanFactory and BeanDefinitionRegistry interface, is a more mature BeanFactory.

Okay, this is the BeanFactory.

2. FactoryBean

2.1. Analysis

FactoryBean actually many people may have seen, just may not go to summarize the generalization. Let me give you some examples.

In SpringBoot project using mybatis, if we want to configure MyBatis to the project, generally need to configure the following Bean:

1
2
3
4
5
6
7
8
9
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="typeAliasesPackage" value="org.javaboy.shirodemo.model"/>
    <property name="mapperLocations">
        <list>
            <value>classpath*:org/javaboy/shirodemo/mapper/*.xml</value>
        </list>
    </property>
</bean>

When we configure Shiro, we typically configure the following beans:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter">
    <property name="securityManager" ref="securityManager"/>
    <property name="loginUrl" value="/login"/>
    <property name="successUrl" value="/index"/>
    <property name="unauthorizedUrl" value="/unauthorizedUrl"/>
    <property name="filterChainDefinitions">
        <value>
            /index=anon
            /doLogin=anon
            /hello=user
            /**=authc
        </value>
    </property>
</bean>

If there is a date string in the form parameters submitted by the front-end and the controller method uses a Date object to accept it, then SpringMVC can’t handle this date by default and you need to configure a date converter, generally we add the following bean in the Spring container.

1
2
3
4
5
6
7
8
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService">
    <property name="converters">
        <set>
            <ref bean="myDateConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>

Looking at the above three beans, there is a common feature, that is, the name of the bean is xxxFactoryBean.

Why use xxxFactoryBean instead of injecting the required bean directly into the Spring container? Let’s take MyBatis as an example:

If you have manually configured MyBatis you will know that MyBatis has two important classes, one is SqlSessionFactory, and the other is SqlSession, through the SqlSessionFactory can get a SqlSession.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SqlSessionFactoryUtils {
    private static SqlSessionFactory SQLSESSIONFACTORY = null;
    public static SqlSessionFactory getInstance() {
        if (SQLSESSIONFACTORY == null) {
            try {
                SQLSESSIONFACTORY = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return SQLSESSIONFACTORY;
    }
}
public class Main {
    public static void main(String[] args) {
        SqlSessionFactory factory = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = factory.openSession();
        List<User> list = sqlSession.selectList("org.javaboy.mybatis01.mapper.UserMapper.getAllUser");
        for (User user : list) {
            System.out.println("user = " + user);
        }
        sqlSession.close();
    }
}

As you can see, neither SqlSessionFactory nor SqlSession is constructed with the new keyword. In fact, both are interfaces, so it’s obviously not possible to use the new keyword directly. The former through the builder pattern to configure various properties, and finally generate an instance of SqlSessionFactory, the latter by the SqlSessionFactory to generate. The final object is an instance of a subclass of both interfaces.

So, since SqlSessionFactory and SqlSession can’t be configured directly in the Spring container, you can configure such beans through xxxFactoryBean.

Let’s take a look at the SqlSessionFactoryBean class. The source code is very long, so I’ve picked out the important ones:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

  private SqlSessionFactory sqlSessionFactory;

  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }
  @Override
  public Class<? extends SqlSessionFactory> getObjectType() {
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
  }
  @Override
  public boolean isSingleton() {
    return true;
  }
}

SqlSessionFactoryBean needs to implement the FactoryBean interface and specify the generic type SqlSessionFactory when implementing the interface, which means that the final bean produced by the SqlSessionFactoryBean will be SqlSessionFactory. After implementing the FactoryBean interface, you need to implement the three methods in the interface:

  • getObject: the object returned by this method is the real object to be registered to the Spring container, in this method, we can be configured in a variety of ways on the bean.
  • getObjectType: This method returns the type of the object that is registered with the Spring container.
  • isSingleton: This method is used to return whether the bean registered to the Spring container is singleton.

This is the characteristic of FactoryBean, because the initialization of a bean is too complicated, then you can use FactoryBean to help register to the Spring container.

2.2. Practice

Suppose I have the following class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Author {

    private String name;
    private Integer age;

    private Author() {
    }

    public static Author init(String name, Integer age) {
        Author author = new Author();
        author.setAge(age);
        author.setName(name);
        return author;
    }

    // ...
}

The feature of this class is that the constructor method is private and you can’t create an instance of it directly using the new keyword. Now I want to register the objects of this class with the Spring container, so I can provide an AuthorFactoryBean.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class AuthorFactoryBean implements FactoryBean<Author> {
    @Override
    public Author getObject() throws Exception {
        return Author.init("javaboy", 99);
    }

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

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

Then you can configure the AuthorFactoryBean in the Spring container.

1
<bean class="org.javaboy.bean.AuthorFactoryBean" id="author"/>

Next, we can get the Author object from the container, but note that the name author gets the Author object, not the AuthorFactoryBean object, and if you want to get the AuthorFactoryBean object, you have to get it by the name &author (recall what was said in the first subsection).

1
2
3
4
5
6
7
8
9
public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Object author = ctx.getBean("author");
        Object authorFactoryBean = ctx.getBean("&author");
        System.out.println("author.getClass() = " + author.getClass());
        System.out.println("authorFactoryBean.getClass() = " + authorFactoryBean.getClass());
    }
}

The results of the run are as follows.

The result of running the program

3. Summary

The difference is that a BeanFactory is the core interface of the Spring Framework that manages and provides instances of a bean, while a FactoryBean is a special bean that creates and manages instances of other beans.FactoryBean provides more customization during the creation of a bean, allowing for additional logical processing.

Reference: https://mp.weixin.qq.com/s/r3rnVhU8vr58Cw__UWOVLA