对象的创建一共有三种:自己创建,工厂模式创建,外部注入。对应三个动词: new , get , set
New --- 自己创建
Get --- 工厂模式
优点:实现了对象的统一创建,调用者无须关心对象创建的过程,只管从工厂中取得即可
Set--- 外部注入
Ioc 模式的优缺点
优点:
IOC 把对象放在了 XML 中定义,所以当我们需要换一个实现子类将会变得很简单(一般这样的对象都是实现于某种接口的),只要修改 XML 就可以了。
缺点:
<!-- [if !supportLists]-->l <!-- [endif]-->生成一个对象的步骤变的复杂了,对于不习惯这种方式的人,会觉得有些别扭和不直观。不过这种复杂都已经被容器实现了,对于使用现有的 IOC 容器进行应用开发的人员来说,使用确实相当简单。
<!-- [if !supportLists]-->l <!-- [endif]-->对象生成因为是使用反射编程,在效率上有些损耗。但相对于 ioc 提高的维护性和灵活性来说,这点损耗是微不足道的。
Spring 的 IOC 容器配置详解
依赖注入 DI(dependency injection) 模式
类型名称 | 类型名称 | 描述 | 项目 |
Type1 | 接口注入 | 服务需要实现专门的接口,通过接口,有对象提供这些服务。 | J2EE, |
Type2 | 构造注入 | 使依赖性以构造函数的形式提供,不以 javabean 属性的形式公开 | Spring |
Type3 | 设置注入 | 通过 javabean 的属性(例如 setter 方法)分配依赖性 | Spring |
1. <!-- [endif]-->type1 接口注入
我们开发一个 InjectUserDao 接口,它的用途是将一个 UserDao 实例注入到实现该接口的类中。
InjectUserDao 接口 代码如下:
- public interface InjectUserDao {
- public void setUserDao(UserDao userDao);
- }
- UserRegister需要容器为它注入一个UserDao的实例,则它必须实现InjectUserDao接口。UserRegister部分代码如下:
- public class UserRegister implements InjectUserDao{
- private UserDao userDao = null;//该对象实例由容器注入
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- // UserRegister的其它业务方法
- }
- 2. type2 构造注入
- public class SetStudent {
- private Problem problem;
- public Student(Problem problem) {
- this.problem = problem;
- }
- public void resolveProblem() {
- System.out.print(problem.displayMessage()+"\r");
- }
- }
public interface InjectUserDao { public void setUserDao(UserDao userDao);}UserRegister需要容器为它注入一个UserDao的实例,则它必须实现InjectUserDao接口。UserRegister部分代码如下:public class UserRegister implements InjectUserDao{ private UserDao userDao = null;//该对象实例由容器注入 public void setUserDao(UserDao userDao) { this.userDao = userDao; }// UserRegister的其它业务方法}2. type2 构造注入public class SetStudent { private Problem problem; public Student(Problem problem) { this.problem = problem;} public void resolveProblem() { System.out.print(problem.displayMessage()+"\r"); }}
对应的配置文件:
- <bean id="Problem" class="com.example1.Problem">
- <property name="title" >
- <value>problem title</value>
- </property>
- </bean>
- <bean id="Student" class="com.example1.Student">
- <constructor-arg ref="problem"></constructor-arg>
- </bean>
<bean id="Problem" class="com.example1.Problem"> <property name="title" > <value>problem title</value> </property> </bean> <bean id="Student" class="com.example1.Student"> <constructor-arg ref="problem"></constructor-arg> </bean>
3. type3 设值注入
- 3. type3 设值注入
- public class SetStudent {
- private Problem problem;
- public Problem getProblem() {
- return problem;
- }
- public void setProblem(Problem problem) {
- this.problem = problem;
- }
- public void resolveProblem() {
- System.out.print(problem.displayMessage()+"\r");
- }
- }
3. type3 设值注入public class SetStudent { private Problem problem; public Problem getProblem() { return problem; } public void setProblem(Problem problem) { this.problem = problem; } public void resolveProblem() { System.out.print(problem.displayMessage()+"\r");}}
对应的配置文件:
- <bean id="ProblemA" class="com.example1.ProblemA">
- <property name="title" >
- <value>problem title</value>
- </property>
- </bean>
- <bean id="ProblemB" class="com.example1.ProblemB">
- <property name="title" >
- <value>problem title</value>
- </property>
- </bean>
- <bean id="Student" class="com.example1.Student">
- <property name="problem" ref="ProblemA" />
- </bean>
- <bean id="StudentA" class="com.example1.StudentA">
- <property name="problem" ref="ProblemA" />
- </bean>
- <bean id="StudentB" class="com.example1.StudentB">
- <property name="problem" ref="ProblemC" />
- </bean>
<bean id="ProblemA" class="com.example1.ProblemA"> <property name="title" > <value>problem title</value> </property> </bean><bean id="ProblemB" class="com.example1.ProblemB"> <property name="title" > <value>problem title</value> </property> </bean> <bean id="Student" class="com.example1.Student"> <property name="problem" ref="ProblemA" /> </bean><bean id="StudentA" class="com.example1.StudentA"> <property name="problem" ref="ProblemA" /> </bean><bean id="StudentB" class="com.example1.StudentB"> <property name="problem" ref="ProblemC" /> </bean>
3 种类型的对比
接口注入模式具有侵入性,一般情况不使用,这里不加说明
2. <!-- [endif]-->type2 构造注入的优势
在构造期即创建一个完整、合法的对象 ” 。对于这条 java 设计原则, type2 无疑是最好的响应者
避免了烦琐的 setter 方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,更加易读。
由于没有 setter 方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后对组件“不变”的稳定状态,无须担心上层代码在调用过程中执行 setter 方法对组件依赖关系产生破坏,特别是对于 singleton 模式的组件而言。这可能对整个系统产生重大的影响。
由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。对于调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的层次清晰性提供了保证。
通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要。
设置注入的优势
对于习惯了传统的 javabean 开发的程序员来说,通过 setter 方法设定依赖关系显得更加直观。更加自然
如果依赖关系较为复杂, type2 模式的构造函数也会相当的庞大,这样 type3 模式往往更加简洁
对于某些第三方类库而言,可能要求我们的组件必须提供一个默认的构造函数。此时 type2 类型的依赖注入机制就体现出其局限性。