1 IOC 概念
控制反转(IoC,Inversion of Control),是一个概念,是一种思想。Before,开发人员创建操作对象。Now,容器创建操作对象。控制权的转移带来的好处就是降低了业务对象之间的依赖程度,即实现了解耦。
依赖注入 (DI, Dependency Injection): 只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建、赋值、查找都由容器内部实现。Spring 框架使用依赖注入实现 IoC, spring 底层创建对象,使用的是反射机制。
为什么要使用 IoC: 目的就是减少对代码的改动,也能实现不同的功能。实现解耦合。
Java 创建对象的方式有:构造方法;反射;序列化;克隆;IoC:容器创建对象;动态代理。
IoC 体现 —— Servlet
- 创建类继承
HttpServelt - 在
web.xml注册 Servlet<servlet-name> myservlet </servlet-name> <servelt-class>com.bjpwernode.controller.MyServlet1</servlet-class> - 没有创建 Servlet 对象,没有
MyServlet myservlet = new MyServlet() - 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 的依赖注入。