EmilKwan

导航

 

摘要

IoC(Inversion of Control,控制反转)原则,又可以称为DI(Dependency Injection,依赖注入),Bean的整个生命周期交由IoC容器控制,依赖注入能够使代码更加整洁及解耦。这里选取了IoC容器的Bean依赖注入、Bean的作用域和Bean的生命周期这几个方向进行讨论。

一、什么是IoC

IoC(Inversion of Control,控制反转)原则,又可以称为DI(Dependency Injection,依赖注入)。

在IoC设计原则中,由对象定义其依赖关系,对象实例由IoC容器(工厂模式)创建,并注入其依赖项。Bean的整个生命周期交由IoC容器控制,依赖注入能够使代码更加整洁及解耦。

BeanFactory和ApplicationContext

  • BeanFactory是IoC容器的核心接口,其提供了IoC的最基础功能
  • ApplicaitonContext是BeanFactory的子接口,其在BeanFactory接口的基础上增加了一些特性,包括:整合AOP、信息源(国际化)、事件发布、为应用提供应用级别的上下文信息

通常情况下应该使用ApplicationContext接口。

Table 1. Spring BeanFactory And ApplicationContext Feature Matrix.png

(Table 1. Spring BeanFactory And ApplicationContext Feature Matrix.png)

二、IoC容器概述

ApplicationContext接口体现了IoC容器和接口实例化、配置、组装Bean的能力。IoC容器通过读取配置的元数据信息来实现上述的功能的,这些元数据信息可以来源于XML、Java注解、Java代码。

Figure 2. The Spring IoC container

(Figure 2. The Spring IoC container)

如上图所示,你向IoC容器输入Bean对象(POJO)和元数据配置(描述依赖关系),IoC就可以为应用提供Bean实例使用了。

Bean的元数据信息配置

Bean的元数据信息配置,描述了Bean的属性及其依赖关系,来源于以下几点:

  • XML
  • 注解(Spring 2.5 开始支持)
  • Java代码(Spring 3.0 开始支持)

在Spring中使用BeanDefinition对象表示,具体的元数据配置信息如下:

  • 类名,通常是Bean的实际实现类
  • Bean的行为配置信息,用来表示Bean在容器内的行为,如:作用域、生命周期回调等
  • 当前Bean对其它Bean的依赖信息
  • 创建对象时的其它配置信息,如连接池中的连接数量
    以上的信息构建成不同BeanDefinition对象。

以下为使用XML来定义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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">   
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

IoC容器的实例化

IoC容器实例化的三个基本过程:

  • Resource定位:Bean的定义文件定位
  • 载入:将Resource定位好的资源载入到BeanDefinition
  • 注册:将BeanDefinition注册到容器中

使用IoC容器

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

三、IoC容器的一些讨论

这里选取了IoC容器的Bean依赖注入、Bean的作用域和Bean的生命周期这几个方向进行讨论。

1、Bean依赖注入

应用中,Bean不可能只有单独的一个Bean,现实中肯定会存在不同Bean之间的协作依赖。下面讨论的是IoC容器如何处理Bean之间的依赖问题。

依赖注入存在两种方式,分别是构造器注入和Setter注入。

IoC依赖注入过程如下:

  • The ApplicationContext is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified by XML, Java code, or annotations.
  • For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method (if you use that instead of a normal constructor). These dependencies are provided to the bean, when the bean is actually created.
  • Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.
  • Each property or constructor argument that is a value is converted from its specified format to the actual type of that property or constructor argument. By default, Spring can convert a value supplied in string format to all built-in types, such as int, long, String, boolean, and so forth.

处理循环依赖问题

  • Spring只能解决Setter方法注入的单例bean之间的循环依赖(注:即使用构造器注入,有可能会存在循环依赖的问题)
  • ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在获取ClassA的实例时,不等ClassA完成创建就将其曝光加入正在创建的bean缓存中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。

参考:24--Spring解决bean之间的循环依赖

2、Bean的作用域

Singleton

Bean的默认作用域,整个应用共享一份实例。

Figure 3. Spring Singleton Scope Bean

(Figure 3. Spring Singleton Scope Bean)

Prototype

该作用域每次请求Bean时都会新建一个Bean实例。

Figure 4. Spring Prototype Scope Bean

(Figure 4. Spring Prototype Scope Bean)

Request,Session,Application,WebSocket

The request, session, application, and websocket scopes are available only if you use a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext).

这几个类型的作用域仅限在Web应用中使用。

  • Request:每次Http请求对应一个Bean实例
  • Session:每个Http会话对应一个Bean实例
  • Application:每个ServletContext对应一个Bean实例
  • WebSocket:每个WebSocket对应一个Bean实例

自定义作用域

可以通过实现org.springframework.beans.factory.config.Scope接口来自定义作用域并使用BeanFactory实现类来对新作用域进行注册。

3、Bean的生命周期

Bean的生命周期回调

Bean的生命周期线索:实例化、初始化、使用、销毁

可以利用Bean来实现Spring的InitializingBean、DisposableBean和BeanPostProcessor等可以对Bean的生命周期进行回调处理。

Figure 5. Spring Bean的生命周期

(Figure 5. Spring Bean的生命周期)

参考资料

posted on 2019-05-28 00:01  EmileKwan  阅读(317)  评论(0编辑  收藏  举报