Spring中BeanFactory和FactoryBean的区别

BeanFactory:

BeanFactory 顾名思义其主体是Factory,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。我们可以通俗的理解为,它是一个开发者可以感知存在的窗口,开发者可以利用这个窗口来实现对bean的获取,所以这个接口是面向开发者和面向应用程序的spring设计的一套接口规范,具体如下:

boolean containsBean(String beanName)

判断工厂中是否包含给定名称的bean定义,若有则返回true
Object getBean(String) 返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常
Object getBean(String, Class) 返回以给定名称注册的bean实例,并转换为给定class类型

Class getType(String name)

返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除NoSuchBeanDefinitionException异常
boolean isSingleton(String) 判断给定名称的bean定义是否为单例模式
String[] getAliases(String name)

返回给定bean名称的所有别名

 源码如下:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

     T getBean(String var1, Class var2) throws BeansException;

     T getBean(Class var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

     T getBean(Class var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class var2) throws NoSuchBeanDefinitionException;

    Class getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

 FactoryBean:

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

一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;


public interface FactoryBean {

	
	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

	
    // 返回由FactoryBean创建的Bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中;
	@Nullable
	T getObject() throws Exception;

	//返回FactoryBean创建的Bean类型。
    //当配置文件中的class属性配置的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身,
    //而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
	@Nullable
	Class getObjectType();

	//返回由FactoryBean创建的Bean实例的作用域是singleton还是prototype;
	default boolean isSingleton() {
		return true;
	}

}

Spring 中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean 即 FactoryBean。FactoryBean跟普通Bean不同,其返回的对象不是指定类的一个实例,而是该FactoryBean的getObject方法所返回的对象。创建出来的对象是否属于单例由isSingleton中的返回决定。

首先它是一个Bean,但又不仅仅是一个Bean。它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。

例:如果使用传统方式配置下面Car的时,Car的每个属性分别对应一个元素标签。

package  com.baobaotao.factorybean;  
    public   class  Car  {  
        private   int maxSpeed ;  
        private  String brand ;  
        private   double price ;  
        public   int  getMaxSpeed ()   {  
            return   this . maxSpeed ;  
        }  
        public   void  setMaxSpeed ( int  maxSpeed )   {  
            this . maxSpeed  = maxSpeed;  
        }  
        public  String getBrand ()   {  
            return   this . brand ;  
        }  
        public   void  setBrand ( String brand )   {  
            this . brand  = brand;  
        }  
        public   double  getPrice ()   {  
            return   this . price ;  
        }  
        public   void  setPrice ( double  price )   {  
            this . price  = price;  
       }  
}

如果用FactoryBean的方式实现就灵活点,下例通过逗号分割符的方式一次性的为Car的所有属性指定配置值: 
当调用getBean("car")时,Spring通过反射机制发现CarFactoryBean实现了FactoryBean的接口,这时Spring容器就调用接口方法CarFactoryBean#getObject()方法返回。

如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName)方法时在beanName前显示的加上"&"前缀:如getBean("&car");

BeanFactoryFactoryBean的关系:

Spring对所有FactoryBean子类提供超常规服务的,通过spring门面BeanFactory子类getBean时候,如果发现getBean对应的class是FactoryBean的子类,那么BeanFactory在getBean时候代码做了特殊处理,具体特殊处理代码在方法AbstractBeanFactory.getObjectForBeanInstance()中,如下图

AbstractApplicationContext类里面的refresh是整个spring框架中的重中之重的方法,是整个最核心的方法,而AbstractApplicationContext又是BeanFactory的抽象实现类,从而展开和FactoryBean的联系。所以我们看到在此方法里面会有一个 finishBeanFactoryInitialization 方法,这个方法非常重要。。。实例化所有剩余的(非懒加载)单例Bean。(也就是我们自己定义的那些Bean们)

Spring中BeanFactory和FactoryBean的区别_第1张图片

 通过注释我们可以看到,

// Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons();

这个是实例化bean的核心方法

Spring中BeanFactory和FactoryBean的区别_第2张图片

DefaultListableBeanFactory是具体的实现isBeanEligibleForMetadataCaching的实现类

 这张图信息量很大,基本印证了我们之前的逻辑:

如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName)方法时在beanName前显示的加上"&"前缀:如getBean("&car");

Spring中BeanFactory和FactoryBean的区别_第3张图片

AbstractBeanFactorydoGetBean方法是具体的获取bean的方法

Spring中BeanFactory和FactoryBean的区别_第4张图片Spring中BeanFactory和FactoryBean的区别_第5张图片

Spring中BeanFactory和FactoryBean的区别_第6张图片 

上图中beanInstance 是spring容器中存贮的原始对象,第1处标记处如果这个bean不是FactoryBean,那么直接返回原始对象;第2处标记处,如果是FactoryBean子类则返回的对象是FactoryBean.getObject() 创造的对象。 如下:

Spring中BeanFactory和FactoryBean的区别_第7张图片

我们可以清晰的看到调用了factory的getobject方法。从而得到验证

Spring中BeanFactory和FactoryBean的区别_第8张图片

总结:

所以二者之间是有着密不可分的关系,两个接口的分工是很明确的

 FactoryBean 子类更多的是偏应用层用于创建具体的bean,BeanFactory 是偏容器的基础设施,是一个门面。

这样说可能还是很难懂,来个形象的比喻,假设spring容器是食堂,BeanFactory 就是食堂打菜的窗口, FactoryBean 才是真正烧菜的地方。BeanFactory 是容器核心类,一般不需要自己实现子类,FactoryBean 是工厂类,一般写个插件啥的会用到,比如mysql-sprijng插件。假如食堂原来只能做西餐,我现在需要做中餐,那么BeanFactory不需要动,我只需要写个FactoryBean 子类就行了。所以FactoryBean是面向开发者应用层面的接口规范,而FactoryBean面向底层具体实例化bean的操作扩展接口,而已对其进行扩展和增强,所以BeanFactory针对的更多的是bean的消费者,FactoryBean更多的是针对bean的生产者,而spring容易扮演着bean的管理者。

实战:

先定义一个Bean实现FactoryBean接口

@Component
public class MyBean implements FactoryBean {
    private String message;
    public MyBean() {
        this.message = "通过构造方法初始化实例";
    }
    @Override
    public Object getObject() throws Exception {
        // 这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例。
        // 如return new Student()...
        return new MyBean("通过FactoryBean.getObject()创建实例");
    }
    @Override
    public Class getObjectType() {
        return MyBean.class;
    }
    public String getMessage() {
        return message;
    }
}

MyBean实现了FactoryBean接口的两个方法,getObject()是可以返回任何对象的实例的,这里测试就返回MyBean自身实例,且返回前给message字段赋值。同时在构造方法中也为message赋值。然后测试代码中先通过名称获取Bean实例,打印message的内容,再通过&+名称获取实例并打印message内容。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
    @Autowired
    private ApplicationContext context;
    @Test
    public void test() {
        MyBean myBean1 = (MyBean) context.getBean("myBean");
        System.out.println("myBean1 = " + myBean1.getMessage());
        MyBean myBean2 = (MyBean) context.getBean("&myBean");
        System.out.println("myBean2 = " + myBean2.getMessage());
        System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
    }
}

/*
myBean1 = 通过FactoryBean.getObject()初始化实例
myBean2 = 通过构造方法初始化实例
myBean1.equals(myBean2) = false
*/

使用场景

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

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

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

FactoryBean 用来创建一类bean。比如你有一些同属鸟类的bean需要被创建,但是它们自己有各自的特点,你只需要把他们的特点注入FactoryBean中就可以生产出各种鸟类的实例。

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

三、区别

  • 他们两个都是个工厂,但FactoryBean本质上还是一个Bean,也归BeanFactory管理
  • BeanFactory是Spring容器的顶层接口,FactoryBean更类似于用户自定义的工厂接口。

你可能感兴趣的:(Spring源码分析,spring,java)