1 FactoryBean和BeanFactory区别
在 Spring 中,有两个接口:BeanFactory
和 FactoryBean
因为名字相近很容易被混淆。那他们之间有什么区别呢
1.1 BeanFactory
1.1.1 定义
BeanFactory
,以Factory
结尾,表示它是一个工厂类(接口), 它负责生产和管理bean
的一个工厂
BeanFactory
定义了IOC
容器的最基本形式,并提供了IOC
容器应遵守的的最基本
的接口,也就是Spring IOC
所遵守的最底层
和最基本
的编程规范。
在Spring
中,BeanFactory
是IOC
容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖
在Spring
代码中,BeanFactory
只是个接口,并不是IOC
容器的具体实现,但是Spring容器给出了很多种实现,比如:
- DefaultListableBeanFactory。
- XmlBeanFactory
- ApplicationContext
这些实现类从不同的维度对 beanFactory
进行了扩展。
1.1.2 beanFactory 源码
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {
factoryBean 的转义标识符。(具体用法后面有介绍)
String FACTORY_BEAN_PREFIX = "&";
根据 name 从容器中拿对应的 bean
Object getBean(String name) throws BeansException;
根据 name 和 type 从容器中拿对应的 bean,要对 bean 的类型做校验。
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
在容器中能否找到与 name 匹配的 bean 或者 beanDefinition。
boolean containsBean(String name);
判断 name 对对应的 bean 是不是 单例。
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
判断 name 对应的 bean 与指定的类型是否匹配。
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
根据 name 获取对应的 bean 的类型。
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
根据 name 获取对应 bean 的 别名。
String[] getAliases(String name);
}
1.1.3 BeanFactory体系结构
BeanFactory
的类体系结构
-
BeanFactory
: 接口位于类结构树的顶端, 它最主要的方法就是getBean(StringbeanName)
,该方法从容器中返回特定名称的Bean
,BeanFactory
的功能通过其他的接口得到不断扩展。 -
ListableBeanFactory
:该接口定义了访问容器中Bean
基本信息的若干方法,如查看Bean 的个数、获取某一类型Bean 的配置名、查看容器中是否包括某一Bean 等方法; -
HierarchicalBeanFactory
:父子级联IoC 容器的接口,子容器可以通过接口方法访问父容器; -
ConfigurableBeanFactory
:是一个重要的接口,增强了IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法; -
AutowireCapableBeanFactory
:定义了将容器中的Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法; -
SingletonBeanRegistry
:定义了允许在运行期间向容器注册单实例Bean 的方法; -
BeanDefinitionRegistry
:Spring
配置文件中每一个<bean>
节点元素在Spring 容器里都通过一个BeanDefinition
对象表示,它描述了Bean
的配置信息。而BeanDefinitionRegistry
接口提供了向容器手工注册BeanDefinition 对象的方法
1.1.4 使用场景
使用场景:
- 通过 名字或类型从容器中获取 bean。
- 判断容器中是否包含指定的 bean。
- 判断 bean 是不是单例。
1.2 FactoryBean
一般情况下,Spring
通过反射机制利用<bean>
的class
属性指定实现类实例化Bean
,但在某些情况下,实例化Bean
过程比较复杂,如果按照传统的方式,则需要在<bean>
中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring
为此提供了一个org.springframework.bean.factory.FactoryBean
的工厂类接口,用户可以通过实现该接口定制实例化Bean
的逻辑。
首先 FactoryBean
是一个 bean
,但它又不仅仅是个 bean
。它是一个可以 创建
或 修饰
其他对象的工厂 bean
,这跟设计模式中的工厂模式
或者装饰模式
很相似,它可以创建除自身以外的其他对象
1.2.1 FactoryBean源码
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
从 factory 中获取 bean
@Nullable
T getObject() throws Exception;
从 beanFactory 中获取类型
@Nullable
Class<?> getObjectType();
判断是单例?
default boolean isSingleton() {
return true;
}
}
从上面的接口可以看出,FactoryBean
有工厂的味道。也就是说,如果一个 A类实现了 FactoryBean
接口,那么 类A 就变成了 A工厂。根据 A 的名称获得的实际上是工厂调用 getObject()
返回的对象, 而不是 A工厂自己。如果想获得 A工厂自己的实例,需要添加 &
前缀。
1.2.2 用个demo解释下
这是一个简单的 FactoryBean 的使用
package com.example.demo.domian;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class MyFactoryBean implements FactoryBean {
// 保存一句话,用来区分不同的对象。
private String message;
// 无参构造器。
public MyFactoryBean() {
// 意思是:当前对象是 MyFactoryBean 的对象。
this.message = "object of myFactoryBeanSelf";
}
// 有参构造器。
public MyFactoryBean(String message) {
this.message = message;
}
// 获取 message。
public String getMessage() {
return this.message;
}
@Override
/**
* 这个方法在执行时创建了新的 MyFactoryBean 类型的对象。
* 这里继续沿用了 MyFactoryBean 类型,但是可以是别的类型
* 比如:Person、Car、等等。
*/
public Object getObject() throws Exception {
// 意思是:当前对象是 MyFactoryBean 的 getObject() 创建的。
return new MyFactoryBean("object from getObject() of MyFactoryBean");
}
@Override
public Class<?> getObjectType() {
return MyFactoryBean.class
}
}
测试内容很简单,MyFactoryBean
被 @Component
注解了,当启动SpringBoot
程序的时候,会为 MyFactoryBean
构建 bean
并且保存到容器中。我们要测试的就是用 类: MyFactoryBean
的 名字: myFactoryBean
去容器中拿 bean,看拿到的结果是什么?
package com.example.demo.domian;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = com.example.demo.domian.MyFactoryBean.class)
class MyFactoryBeanTest {
@Autowired
private ApplicationContext context;
@Test
public void test() {
// 第一次用 myFactoryBean 去拿。
MyFactoryBean myBean1 = (MyFactoryBean) context.getBean("myFactoryBean");
System.out.println("myBean1 = " + myBean1.getMessage());
// 第二次用 &myFactoryBean 去拿。
MyFactoryBean myBean2 = (MyFactoryBean) context.getBean("&myFactoryBean");
System.out.println("myBean2 = " + myBean2.getMessage());、
// 判断两次拿到的对象是不是一样的?
System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
}
}
测试结果:
myBean1 = object from getObject() of MyFactoryBean
myBean2 = object of myFactoryBeanSelf
myBean1.equals(myBean2) = false
结果很明显:
第一次使用 myFactoryBean
去容器中拿,实际上是容器中 MyFactorybean
的bean
调用了 getObject()
方法,并将结果返回。
第二次使用 &myFactoryBean
去容器中拿,才是真正拿到了 MyFactorybean
的 bean。
两次拿出来的对象当然是不一样的。
而 &
就是用于区分到底拿谁的的前缀。
1.2.3 使用场景
为什么需要 FactoryBean
,它的特殊功能是什么呢?
Spring
中FactoryBean
最大的应用场景是用在 AOP
中。我们都知道,AOP
实际上是 Spring
在运行是创建出来的代理对象,这个对象是在运行时才被创建的,而不是在启动时定义的,这与工厂方法模式是一致的。更生动地说,AOP
代理对象通过 java
反射机制在运行时创建代理对象,并根据业务需求将相应的方法编织到代理对象的目标方法中。Spring
中的 ProxyFactoryBean
就是干这事的。
因此,FactoryBean
提供了更灵活的实例化 bean
的方法。通过 FactoryBean
我们可以创建更复杂的 bean
1.3 区别总结
-
BeanFactory
:IOC
容器,并且提供方法支持外部程序对这些bean
的访问,在程序启动时 根据传入的参数产生各种类型的bean
,并添加到IOC
容器(实现BeanFactory
接口的类) 的singletonObject
属性中。 -
FactoryBean
: 首先是个bean
,也存放在BeanFactory
中。它具有工厂方法的功能,在程序运行中 产生指定(一种)类型的bean
,并添加到了IOC
容器中的factoryBeanObjectCache
属性中。
所以,这两种方式创建的bean
都是被spring
容器管理的 -
BeanFactory
和FactoryBean
都可以用来创建对象,只不过创建的流程和方式不同- 当使用
BeanFactory
的时候,必须要严格的遵守bean
的生命周期,经过一系列繁杂的步骤之后可以创建出单例对象,是流水线式的创建过程 -
FactoryBean
是用户可以自定义bean
对象的创建流程,不需要按照bean
的生命周期来创建
- 当使用
转载于:https://blog.csdn.net/yy_diego/article/details/115710104
2 BeanFactory和ApplicationContext区别
BeanFactory
已经在上面说过了,下面就不再赘述了
2.1 ApplicationContext
2.1.1 和BeanFactory关系
BeanFactory
和ApplicationContext
接口及其子类图
2.1.2 定义
继承了BeanFactory
接口,拥有BeanFactory
的全部功能,并且扩展了很多高级特性,每次容器启动时就会创建所有的对象。
创建ApplicationContext
的方法:
- 从类路径下加载配置文件:
ClassPathXmlApplicationContext("applicationContext.xml");
- 从硬盘绝对路径下加载配置文件:
FileSystemXmlApplicationContext(“d:/xxx/yyy/xxx”);
2.1.3 ApplicationContext类体系结构
Bean
工厂(com.springframework.beans.factory.BeanFactory
)是Spring
框架最核心的接口,它提供了高级IOC
的配置机制。
应用上下文(com.springframework.context.ApplicationContext
)建立在BeanFactory
基础之上。
几乎所有的应用场合我们都直接使用ApplicationContext
而非底层的BeanFactory
ApplicationContext
由BeanFactory
派生而来,提供了更多面向实际应用的功能。在BeanFactory
中,很多功能需要以编程的方式实现,而在ApplicationContext
中则可以通过配置的方式实现。
ApplicationContext
的主要实现类是ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。
核心接口包括:
-
ApplicationEventPublisher
:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。实现了ApplicationListener
事件监听接口的Bean
可以接收到容器事件, 并对事件进行响应处理。在ApplicationContext
抽象实现类AbstractApplicationContext
中,我们可以发现存在一个ApplicationEventMulticaster
,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听者 -
MessageSource
:为应用提供i18n
国际化消息访问的功能; -
ResourcePatternResolver
: 所有ApplicationContext
实现类都实现了类似于PathMatchingResourcePatternResolver
的功能,可以通过带前缀的Ant 风格的资源文件路径装载Spring 的配置文件。 -
LifeCycle
:该接口是Spring 2.0 加入的,该接口提供了start()
和stop()
两个方法,主要用于控制异步处理过程。在具体使用时,该接口同时被ApplicationContext
实现及具体Bean
实现,ApplicationContext
会将start/stop
的信息传递给容器中所有实现了该接口的Bean
,以达到管理和控制JMX、任务调度等目的。 -
ConfigurableApplicationContext
扩展于ApplicationContext
,它新增加了两个主要的方法:refresh()
和close()
,让ApplicationContext
具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用refresh()
即可启动应用上下文,在已经启动的状态下,调用refresh()
则清除缓存并重新装载配置信息,而调用close()
则可关闭应用上下文。这些接口方法为容器的控制管理带来了便利.
代码示例:
ApplicationContext ctx =new ClassPathXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx =new FileSystemXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"conf/beans1.xml","conf/beans2.xml"});
2.2 结论
早期的电脑性能低,内存小,所以Spring
容器的容量不足,不能将所有的对象全部创建好放入容器,所以使用的是BeanFactory
,需要某个对象时,再进行创建,随着电脑硬件的发展,内存越来越大,所以Spring
框架引入了ApplicationContext
,将所有的对象都创建好,放入容器,使用哪个对象,从容器中取得即可。
所以,web开发中,使用applicationContext
. 在资源匮乏的环境可以使用BeanFactory
因此,ApplicationContext
的初始化和BeanFactory
有一个重大的区别:BeanFactory
在初始化容器时,并未实例化Bean
,直到第一次访问某个Bean
时才实例目标Bean
;而ApplicationContext
则在初始化应用上下文时就实例化所有单实例的Bean
ApplicationContext
接口,它由BeanFactory
接口派生而来,ApplicationContext
包含BeanFactory
的所有功能,通常建议比BeanFactory
优先
转载于:https://blog.csdn.net/qq_20757489/article/details/88543252