> For the complete documentation index, see [llms.txt](https://jun-wang.gitbook.io/learnjava/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jun-wang.gitbook.io/learnjava/ji-shu-xue-xi/spring/factorybean-li-jie.md).

# FactoryBean理解

## 一、简介

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

```java
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的依赖：

```markup
<?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 接口和实现类

```java
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

```java
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

```markup
<?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 测试类

```java
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 随机打印
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jun-wang.gitbook.io/learnjava/ji-shu-xue-xi/spring/factorybean-li-jie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
