BeanFactory和FactoryBean的区别
BeanFactory的介绍:
官网对于BeanFactory的解释:
下面给出官网文档截图:
英文不好的同学,看着头疼吧?没关系,下面有我粗略的中文翻译(有不正确的地方敬请提出):
1.16 The BeanFactory
BeanFactory为spring的IoC功能提供了基础,但是它只能直接与其他第三方框架集成使用,并且对于大多数Spring用户来说,它已成为历史。
BeanFactory和它有关的接口,如BeanFactoryAward、InitializingBean、DisposableBean,仍然存在于Spring中,目的是与大量Spring集成的第三方框架兼容。
通常第三方组件不能使用更现代化的组件,例如@PostConstruction 或者@Predestroy,以避免对JSR-250的依赖。
本节为区分BeanFactory和ApplicationContext提供额外的背景,并介绍如何通过典型的单例查找直接访问IoC容器。
1.16.1 BeanFactory or ApplicationContext?
推荐使用ApplicationContext除非你必须弃用它。
因为ApplicationContext包括了所有BeanFactory的功能,除非一些情况下,比如在资源受限设备上运行的嵌入式应用,内存消化可能很关键,一些额外的KB字节可能会产生影响,才推荐使用BeanFactory。
然而,对于大多数典型的企业应用和系统来讲,ApplicationContext就是您想使用的了。
Spring大量使用BeanPostProcessor扩展点(用于代理等)。
如果你只使用普通的BeanFactory,那么大量的支持功能将会失效(如AOP和事务),至少使用这些功能您并不需要多余步骤。在这种情况下你可能会觉得困惑,因为配置实际上并没有问题,而功能不生效。
上述是Spring官网文档对于BeanFactory的介绍,可能我翻译不是很恰当,或者本身Spring官网讲的就比较晦涩难懂,我总结一下他想表达的意思,分别如下两点:
1)BeanFactory为Spring提供了基础的功能,但是现在大多数已经弃用了,为了保持与第三方框架的兼容性,Spring对它作了保留。
2)在一般企业级开发中,推荐使用ApplicationContext而不是BeanFactory,如果你必须使用BeanFactory,那么有一些支持的功能将不生效,比如AOP和事务之类的。
并且给出示例代码:
上述代码,如果看过IoC初始化文章的,应该很熟悉,它在ClassPathXmlApplicationContext类中集成了上述操作。
代码片段1,指如果要使用一个Bean的后置处理器,需要创建一个DefaultListableBeanFactory对象,用来存储跟Xml文件对应的BeanDefinition对象。然后创建一个自定义后置处理类,并设置到工厂中。
代码片段2,同样创建DefaultListableBeanFactory对象,并需要创建XML配置文件的解析对象XmlBeanDefinitionReader,然后执行加载过程,将配置文件读入,解析。
上文讲述了两个东西,一个是BeanFactory,一个是ApplicationContext。
下面看看两者的关系:
由上图可知,ApplicationContext与BeanFactory是父子关系。难怪Spring官网说,ApplicationContext拥有所有BeanFactory的功能。
BeanFactory拥有哪些方法呢:
从截图得知,其实BeanFactory的方法比较的简单,下面介绍方法的意义:
getBean获取Bean实例,也是依赖注入的入口;
containsBean判断容器是否存在输入的该Bean;
isSingleton判断容器中,输入的BeanName是否为单例;
总结:BeanFactory是一个IoC顶层的接口,定义了IoC容器的基础模型,提供getBean的总入口。其由ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、XMLWebApplicationContext、DefaultListableBeanFactory等提供相应的实现。
FactoryBean的介绍
FactoryBean本身是一种Bean,一种用来创建实例的工厂Bean,spring本身其实就提供了许多FactoryBean的实现。下面将通过一个例子,讲述FactoryBean的实际作用。
首先定义我们需要生产的Bean,Dog.java:
public class Dog {
private String name;
private int age;
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;
}
}
接下来,我们定义一个生产够的工厂DogFactoryBean,其实现了FactoryBean接口,DogFactoryBean.java:
public class DogFactoryBean implements FactoryBean<Dog> {
private String name;
private int age;
@Override
public Dog getObject() throws Exception {
Dog dog = new Dog();
dog.setAge(this.age);
dog.setName(this.name);
return dog;
}
@Override
public Class<?> getObjectType() {
return Dog.class;
}
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;
}
}
配置工厂Bean的配置文件:
<?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-3.1.xsd">
<bean id="dog" class="com.example.demo.test.factory.DogFactoryBean">
<property name="age" value="1"/>
<property name="name" value="tom"/>
</bean>
</beans>
测试方法:
//FactoryBean的测试
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context2.xml");
//使用&,获取工厂bean本身
Object factoryBean = context.getBean("&dog");
//不使用&,则获取工厂生产的bean
Object dog = context.getBean("dog");
System.out.println("dogFactoryBean:" + factoryBean);
System.out.printf("dog:" + dog);
}
输出结果:
从以上代码可以看出来,FactoryBean就是一个工厂,但其本身又是一个由IoC容器管理的bean,可以通过加&符号的方式获取其本身对象。
总结:其实IoC容器有两种类型的bean,一种是普通bean,就是我们经常使用bean,平时用到的controller、service、dao都是普通bean的一种。另外一种叫工厂bean,这种bean主要用来创建bean的。如果还是不能够理解,建议查看IoC源码AbstraceBeanFactory.getObjectForBeanInstance方法。可以了解其在spring中是如何被使用的。
详情请参考:getObjectForBeanInstance(instance,name,beanName,mbd)
结论
因此,当面试官问你,BeanFactory和FactoryBean的区别的时候,你可以这样子回答:
BeanFactory是IoC容器顶层的一个接口,该接口声明了一些IoC容器应该具备的基础功能,其主要负责对bean的创建,访问等工作。而FactoryBean是一种特殊的Bean,是一个抽象工厂,可以用来生产普通的bean对象(可以自定义其生产方式)。通过+&符号可以获取其对象本身。