What is the difference between BeanFactory and FactoryBean in Spring?

This is also a classic interview question during Spring interviews. Today we will talk about this topic.

In fact, you can tell from the name. BeanFactory is a Factory and FactoryBean is a Bean. Let’s take a look at the summary first:

  • BeanFactory is one of the core interfaces of the Spring framework, used to manage and obtain Bean instances in applications. It is an implementation of the factory pattern and is responsible for creating, configuring and managing Bean objects. The BeanFactory is the basis of the Spring IoC container, which reads bean definitions from configuration metadata (such as XML files) and instantiates and provides these 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, which allows developers to perform more customized operations during the creation of beans. By implementing the FactoryBean interface, developers can create complex Bean instances or perform some additional logic before the Bean is instantiated.

The difference is that BeanFactory is the core interface of the Spring framework and is used to manage and provide Bean instances, while FactoryBean is a special Bean used to create and manage instances of other Beans. FactoryBean provides more customization capabilities in the bean creation process, allowing additional logic processing.

Some friends may not see it clearly yet, so let’s take a closer look.

1. BeanFactory

BeanFactory You can tell from the name that this is a Bean factory. Friends know that the Spring IoC container helps us complete Bean creation, management and other operations, so these operations are inseparable from BeanFactory.

Let’s take a brief look at the code of BeanFactory:

public interface BeanFactory {
 String FACTORY_BEAN_PREFIX = " & amp;";
 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);

}

These methods are basically well-known by name:

  • FACTORY_BEAN_PREFIX: This variable actually means that if the current Bean is not an ordinary Bean like User or Book, but a FactoryBean, add a & amp; prefix to the Bean name. I will explain this in the second step. Demonstrate with your friends in this section.

  • getBean: This method is to query the Bean based on its name, type, etc.

  • getBeanProvider: This method can obtain an ObjectProvider. ObjectProvider is an interface in the Spring framework and is used to obtain instances of Bean objects. It provides a way to lazy load beans and dynamically obtain bean instances when needed (lazy loading).

  • containsBean: Determine whether a Bean is included.

  • isSingleton: Determines whether a Bean is a singleton.

  • isPrototype: Determines whether a Bean has multiple instances.

  • isTypeMatch: Determines whether the type of a certain Bean is a given type.

  • getType: Get the type of Bean.

  • getAliases: Get the aliases of the Bean.

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

When many friends first come into contact with Spring, they will use an object ClassPathXmlApplicationContext, which is actually a subclass of BeanFactory. Let’s take a look at the inheritance diagram of BeanFactory:

Picture

There are many inheritance classes. Let me mention a few that you may be familiar with:

  1. ClassPathXmlApplicationContext: This is the XML configuration file loaded from the current classpath when the Spring container starts. The parameter is the XML file path under the classpath.

  2. FileSystemXmlApplicationContext: This is used to load the XML configuration file from the file system when the Spring container starts. The parameter is an absolute path.

  3. AnnotationConfigApplicationContext: This is if we use Java code to configure the Spring container, we use this configuration class to load the Java configuration class.

  4. DefaultListableBeanFactory: This implements the ListableBeanFactory and BeanDefinitionRegistry interfaces by default and is a relatively mature BeanFactory.

Okay, this is the feature of BeanFactory, everyone understands it~

2. FactoryBean

2.1 Analysis

FactoryBean In fact, many friends may have seen it, but they may not have summarized it. Let me give you some examples.

In the SSM project, if we want to configure MyBatis into the project, we generally need to configure the following Bean:

<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 generally configure the following beans:

<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 the parameter passed by our front end is in key-value format and has a date, then friends know that the server-side SpringMVC cannot handle this date by default and needs to configure a date converter. Generally, we add the following Bean in the Spring container:

<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"/>

Friends, please observe that the three Beans above have a common feature, that is, the Bean names are all xxxFactoryBean.

Why use xxxFactoryBean instead of directly injecting the required beans into the Spring container? Take MyBatis as an example:

Friends who have manually configured MyBatis should know that MyBatis has two important classes, one is SqlSessionFactory, and the other is SqlSession. A SqlSession can be obtained through SqlSessionFactory. But I don’t know if friends still remember the configuration code. The manual configuration code is as follows

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

Friends, you can see that neither SqlSessionFactory nor SqlSession is officially new. In fact, both of them are interfaces. Obviously, they cannot be new. The former uses the builder mode to configure various properties and finally generates an instance of SqlSessionFactory. , the latter is generated through the factory of the former, and what is finally obtained are objects of subclasses of these two interfaces.

Therefore, SqlSessionFactory and SqlSession cannot be configured directly in the Spring container, so for such beans, they can be configured through xxxFactoryBean.

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

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

Let’s take a look, SqlSessionFactoryBean needs to implement the FactoryBean interface, and when implementing the interface, specify the generic to be SqlSessionFactory, that is, the final bean produced by SqlSessionFactoryBean is 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 object that is actually to be registered in the Spring container. In this method, we can configure the Bean in various ways.

  • getObjectType: This method returns the object type registered in the Spring container.

  • isSingleton: This method is used to return whether the Bean registered in the Spring container is a singleton.

This is the characteristic of FactoryBean. Since the initialization of a certain Bean is too complicated, FactoryBean can be used to help register it in the Spring container.

2.2 Practice

Brother Song wrote another simple example for friends to experience FactoryBean.

Let’s say I have a class like this:

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;
    }
    //Omit getter/setter/toString
}

The characteristic of this class is that the constructor is private, and you cannot use new from the outside. Now I want to register the object of this class into the Spring container, then I can provide an AuthorFactoryBean:

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 configure AuthorFactoryBean in the Spring container:

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

Next we can get the Author object from the container, but be aware that what you get through the name author is the Author object, not the AuthorFactoryBean object. If you want to get the AuthorFactoryBean object, you have to pass & amp ;author to get the name.

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Object author = ctx.getBean("author");
        Object authorFactoryBean = ctx.getBean(" & amp;author");
        System.out.println("author.getClass() = " + author.getClass());
        System.out.println("authorFactoryBean.getClass() = " + authorFactoryBean.getClass());
    }
}

Let’s take a look at the final running results:

Picture

It’s consistent with what we think~

3. Summary

After the previous introduction, I believe that friends can already distinguish between BeanFactory and FactoryBean. Let’s review the content at the beginning of this article:

  • BeanFactory is one of the core interfaces of the Spring framework, used to manage and obtain Bean instances in applications. It is an implementation of the factory pattern and is responsible for creating, configuring and managing Bean objects. The BeanFactory is the foundation of the Spring IoC container, which reads bean definitions from configuration metadata (such as XML files) and instantiates and provides these 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, which allows developers to perform more customized operations during the creation of beans. By implementing the FactoryBean interface, developers can create complex Bean instances or perform some additional logic before the Bean is instantiated.

The difference is that BeanFactory is the core interface of the Spring framework and is used to manage and provide Bean instances, while FactoryBean is a special Bean used to create and manage instances of other Beans. FactoryBean provides more customization capabilities in the bean creation process, allowing additional logic processing.