1.概述

​ Spring 框架带有两个 IOC 容器—— BeanFactoryApplicationContextBeanFactory是 IOC 容器的最基本版本,ApplicationContext扩展了BeanFactory的特性。

​ Spring容器最基本的接口就是BeanFactory。BeanFactory负责配置、创建、管理Bean,它有一个子接口ApplicationContext,也被称为Spring上下文,容器同时还管理着Bean和Bean之间的依赖关系。

​ spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说,DefaultListableBeanFactory 是整个spring ioc的始祖。

1.1BeanFactory

在这里插入图片描述

接口介绍

1.BeanFactory接口:
 是Spring bean容器的根接口,提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean 别名的方法 。它最主要的方法就是getBean(String beanName)。
2.BeanFactory的三个子接口:

  • HierarchicalBeanFactory:提供父容器的访问功能
  • ListableBeanFactory:提供了批量获取Bean的方法
  • AutowireCapableBeanFactory:在BeanFactory基础上实现对已存在实例的管理
    3.ConfigurableBeanFactory:
    主要单例bean的注册,生成实例,以及统计单例bean
    4.ConfigurableListableBeanFactory:
    继承了上述的所有接口,增加了其他功能:比如类加载器,类型转化,属性编辑器,BeanPostProcessor,作用域,bean定义,处理bean依赖关系, bean如何销毁…
    5.实现类DefaultListableBeanFactory详细介绍:
    实现了ConfigurableListableBeanFactory,实现上述BeanFactory所有功能。它还可以注册BeanDefinition
    接口详细介绍请参考:揭秘BeanFactory

接口具体说明

1、BeanFactory作为一个主接口不继承任何接口,暂且称为一级接口
  2、有3个子接口继承了它,进行功能上的增强。这3个子接口称为二级接口
  3、ConfigurableBeanFactory可以被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry
  4、ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的所有接口,无所不包,称为四级接口。(这4级接口是BeanFactory的基本接口体系。继续,下面是继承关系的2个抽象类和2个实现类)
  5、AbstractBeanFactory作为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能。
  6、AbstractAutowireCapableBeanFactory同样是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory
  7、DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了最强大的四级接口ConfigurableListableBeanFactory,并实现了一个外来接口BeanDefinitionRegistry,它并非抽象类。
  8、最后是最强大的XmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使自己更强大。

1、BeanFactory作为一个主接口不继承任何接口,暂且称为一级接口
  2、有3个子接口继承了它,进行功能上的增强。这3个子接口称为二级接口
  3、ConfigurableBeanFactory可以被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry
  4、ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的所有接口,无所不包,称为四级接口
  (这4级接口是BeanFactory的基本接口体系。继续,下面是继承关系的2个抽象类和2个实现类:)
  5、AbstractBeanFactory作为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能。
  6、AbstractAutowireCapableBeanFactory同样是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory
  7、DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了最强大的四级接口ConfigurableListableBeanFactory,并实现了一个外来接口BeanDefinitionRegistry,它并非抽象类。
  8、最后是最强大的XmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使自己更强大。

源码说明

public interface BeanFactory {

    /**
     * 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory
     */
    String FACTORY_BEAN_PREFIX = "&";
    /*
     * 四个不同形式的getBean方法,获取实例
     */
    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;
 	// 根据名称判断bean是否存在
    boolean containsBean(String name);
	// 是否为单实例Bean
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	// 是否为原型(多实例)
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	// 名称、类型是否匹配
    boolean isTypeMatch(String name, Class<?> targetType)
            throws NoSuchBeanDefinitionException;
	// 获取类型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	// 根据实例的名字获取实例的别名
    String[] getAliases(String name);
}

2.延迟加载与立即加载

BeanFactory按需加载 bean,而ApplicationContext在启动时加载所有 bean

因此, BeanFactory与 ApplicationContext相比是轻量级的。例子如下:

2.1.使用BeanFactory延迟加载

假设我们有一个名为Student的单例 bean类,它有一个方法:

public class Student {
    public static boolean isBeanInstantiated = false;
    public void postConstruct() {
        setBeanInstantiated(true);
    }
    //standard setters and getters
}

我们将 在BeanFactory配置文件ioc-container-difference-example.xml中将*postConstruct()*方法定义为init-method

<bean id="student" class="com.baeldung.ioccontainer.bean.Student" 
      init-method="postConstruct"/>

现在,让我们编写一个测试用例,创建一个BeanFactory来检查它是否加载了Student bean:

@Test
public void whenBFInitialized_thenStudentNotInitialized() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    BeanFactory factory = new XmlBeanFactory(res);
    
    assertFalse(Student.isBeanInstantiated());
}

在这里,Student对象没有被初始化。换句话说,只有BeanFactory被初始化只有当我们显式调用getBean()方法时,我们的BeanFactory中定义的 bean才会被加载*。*

让我们检查一下我们手动调用getBean()方法的Student bean的初始化:

@Test
public void whenBFInitialized_thenStudentInitialized() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    BeanFactory factory = new XmlBeanFactory(res);
    Student student = (Student) factory.getBean("student");

    assertTrue(Student.isBeanInstantiated());
}

在这里,Student bean 加载成功。因此,BeanFactory仅在需要时才加载 bean。

2.2. 使用ApplicationContext预加载

现在,让我们使用ApplicationContext代替BeanFactory

我们将只定义*ApplicationContext,*它会通过使用立即加载策略立即加载所有 bean:

@Test
public void whenAppContInitialized_thenStudentInitialized() {
    ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
    
    assertTrue(Student.isBeanInstantiated());
}

在这里,即使我们没有调用getBean()方法,也会创建Student对象。

ApplicationContext被认为是一个沉重的 IOC 容器,因为它的预先加载策略会在启动时加载所有 bean。相比之下, BeanFactory是轻量级的,可以在内存受限的系统中使用。尽管如此,我们最常用的仍是ApplicationContext

3.ApplicationContext

ApplicationContext以更加面向框架的风格增强了BeanFactory,并提供了一些适用于企业应用程序的特性。

  • 默认初始化所有的Singleton,也可以通过配置取消预初始化。

  • 继承MessageSource,因此支持国际化。

  • 资源访问,比如访问URL和文件。

  • 事件传播特性,即支持aop特性。

  • 同时加载多个配置文件。

  • 以声明式方式启动并创建Spring容器。

ApplicationContext:是IOC容器另一个重要接口, 它继承了BeanFactory的基本功能, 同时也继承了容器的高级功能,如:MessageSource(国际化资源接口)、ResourceLoader(资源加载接口)、ApplicationEventPublisher(应用事件发布接口)等。

4. BeanFactoryPostProcessor和BeanPostProcessor的自动注册

ApplicationContext在启动时会**自动注册BeanFactoryPostProcessor和****BeanPostProcessor 。**另一方面,BeanFactory不会自动注册这些接口。

4.1.在BeanFactory中注册

为了理解,让我们编写两个类。

首先,我们有CustomBeanFactoryPostProcessor类,它实现了BeanFactoryPostProcessor

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    private static boolean isBeanFactoryPostProcessorRegistered = false;
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){
        setBeanFactoryPostProcessorRegistered(true);
    }

    // standard setters and getters
}

在这里,我们重写了postProcessBeanFactory() 方法来检查它的注册。

其次,我们有另一个类CustomBeanPostProcessor,它实现了BeanPostProcessor

public class CustomBeanPostProcessor implements BeanPostProcessor {
    private static boolean isBeanPostProcessorRegistered = false;
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName){
        setBeanPostProcessorRegistered(true);
        return bean;
    }

    //standard setters and getters
}

在这里,我们重写了postProcessBeforeInitialization()方法来检查它的注册。

此外,我们在ioc-container-difference-example.xml配置文件中配置了这两个类:

<bean id="customBeanPostProcessor" 
  class="com.baeldung.ioccontainer.bean.CustomBeanPostProcessor" />
<bean id="customBeanFactoryPostProcessor" 
  class="com.baeldung.ioccontainer.bean.CustomBeanFactoryPostProcessor" />

让我们看一个测试用例来检查这两个类是否在启动时自动注册:

@Test
public void whenBFInitialized_thenBFPProcessorAndBPProcessorNotRegAutomatically() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    ConfigurableListableBeanFactory factory = new XmlBeanFactory(res);

    assertFalse(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
    assertFalse(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}

从我们的测试中可以看出,自动注册没有发生

现在,让我们看一个在BeanFactory中手动添加它们的测试用例:

@Test
public void whenBFPostProcessorAndBPProcessorRegisteredManually_thenReturnTrue() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    ConfigurableListableBeanFactory factory = new XmlBeanFactory(res);

    CustomBeanFactoryPostProcessor beanFactoryPostProcessor 
      = new CustomBeanFactoryPostProcessor();
    beanFactoryPostProcessor.postProcessBeanFactory(factory);
    assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());

    CustomBeanPostProcessor beanPostProcessor = new CustomBeanPostProcessor();
    factory.addBeanPostProcessor(beanPostProcessor);
    Student student = (Student) factory.getBean("student");
    assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}

在这里,我们使用postProcessBeanFactory()方法注册CustomBeanFactoryPostProcessoraddBeanPostProcessor()方法注册CustomBeanPostProcessor。在这种情况下,他们俩都成功注册。

4.2. 在ApplicationContext中注册

正如我们前面提到的,ApplicationContext会自动注册这两个类,而无需编写额外的代码。

让我们在单元测试中验证此行为:

@Test
public void whenAppContInitialized_thenBFPostProcessorAndBPostProcessorRegisteredAutomatically() {
    ApplicationContext context 
      = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");

    assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
    assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}

如我们所见,在这种情况下**,两个类的自动注册都是成功的。**

因此,始终建议使用ApplicationContext,因为 Spring 2.0(及更高版本)大量使用BeanPostProcessor。

还值得注意的是,如果您使用的是普通的BeanFactory,那么事务和 AOP 等功能将不会生效(至少在不编写额外代码行的情况下不会生效)。这可能会导致混淆,因为配置看起来没有任何问题。

5. 结论

  1. BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。

  2. ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:
    a. 国际化支持
    b. 资源访问:Resource rs = ctx. getResource(“classpath:config.properties”), “file:c:/config.properties”
    c. 事件传递:通过实现ApplicationContextAware接口。

  3. 常用的获取ApplicationContext

FileSystemXmlApplicationContext:从文件系统或者url指定的xml配置文件创建,参数为配置文件名或文件名数组,有相对路径与绝对路径。

ApplicationContext factory=new FileSystemXmlApplicationContext("src/applicationContext.xml");
ApplicationContext factory=new FileSystemXmlApplicationContext("E:/Workspaces/MyEclipse 8.5/Hello/src/applicationContext.xml");

ClassPathXmlApplicationContext:从classpath的xml配置文件创建,可以从jar包中读取配置文件。ClassPathXmlApplicationContext 编译路径总有三种方式:

ApplicationContext factory = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml"); 
ApplicationContext factory = new ClassPathXmlApplicationContext("file:E:/Workspaces/MyEclipse 8.5/Hello/src/applicationContext.xml");

XmlWebApplicationContext:从web应用的根目录读取配置文件,需要先在web.xml中配置,可以配置监听器或者servlet来实现

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

这两种方式都默认配置文件为web-inf/applicationContext.xml,也可使用context-param指定配置文件

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myApplicationContext.xml</param-value>
</context-param>
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐