【Spring】一篇文章快速搞懂BeanFactory和FactoryBean的区别

2023-11-02

目录

一、BeanFactory

1.1 源码

1.2 使用场景

二、FactoryBean

2.1 源码

2.2 示例

2.2.1 方法一

2.2.2 方法二

2.3 FactoryBean的两种用法

2.3.1 简化xml配置,隐藏细节

2.3.2 返回不同Bean的实例

2.4 使用场景

三、BeanFactory和FactoryBean的区别以及共同点


一、BeanFactory

BeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂,我们可以通过它获取工厂管理的对象。在Spring中,BeanFactoryIOC容器的核心接口,它的职责包括实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。它定义了getBean()containsBean()等管理Bean的通用方法。但BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 :

  • DefaultListableBeanFactory
  • XmlBeanFactory
  • ApplicationContext

 

其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。

 

1.1 源码

public interface BeanFactory {
    /**
	用于区分factoryBean和bean,后面会讲到
    /*String FACTORY_BEAN_PREFIX = "&";

    /**
     返回byName返回bean的实例
	*/
    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;


    /**
     * Return a provider for the specified bean, allowing for lazy on-demand retrieval
     * of instances, including availability and uniqueness options.
     * @param requiredType type the bean must match; can be an interface or superclass
     * @return a corresponding provider handle
     * @since 5.1
     * @see #getBeanProvider(ResolvableType)
     */

    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    /**
	 判断工厂中是否包含给定名称的bean定义,若有则返回true
	*/
    boolean containsBean(String name);

    /**
    判断bean是否为单例
	*/
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    /**
	判断bean是否为多例
	*/
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    /**
	检查具有给定名称的bean是否匹配指定的类型。
     */
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    /**
	返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除*/
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    /**
	返回给定bean名称的所有别名 
	*/
    String[] getAliases(String name);
}

 

1.2 使用场景

  • 从Ioc容器中获取Bean(byName or byType)
  • 检索Ioc容器中是否包含指定的Bean
  • 判断Bean是否为单例

 

二、FactoryBean

使用XML配置spring容器的时候,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,比如一个类大量依赖了其他的对象属性,此时就算是使用自动装配,不需要再显式的写出bean之间的依赖关系,但是其依赖的对象也需要将其装配到spring容器中,也需要为它所依赖的多有对象都创建bean标签将他们注入,如果这个类依赖了上百个对象,那么这个工作量无疑是非常大的。

Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式。

Spring中共有两种bean,一种为普通bean,另一种则为工厂bean

以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

 

2.1 源码

public interface FactoryBean<T> {
    //从工厂中获取bean
    @Nullable
    T getObject() throws Exception;

    //获取Bean工厂创建的对象的类型
    @Nullable
    Class<?> getObjectType();

    //Bean工厂创建的对象是否是单例模式
    default boolean isSingleton() {
        return true;
    }
}

从它定义的接口可以看出,FactoryBean表现的是一个工厂的职责。 即一个Bean A如果实现了FactoryBean接口,那么A就变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()返回的对象,而不是A本身,如果要获取工厂A自身的实例,那么需要在名称前面加上'&'符号。

  • getObject('name')返回工厂中的实例
  • getObject('&name')返回工厂本身的实例

 

通常情况下,bean 无须自己实现工厂模式,Spring 容器担任了工厂的角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。由工厂 bean 产生的其他 bean 实例,不再由 Spring 容器产生,因此与普通 bean 的配置不同,不再需要提供 class 元素。

 

2.2 示例

我们现在要将下面这个TempDaoFactoryBean类交给工厂去创建管理

public class TempDaoFactoryBean {
    private String msg1;
    private String msg2;
    private String msg3;

    public void test() {
        System.out.println("FactoryBean");
    }
    public void setMsg1(String msg1) {
        this.msg1 = msg1;
    }
    public void setMsg2(String msg2) {
        this.msg2 = msg2;
    }
    public void setMsg3(String msg3) {
        this.msg3 = msg3;
    }
    public String getMsg1() {
        return msg1;
    }
    public String getMsg2() {
        return msg2;
    }
    public String getMsg3() {
        return msg3;
    }
}

 我们有两种方法可以选择:

方法一:通过spring的xml的方式对其进行配置.

方法二:定义一个CarProxy类,实现factoryBean接口.

 

2.2.1 方法一

如果使用传统方式配置下面Car的<bean>时,Car的每个属性分别对应一个<property>元素标签,就算是使用自动装配也要写很多<bean>标签,十分的麻烦

 

2.2.2 方法二

定义DaoFactoryBean实现FactoryBean接口

/**
 * FactoryBean由名字可以看出,是以bean结尾的,就说明这是一个bean,是由IOC容器管理的一个bean对象
 *
 * 如果你的类实现了FactoryBean
 * 那么spring容器当中会存储两个对象:一个是getObject()方法返回的对象(TempDaoFactoryBean),还有一个就是当前对象(DaoFactoryBean)
 *
 * getObject()返回的对象(TempDaoFactoryBean)存储在spring容器中给这个对象设置的beanName是当前类指定的对象,也就是     @Component("daoFactoryBean")  中的daoFactoryBean
 * 当前对象(DaoFactoryBean)在spring容器中设置的beanName是在@Component("")指定name的基础上加一个“&”,这里也就是&daoFactoryBean
 *  
 * ClassCastException类型转换异常
 */
public class DaoFactoryBean implements FactoryBean {
     // DaoFactoryBean这个工厂bean管理的对象
    private String msg;

    // 使用setter方法将其注入
    public void setMsg(String msg) {
        this.msg = msg;
    }

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

    @Override
    public Object getObject() throws Exception {    
        // 在FactoryBean内部创建对象实例
        TempDaoFactoryBean temp = new TempDaoFactoryBean();
        String[] msfArray = msg.split(",");
           temp.setMsg1(msfArray[0]);
        temp.setMsg2(msfArray[1]);
        temp.setMsg3(msfArray[2]);
        return temp;
    }

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

    /**
     * 是否是单例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}

 使用xml将这个factoryBean装配到spring容器中

<?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 id="daoFactory" class="priv.cy.dao.DaoFactoryBean">
        <property name="msg" value="msg1,msg2,msg3"></property>
    </bean>

</beans>

 

测试类:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(AppConfig.class);

        TempDaoFactoryBean tempDaoFactoryBean = (TempDaoFactoryBean) annotationConfigApplicationContext.getBean("daoFactory");
        System.out.println(tempDaoFactoryBean.getMsg1());
        System.out.println(tempDaoFactoryBean.getMsg2());
        System.out.println(tempDaoFactoryBean.getMsg3());
    }
}

 

执行结果:

 

因为当我们getBean时,spring对实现了FactoryBean接口的类实现了特殊处理

当调用getBean("daoFactory")时,Spring通过反射机制发现DaoFactoryBean实现了FactoryBean的接口

这时Spring容器就调用接口方法中的getObject()方法返回。如果希望获取CarFactoryBean的实例,

则需要在使用getBean(beanName)方法时在beanName前显示的加上"&"前缀:如getBean("&car");  

 

2.3 FactoryBean的两种用法

 

2.3.1 简化xml配置,隐藏细节

如果一个类有很多的属性,我们想通过Spring来对类中的属性进行值的注入,势必要在配置文件中书写大量属性配置,造成配置文件臃肿,那么这时可以考虑使用FactoryBean来简化配置

 

新建bean

public class Student {
    /** 姓名 */
    private String name;
    /** 年龄 */
    private int age;
    /** 班级名称 */
    private String className;
    public Student() {
    }
    public Student(String name, int age, String className) {
        this.name = name;
        this.age = age;
        this.className = className;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getClassName() {
        return className;
    }
    public void setClassName(String className) {
        this.className = className;
    }
    @Override
    public String toString() {
        return "Student{" + "name='" + name + '\'' + ", age=" + age + ", className='" + className + '\'' + '}';
    }
}

 

实现FactoryBean接口

public class StudentFactoryBean implements FactoryBean<Student> {
    private String studentInfo;
    @Override
    public Student getObject() throws Exception {
        if (this.studentInfo == null) {
            throw new IllegalArgumentException("'studentInfo' is required");
        }
        String[] splitStudentInfo = studentInfo.split(",");
        if (null == splitStudentInfo || splitStudentInfo.length != 3) {
            throw new IllegalArgumentException("'studentInfo' config error");
        }

        Student student = new Student();
        student.setName(splitStudentInfo[0]);
        student.setAge(Integer.valueOf(splitStudentInfo[1]));
        student.setClassName(splitStudentInfo[2]);
        return student;
    }
    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }
    public void setStudentInfo(String studentInfo) {
        this.studentInfo = studentInfo;
    }
}

 

新建day03.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"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--注意:class是StudentFactoryBean而不是Student-->
    <bean id="student" class="com.lyc.cn.day03.StudentFactoryBean" p:studentInfo="张三,25,三年二班"/>
</beans>

 

测试类

public class MyTest {
    @Before
    public void before() {
        System.out.println("---测试开始---\n");
    }
    @After
    public void after() {
        System.out.println("\n---测试结束---");
    }
    @Test
    public void testStudentFactoryBean() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("day03.xml");
        System.out.println(applicationContext.getBean("student"));
        System.out.println(applicationContext.getBean("&student"));
    }
}

 

运行

---测试开始---

Student{name='张三', age=25, className='三年二班'}

org.springframework.beans.factory_bean.StudentFactoryBean@1ae369b7

---测试结束---

 

这样我们就实现了通过BeanFactory接口达到了简化配置文件的作用。另外大家也可以发现getBean(“student”)返回的Student类的实例;而getBean("&student")返回的是StudentFactoryBean实例,即工厂bean其本身。

 

2.3.2 返回不同Bean的实例

既然FactoryBean是一种工厂bean,那么我们就可以根据需要的类型,返回不同的bean的实例,通过代码简单说明一下

 

新建bean

public interface Animal {
    void sayHello();
}

public class Cat implements Animal {
    @Override
    public void sayHello() {
        System.out.println("hello, 喵喵喵...");
    }
}

public class Dog implements Animal {
    @Override
    public void sayHello() {
        System.out.println("hello, 汪汪汪...");
    }
}

创建了一个Animal接口极其两个实现类Cat和Dog,并进行简单输出,那么如何通过FactoryBean来通过配置返回不同的Animal实例呢

 

新建AnimalFactoryBean

public class AnimalFactoryBean implements FactoryBean<Animal> {
    private String animal;

    @Override
    public Animal getObject() throws Exception {
        if (null == animal) {
            throw new IllegalArgumentException("'animal' is required");
        }
        if ("cat".equals(animal)) {
            return new Cat();
        } else if ("dog".equals(animal)) {
            return new Dog();
        } else {
            throw new IllegalArgumentException("animal type error");
        }
    }

    @Override
    public Class<?> getObjectType() {
        if (null == animal) {
            throw new IllegalArgumentException("'animal' is required");
        }
        if ("cat".equals(animal)) {
            return Cat.class;
        } else if ("dog".equals(animal)) {
            return Dog.class;
        } else {
            throw new IllegalArgumentException("animal type error");
        }
    }
    public void setAnimal(String animal) {
        this.animal = animal;
    }
}

 

修改day03.xml配置文件,增加bean

<bean id="animal" class="com.lyc.cn.day03.AnimalFactoryBean" p:animal="cat"/>

 

在MyTest中添加测试用例

@Test
public void testAnimalFactoryBean() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("day03.xml");
    Animal animal = applicationContext.getBean("animal", Animal.class);
    animal.sayHello();
}

 

运行

---测试开始---

hello, 喵喵喵...

---测试结束---

可以看到,配置文件里我们将animal配置成了cat,那么返回的就是cat的实例,也是简单工厂的一个实现

 

 

2.4 使用场景

说了这么多,为什么要有FactoryBean这个东西呢,有什么具体的作用吗?

 

FactoryBean在Spring中最为典型的一个应用就是用来创建AOP的代理对象。

我们知道AOP实际上是Spring在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP代理对象通过Java的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在Spring中就是——ProxyFactoryBean。

所以,FactoryBean为我们实例化Bean提供了一个更为灵活的方式,我们可以通过FactoryBean创建出更为复杂的Bean实例。

 

还有比如我们spring需要整合mybatis,在没有spring-mybatis的情况下(spring-mybatis会帮助你将MyBatis 代码无缝地整合到Spring ),我们需要将mybatis核心类SqlSessionFactory注入到spring容器,那么思考使用最常用的两种方式:

  1. 注解,可是mybatis是个我们引用的独立的项目.与我们自己的项目源码无关,我们无法去修改它的源码,在它的源码上添加注解,所以不能使用注解的方法
  2. xml,sqlSessionFacory需要注入许多的依赖,如果使用XML来配置,需要我们写大量的配置标签,非常不方便维护。

所以可以选择一个代理类去处理sqlSessionFacory,也就是我们在整合spring+mybatis时使用的SqlSessionFactoryBean,这个类是由mybatis提供的用来方便我们快速配置mybatis的factoryBean,通过这个类把很多繁琐的配置代码封装了起来,类似于装饰者模式,SqlSessionFactoryBean里面管理了sqlSessionFacory并且对他进行相关配置设置操作,我们只需要将SqlSessionFactoryBean注入到spring容器中,并在在xml向这个factoryBean传入一些简单的配置信息,SqlSessionFactoryBean就会帮我们自动配置好sqlSessionFacory,很多复杂的配置都帮我们填充好了,然后我们就可以通过SqlSessionFactoryBean获取已经配置完成的sqlSessionFacory。

 

三、BeanFactoryFactoryBean的区别以及共同点

共同点:都是接口

区别:

  • BeanFactory 以Factory结尾,表示它是一个工厂类,用于管理Bean的一个工厂。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。该接口是IoC容器的顶级接口,是IoC容器的最基础实现,也是访问Spring容器的根接口,负责对bean的创建,访问等工作
  • 对FactoryBean而言,以Bean结尾,说明这是一个交给容器去管理的bean。这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

参考资料:https://blog.csdn.net/lyc_liyanchao/article/details/82424122

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Spring】一篇文章快速搞懂BeanFactory和FactoryBean的区别 的相关文章

随机推荐

  • FPN详解

    论文题目 Feature Pyramid Networks for Object Detection 论文链接 论文链接 论文代码 Caffe版本代码链接 一 FPN初探 1 图像金字塔 图1 图像金字塔 图2 高斯金字塔效果 如上图所示
  • 【恒指早盘分析】9.18恒指今日复盘及后市思路

    9月18日消息 美股周二小幅收高 市场正在等待美联储货币政策会议结果 沙特称原油生产设施遭袭造成的产能损失已恢复50 预计月底将完全恢复 今日开盘 港股恒指高开0 18 午后转跌 截至收盘 恒生指数 26754 12 36 12 0 13
  • 服务器巡检都做什么工作,巡检的手段是什么?

    服务器巡检都做什么工作 服务器巡检主要分为 1 对于基础资源的巡检 比如说磁盘 cpu 内存的利用率有没有达到公司设定的阈值范围之上 如果达到了就需要及时扩容或者杀进程 2 核心业务的巡检 跟钱打交道的程序 对核心业务的相关指标监控的图形
  • 解决root@localhost's password:localhost:permission denied,please try again

    背景 在装完hadoop及jdk之后 在执行start all sh的时候出现 root localhost s password localhost permission denied please try again 可是 我记得当时设
  • 计算机视觉与深度学习-经典网络解析-ZFNet-[北邮鲁鹏]

    这里写目录标题 ZFNet 主要改进 减小第一层卷积核 将第二 第三个卷积层的卷积步长都设置为2 增加了第三 第四个卷积层的卷积核个数 ZFNet ZFNet是一种基于AlexNet的模型 由Matthew D Zeiler和Rob Fer
  • smplayer中使用srt字幕乱码问题

    选择菜单 选项 gt 首选项 gt 字幕 选择默认字幕编码cp936 简体中文 和正确的中文字体 本文转自JustRun博客园博客 原文链接 http www cnblogs com JustRun1983 archive 2012 10
  • 用户画像技术干货

    1 用户画像是什么 在互联网步入大数据时代后 用户行为给企业的产品和服务带来了一系列的改变和重塑 其中最大的变化在于 用户的一切行为在企业面前是可 追溯 分析 的 企业内保存了大量的原始数据和各种业务数据 这是企业经营活动的真实记录 如何更
  • 在传统公司干IT是一种什么体验(五)

    与不懂技术的人交流 最痛苦的不是你说的她听不懂 而是她自以为懂帝 强行把概念降低到她能所理解的层面 然后丰富的经验来指导你 表哥语录 表哥以前最喜欢当老师 例尤其是公司新来的同事 都愿意找表哥请教技术问题 自动去了新公司之后 表哥天天面对不
  • 内网穿透神器-frp的概念,搭建和使用,方便访问内网服务

    FRP概念 FRP是什么 借助官网的描述 frp 是一个专注于内网穿透的高性能的反向代理应用 支持 TCP UDP HTTP HTTPS 等多种协议 且支持 P2P 通信 可以将内网服务以安全 便捷的方式通过具有公网 IP 节点的中转暴露到
  • sql注入;基于时间盲注;sqli-labs/less-9;

    1 搭建好环境 连好数据库 这里看我这篇文章 可以找到网盘文件 sql注入之报错注入 这里用到的是sqli labs Less 1 也就是第一关 利用报错信息注入 小乘杭的博客 CSDN博客 2 无真假 报错回显 通过时间延迟判断 3 通过
  • Hadoop Kerberos安全机制

    Hadoop Kerberos安全机制 1 背景 在Hadoop1 0 0或者CDH3 版本之前 hadoop并不存在安全认证一说 默认集群内所有的节点都是可靠的 值得信赖的 用户与HDFS或者M R进行交互时并不需要进行验证 导致存在恶意
  • Pyramid pooling module(PPM)

    参考代码 https blog csdn net wd18508423052 article details 93882113 上采样问题可以参考 https blog csdn net zouxiaolv article details
  • Unity无法打开项目的解决方法

    Unity无法打开项目的解决方法 这两天硬盘突然大量文件损坏导致一些项目无法打开 如Unity的项目在hub窗口打开项目后就会卡死报错 求助他人以后基本问题得到了解决 故记录一下 遇到项目无法打开的情况后 尝试正常建立新项目 发现工程能正常
  • C语言-蓝桥杯-算法提高 01背包

    问题描述 给定N个物品 每个物品有一个重量W和一个价值V 你有一个能装M重量的背包 问怎么装使得所装价值最大 每个物品只有一个 输入格式 输入的第一行包含两个整数n m 分别表示物品的个数和背包能装重量 以后N行每行两个数Wi和Vi 表示物
  • 稳压二极管的串联与并联

    稳压二极管又叫齐纳二极管 是一种用于稳定电压的二极管 注 其承受电流非常小 需要根据场合使用 常见类型 其组合方式有串联与并联 串联 串联有以上四种方式 图1 10V和8V稳压管均击穿 VCC为两者稳压值的和 VCC 10 8 18V 图2
  • 整理java集合

    1 map 实现类 HashMap 无序 键值可为null 非同步 Hashtable 无序 键值非null 作键的对象必须实现 hashCode 方法和 equals 同步 性能低 LinkedHashMap 有序记录插入顺序 如果在映射
  • 【超详细Python秒杀代码】京东淘宝抢购通用

    由于之前手动抢购枪神7超竞连续失败两次后怒学脚本抢购 今天给大家分享一期超简单 直观 好用的Python脚本 此代码目前仅适用于可以加入购物车的商品 直接上代码 import datetime import time from seleni
  • 为什么机油使用后变红_汽车机油用完后放出油红色怎么回事

    展开全部 汽车e68a84e8a2ad62616964757a686964616f31333433623861机油用完后放出油红色的原因为 因为汽车添加的机油油品出现问题了 劣质机油往往都没有很好的润滑冷却作用 不仅无法清除发动机内部的积碳
  • MarkerView

    前言 过了一个愉快的五一后 我们又开始上班了 完成了本分的工作 抽点时间来和说说MarkerView咯 给我的印象 MarkerView的扩展性很强 它可以自定义自己想要的U样式 MarkerView源码 View that can be
  • 【Spring】一篇文章快速搞懂BeanFactory和FactoryBean的区别

    目录 一 BeanFactory 1 1 源码 1 2 使用场景 二 FactoryBean 2 1 源码 2 2 示例 2 2 1 方法一 2 2 2 方法二 2 3 FactoryBean的两种用法 2 3 1 简化xml配置 隐藏细节