1 IOC 概念

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。Before,开发人员创建操作对象。Now,容器创建操作对象。控制权的转移带来的好处就是降低了业务对象之间的依赖程度,即实现了解耦。

依赖注入 (DI, Dependency Injection): 只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建、赋值、查找都由容器内部实现。Spring 框架使用依赖注入实现 IoC, spring 底层创建对象,使用的是反射机制。

为什么要使用 IoC: 目的就是减少对代码的改动,也能实现不同的功能。实现解耦合。

Java 创建对象的方式有:构造方法;反射;序列化;克隆;IoC:容器创建对象;动态代理。

IoC 体现 —— Servlet

  1. 创建类继承 HttpServelt
  2. web.xml 注册 Servlet
    <servlet-name> myservlet </servlet-name>
    <servelt-class>com.bjpwernode.controller.MyServlet1</servlet-class>
  3. 没有创建 Servlet 对象,没有 MyServlet myservlet = new MyServlet()
  4. Servlet 是 Tomcat 服务器它能你创建的。 Tomcat 也称为容器
    Tomcat 作为容器:里面存放的有 Servlet 对象,Listener ,Filter 对象

2 注入分类

Bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类:set 注入、构造注入。

注入方式描述
setter 方法注入因为方法可以命名,所以 setter 方法注入在描述性上要比构造方法注入好一些。
另外,setter 方法可以被继承,允许设置默认值,而且有良好的 IDE 支持。
缺点当然就是对象无法在构造完成后马上进入就绪状态。
构造方法注入这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。
缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。
而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。
而且在 Java 中,构造方法无法被继承,无法设置默认值。
对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。
接口注入从注入方式的使用上来说,接口注入是现在不甚提倡的一种方式,基本处于“退役状态”。
因为它强制被注入对象实现不必要的接口,带有侵入性。
而构造方法注入和 setter 方法注入则不需要如此。
从 Spring 4 开始已被废弃。

2.1 set 注入

set 注入也叫设值注入,是指通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。

简单数据类型

<property name = "Name" value = "">
该语句会调用类中的 setName 方法,并将 value 作为参数传入进去。Name 值为 email,就调用 setEmail 方法。也可以使用注解来设置 set 方法。

需要注意,仅仅会调用 setName 方法,对于方法内部如何实现不关心。即,set 方法不赋值,那么对应值就是 null。
类中没有 email 属性,但是有 setEmail 方法,也是会调用 setEmail 方法。但是,如果没有对应的 set 方法,那么会报错。

SetName 方法必须只有一个参数,且 value 值可以转换成传入参数类型。
name="age" 这种情况,编译器会去搜索 Student 类中是否含有 public void setAge(Object value) 方法。
如果没有,编译器会报错,name="age" 会标红,并推荐创建 public void setAge(String value) 方法。
如果 value 值不能转换为 set 方法中传入参数类型,如 char,编译器仍会报错,value 字段会标红。

<bean id="myStudent" class="com.bjpowernode.ba01.Student" >
	<property name="name" value="李四" /><!--setName("李四")-->
	<property name="age" value="22" /><!--setAge(21)-->
	<property name="email" value="lisi@qq.com" /><!--setEmail("lisi@qq.com")-->
</bean>

引用数据类型

<property name="school" ref="mySchool"/>
当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。
Ref 的值必须为某 bean 的 id 值。

下图中,先创建 Student 对象,再创建 School 对象。然后对 School 赋值,最后再对 Student 赋值。
先创建一个对象,然后检查所有 property。
如果没有 ref,那么直接赋值,然后创建下一个对象;否则根据 ref 创建引用对象,对引用对象进行赋值后再赋值源对象。

<bean id="myStudent" class="com.bjpowernode.ba02.Student" >
	<property name="name" value="李四" />
	<property name="age" value="26" />
	<!--引用类型-->
	<property name="school" ref="mySchool" /><!--setSchool(mySchool)-->
</bean>
 
<!--声明School对象-->
<bean id="mySchool" class="com.bjpowernode.ba02.School">
	<property name="name" value="北京大学"/>
	<property name="address" value="北京的海淀区" />
</bean>

2.2 构造注入

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。

<constructor-arg name="myage" value="20" />
name : 表示构造方法的形参名
index : 表示构造方法的参数的位置,下标从 0 开始。Name 和 index 都不存在时,标签顺序为参数顺序。
value :构造方法的形参类型是简单类型的,使用 value
ref :构造方法的形参类型是引用类型的,使用 ref

public Student(String myname,int myage, School mySchool){ }
<!--使用name属性实现构造注入-->
<bean id="myStudent" class="com.bjpowernode.ba03.Student" >
 <constructor-arg name="myage" value="20" />
 <constructor-arg name="mySchool" ref="myXueXiao" />
 <constructor-arg name="myname" value="周良"/>
</bean>
 
<!--使用index属性-->
<bean id="myStudent2" class="com.bjpowernode.ba03.Student">
 <constructor-arg index="1" value="22" />
 <constructor-arg index="0" value="李四" />
 <constructor-arg index="2" ref="myXueXiao" />
</bean>
 
<!--省略index-->
<bean id="myStudent3" class="com.bjpowernode.ba03.Student">
 <constructor-arg value="张强强" />
 <constructor-arg value="22" />
 <constructor-arg ref="myXueXiao" />
</bean>

3 注解与 XML 的对比

注解优点:

  • 方便
  • 直观
  • 高效(代码少,没有配置文件的书写那么复杂)

注解缺点:

  • 以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。

XML 优点:

  • 配置和代码是分离的
  • 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。

XML 缺点:

  • 编写麻烦,效率低,大型项目过于复杂。

4 IOC 作用

  • 管理对象的创建和依赖关系的维护。当对象的创建很复杂时,程序员维护是很头疼的。
  • 解耦,由容器去维护具体对象。
  • 托管了类的整个生命周期,涉及到创建对象、管理对象、装配对象等等。

5 IOC 优点

  • IOC 可以将代码量进一步降低。
  • 它使应用容易测试,单元测试不再需要单例和 JNDI 查找机制。
  • 最小的代价和最小的侵入性使松散耦合得以实现。
  • IOC 容器支持加载服务时的饿汉式初始化和懒加载。

6 Spring 的 IoC 支持哪些功能

  • 依赖注入
  • 依赖检查
  • 自动装配
  • 支持集合
  • 指定初始化方法和销毁方法
  • 支持回调某些方法(但是需要实现 Spring 接口,略有侵入)

其中,最重要的就是依赖注入,从 XML 的配置上说,即 ref 标签。对应 Spring RuntimeBeanReference 对象。

对于 IoC 来说,最重要的就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。