FactoryBean理解

一、简介

Spring中有两种Bean,一种是普通的Bean,另外一种就是FactoryBean。FactoryBean与普通的Bean不同,其返回的对象不是指定类的一个实例,而是该FactoryBean的getObject方法所返回的对象。创建出来的对象的类型由getObjectType方法决定,是否是单例由isSingleton方法决定。上面提到的这个三个方法就组成了FactoryBean接口。

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

public interface FactoryBean<T> {
    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

二、什么时候用?

一般情况下,Spring利用反射实例化<bean>的class属性执行的实现类来初始化bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的。Spring为此提供了一个FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

FactoryBean 通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。

三、实践出真知

3.1 新建一个maven项目

引入Spring的依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>LearnFactoryBean</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.4.RELEASE</version>
        </dependency>
    </dependencies>

</project>

成品的目录结构如下,下面依次实现每个类和接口。

.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── wangjun
│   │   │           └── test
│   │   │               ├── Main.java
│   │   │               ├── TestFactoryBean.java
│   │   │               ├── TestInterface.java
│   │   │               ├── TestInterfaceImplA.java
│   │   │               └── TestInterfaceImplB.java
│   │   └── resources
│   │       └── spring.xml

3.2 接口和实现类

public interface TestInterface {
    void name();
}

public class TestInterfaceImplA implements TestInterface {
    public void name() {
        System.out.println("impl:a");
    }
}

public class TestInterfaceImplB implements TestInterface {
    public void name() {
        System.out.println("impl:b");
    }
}

3.3 工厂Bean

public class TestFactoryBean implements FactoryBean<TestInterface> {
    private Class<?> testInterface;

    /**
     * 随机返回TestInterface接口的实现类A或实现类B
     * @return
     * @throws Exception
     */
    public TestInterface getObject() throws Exception {
        return new Random().nextInt(2) == 0? new TestInterfaceImplA(): new TestInterfaceImplB();
    }

    /**
     * 返回bean的类型
     * @return
     */
    public Class<?> getObjectType() {
        return testInterface;
    }

    public void setTestInterface(Class<?> testInterface) {
        this.testInterface = testInterface;
    }

      public void test() {
        System.out.println("test1111");
    }
}

3.4 xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="testFactoryBean" class="com.wangjun.test.TestFactoryBean">
        <property name="testInterface" value="com.wangjun.test.TestInterface"/>
    </bean>

</beans>

3.5 测试类

public class Main {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        TestInterface testInterface = context.getBean(TestInterface.class);
          // 也可以这样获取bean
        // TestInterface testInterface = (TestInterface) context.getBean("testFactoryBean");
        testInterface.name();

          // 如果需要获取实现了FactoryBean的实例FirstFactoryBean,而不是getObject返回的实例,则需要在beanName前加&
        // 参考:https://stackoverflow.com/questions/49199901/what-does-the-mean-in-in-a-bean-name
        FirstFactoryBean f = (FirstFactoryBean) context.getBean("&shareInterface");
        f.test();
    }
}

3.6 测试运行

impl:a 和 impl:b 随机打印

Last updated