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

14 篇文章 33 订阅

目录

一、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

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring框架中,BeanFactoryFactoryBean是两个重要的概念,虽然名字相近,但它们在功能和作用上有很大的不同。 首先,BeanFactorySpring的容器,是用于管理对象的工厂,负责创建、初始化、装配和管理Bean对象。它是一个顶级接口,Spring框架提供了多个BeanFactory的实现,比如DefaultListableBeanFactory和XmlBeanFactory等。 而FactoryBean是一个工厂Bean接口,它的实现类可以是一个普通的Bean,也可以是一个特殊的Bean。FactoryBean允许我们在Bean创建之前对Bean进行一些配置或者装配处理,从而灵活地管理Bean对象。由于FactoryBean是一个接口,所以我们需要实现它的getObject()方法来返回一个Bean实例。 它们的区别在于: 1. BeanFactorySpring框架中的一个顶级接口,它是一个对象容器,用于管理对象的创建、初始化、装配和管理;FactoryBean是一个接口,它用于定制Bean的实例化方式。 2. BeanFactory可以创建和获取多种类型的Bean,包括普通java对象以及Spring特有的Bean对象;而FactoryBean只能够创建一种特殊的Bean类型。 3. BeanFactory是异步创建和初始化Bean对象,以便在需要时才能获取Bean实例;而FactoryBean是同步创建和初始化Bean对象,并将实例化后的Bean对象放入容器中以便获取。 4. BeanFactory在获得Bean对象时,需要显式调用getBean()方法才能获取对象;而FactoryBean在获取对象时,直接访问FactoryBean的BeanName即可。 总之,BeanFactorySpring的核心接口之一,它用来管理Bean对象;而FactoryBean是Spring中的一个特殊接口,它用来定制Bean的实例化方法,使用上要注意二者的区别
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值