Spring框架入门(三):依赖注入和控制反转

依赖注入和控制反转概念

依赖注入(IoC) 和 控制反转(DI) 有什么关系呢?其实它们是同一个概念的不同角度描述。

依赖注入(Dependency injection)

是指应用在运行期,由外部容器(Spring容器)动态地将依赖对象注入到另一个对象中。

理解 DI 的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”

  • 谁依赖于谁:当然是某个容器管理对象依赖于 IoC 容器;“被注入对象的对象”依赖于“依赖对象”;
  • 为什么需要依赖:容器管理对象需要 IoC 容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是 IoC 容器注入某个对象,也就是注入“依赖对象”;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

控制反转(Inversion Of Control)

IoC 容器就是具有依赖注入功能的容器,IoC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中 new 相关的对象,应用程序由 IoC 容器进行组装。

在传统模式下通常有两种做法:

  • 原始做法: 调用者主动创建被依赖对象,然后再调用被依赖对象的方法。
  • 简单工厂模式: 调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后再调用被依赖对象的方法。

Spring容器的依赖注入

  • 设值注入:是指IoC容器通过成员变量的setter方法来注入被依赖对象。这种注入方式简单、直观,因而在Spring的依赖注入里大量使用。
  • 构造注入:利用构造器来设置依赖关系的方式,被称为构造注入。通过构造器参数对成员变量执行初始化,驱动Spring在底层以反射方式执行带指定参数的构造器。

注意: 建议采用设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他依赖关系的注入,则考虑采用设值注入。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 通过property属性注入(必须提供set方法) -->
<bean id="user" class="cn.sr.spring.javabean.User">
<property name="name" value="张三"></property>
<property name="password" value="123456"></property>
</bean>

<!-- 通过构造函数注入 -->
<bean id="user" class="cn.sr.spring.javabean.User">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="password" value="123456"></constructor-arg>
</bean>

//除此之外还可以注入集合类型的对象,List、Set、Map、Properties……

Spring容器中的Bean

对于开发者来说,开发者使用Spring框架主要是做两件事:①开发Bean;②配置Bean。对于Spring框架来说,它要做的就是根据配置文件来创建Bean实例,并调用Bean实例的方法完成”依赖注入”——这就是所谓IoC的本质。

容器中Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下五种作用域:

  • singleton: 单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。
  • prototype: 每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
  • request: 对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
  • session:该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。
  • global session: 每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效,同样只在Web应用中有效。

配置格式如下:

1
<bean id="…" class="…" scope="singleton"></bean>

如果不指定Bean的作用域,Spring默认使用singleton作用域。prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成果,就可以重复使用。因此,应该尽量避免将Bean设置成prototype作用域。

创建Bean的3种方式

  1. 使用构造器创建Bean实例:
    需要提供无参数的构造器,Spring对Bean实例的所有属性执行默认初始化,即所有的基本类型的值初始化为0或false;所有的引用类型的值初始化为null。
  2. 使用静态工厂方法创建Bean:
    class属性须指定静态工厂类,使用factory-method属性来指定静态工厂方法,如果静态工厂方法需要参数,则使用<constructor-arg…/>元素指定静态工厂方法的参数。
1
2
3
<bean id="user" class="cn.sr.spring.javabean.UserFactory" factory-mothod="getUser">
<constructor-arg name="name" value="张三"></constructor-arg>
</bean>
1
2
3
4
5
6
7
8
package cn.sr.spring.javabean;

public class UserFactory {
//对应factory-method
public static void getUser(String name) {
System.out.println("这是"+name);
}
}
  1. 使用实例工厂方法创建Bean:
  • factory-bean指定工厂实例
  • factory-method指定实例工厂的工厂方法。

若调用实例工厂方法时需要传入参数,则使用<constructor-arg…/>元素确定参数值。

1
2
3
4
5
<bean id="userFactory" class="cn.sr.spring.javabean.UserFactory" />
//此时getUser方法应为非静态
<bean id="user" factory-bean="userFactory" factory-mothod="getUser">
<constructor-arg name="name" value="张三"></constructor-arg>
</bean>

Bean的生命周期

这里简单的说明一下,对于作用域为prototype的Bean,Spring在仅仅负责创建Bean,每当容器创建Bean后就交给客户端的代码进行维护。但是对应singlton作用域的Bean,Spring会进行创建一次,然后整个容器中共享。所以Spring容器会负责进行跟踪其状态,负责依赖的注入和依赖实例的分配。


结尾

本文只是做简单介绍,个人理解方面偏多,更多有关于IOC和DI以及Bean对象的详解介绍请自行查找相关资料!


Spring框架入门(三):依赖注入和控制反转
https://www.srblog.cn/posts/2d0cab43/
作者
sr
发布于
2018年11月2日
许可协议