IoC的全称是Inversion of Control,中文称为控制反转, Martin Flower由根据它创造了一个新词:Dependency Injection,中文称为依赖注入。这两个词讲的是一回事儿。

控制反转模式(也称作依赖性介入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。

容器 (在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。IoC的实质是如何管理对象,传统意义上我们使用new方式来创建对象,但在企业应用开发的过程中,大量的

对象创建都在程序中维护很容易造成资源浪费,并且不利于程序的扩展。

实现IoC通常有三种方式:

1)利用接口或者继承,一般以接口较多。这种实现方式和我们平时提到的lazy load有异曲同工之妙。

2)构造函数注入。

3)属性注入。

IoC简单示例

  我们先来定义一个简单的接口和实现:

 1 public interface UserDao {
2 void save();
3 }
4
5 public class UserDaoImpl implements UserDao
6 {
7
8 public void save() {
9 System.out.println("save() is called.");
10 }
11
12 }

  然后是在classpath下创建一个beans.xml文件(这个文件名不是必须这样的):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl"/>
</beans>

  接下来是测试代码:

1 private static void test1()
2 {
3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml");
4 UserDao userDao = (UserDao)ctx.getBean("userDaoImpl");
5 userDao.save();
6 }

  输出结果如下:

save() is called.

  我们还可以通过工厂方式来创建对象。

  通过静态工厂创建Bean

  添加一个类,如下:

1 public class UserDaoFactory {
2
3 public static UserDao getUserDao()
4 {
5 return new UserDaoImpl();
6 }
7 }

  在beans.xml中,添加如下内容:

1 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoFactory" factory-method = "getUserDao"/>

  测试代码和执行结果和上面类似,不再赘述。

  通过实例工厂创建Bean

  添加如下类:

1 public class UserDaoFactory2
2 {
3 public UserDao getUserDao()
4 {
5 return new UserDaoImpl();
6 }
7 }

  这个类和UserDaoFactory唯一的区别是这里的getUserDao是实例方法,而不是静态方法。

  在beans.xml中追加如下内容:

1 <bean id="factory" class="sample.spring.ioc.UserDaoFactory2"/>
2 <bean id="userDaoImpl3" factory-bean="factory" factory-method="getUserDao"/>

  测试方法和结果同上。

  对象的生命周期

  我们可以通过设置bean节点的scope属性来控制对象的声明周期,它包含两个可选值:

  1)singleton,表明系统中对于同一个对象,只保留一个实例。

  2)prototype,表明系统中每次获取bean时,都新建一个对象。

  我们修改beans.xml文件:

1 <bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl" scope="singleton"/>
2 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoImpl" scope="prototype"/>

  这两个bean指向同一个类型,但是scope的设置不同。

  下面是测试方法:

 1 private static void scopeTest()
2 {
3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/scope.xml");
4 System.out.println("=====Singleton test=====");
5 UserDao userDao1A = (UserDao)ctx.getBean("userDaoImpl");
6 UserDao userDao1B = (UserDao)ctx.getBean("userDaoImpl");
7 System.out.println("userDao1A == userDao1B:" + (userDao1A==userDao1B));
8 System.out.println("=====Prototype test=====");
9 UserDao userDao2A = (UserDao)ctx.getBean("userDaoImpl2");
10 UserDao userDao2B = (UserDao)ctx.getBean("userDaoImpl2");
11 System.out.println("userDao2A == userDao2B:" + (userDao2A==userDao2B));
12 }

  执行结果如下:

=====Singleton test=====
userDao1A == userDao1B:true
=====Prototype test=====
userDao2A == userDao2B:false

  如何设置对象属性

  上面的示例中,我们的对象中没有包含属性,对于业务对象来说,这一般是不现实。实际中的对象或多或少都会有一些属性。

  Spring支持两种方式对属性赋值:set方式和构造函数。

  下面我们会分别描述两种方式,但首先我们需要展示业务对象:

public class UserServiceBean
2 {
3 private int userID;
4 private String userName;
5 private UserDao userDao;
6 private List<String> hobbies;
7 private Map<String, Integer> scores;
8
9 public UserServiceBean(int userID, String userName, UserDao userDao, List hobbies,Map scores)
10 {
11 this.userID = userID;
12 this.userName = userName;
13 this.userDao = userDao;
14 this.hobbies = hobbies;
15 this.scores = scores;
16 }
17
18 public UserServiceBean(){}
19
20 public void setUserID(int userID) {
21 this.userID = userID;
22 }
23 public int getUserID() {
24 return userID;
25 }
26 public void setUserName(String userName) {
27 this.userName = userName;
28 }
29 public String getUserName() {
30 return userName;
31 }
32 public void setUserDao(UserDao userDao) {
33 this.userDao = userDao;
34 }
35 public UserDao getUserDao() {
36 return userDao;
37 }
38 public void setHobbies(List<String> hobbies) {
39 this.hobbies = hobbies;
40 }
41 public List<String> getHobbies() {
42 return hobbies;
43 }
44 public void setScores(Map<String, Integer> scores) {
45 this.scores = scores;
46 }
47 public Map<String, Integer> getScores() {
48 return scores;
49 }
50 }

  这是一个典型的学生信息,包括学号、姓名、爱好和成绩。

  通过Set方式为对象属性赋值

  我们在beans.xml中追加如内容:

 1 <bean id="userService" class="sample.spring.ioc.UserServiceBean">
2 <property name="userID" value="1"/>
3 <property name="userName" value="张三"/>
4 <property name="userDao" ref="userDaoImpl"/>
5 <property name="hobbies">
6 <list>
7 <value>羽毛球</value>
8 <value>看电影</value>
9 <value>弹吉他</value>
10 </list>
11 </property>
12 <property name="scores">
13 <map>
14 <entry key="数据结构" value="90"/>
15 <entry key="编译原理" value="85"/>
16 <entry key="离散数学" value="82"/>
17 </map>
18 </property>
19 </bean>

  上面是典型的为属性赋值的示例,其中属性不仅包括简单属性(整数、字符串),也包含了复杂属性(List、Map),还有其他的bean。

  下面是测试代码:

 1 private static void propertyTest1()
2 {
3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml");
4 UserServiceBean userService = (UserServiceBean)ctx.getBean("userService");
5 printUserService(userService);
6 }
7
8 private static void printUserService(UserServiceBean userService)
9 {
10 System.out.println("编号:" + userService.getUserID());
11 System.out.println("姓名:" + userService.getUserName());
12 System.out.println("爱好:");
13 for(String hobby:userService.getHobbies())
14 {
15 System.out.println(hobby);
16 }
17 System.out.println("学习成绩:");
18 for(Entry<String,Integer> entry:userService.getScores().entrySet())
19 {
20 System.out.println(entry.getKey() + "\t" + entry.getValue());
21 }
22 userService.getUserDao().save();
23 }

  输出结果如下:

编号:1
姓名:张三
爱好:
羽毛球
看电影
弹吉他
学习成绩:
数据结构 90
编译原理 85
离散数学 82
save() is called.

  通过构造函数为对象属性赋值

  我们也可以通过构造函数来为对象赋值,在上面定义UserServiceBean时,我们已经添加了一个构造函数。下面来看beans.xml中的配置:

 1 <bean id="userService2" class="sample.spring.ioc.UserServiceBean">
2 <constructor-arg index="0" value="1"/>
3 <constructor-arg index="1" value="张三"/>
4 <constructor-arg index="2" ref="userDaoImpl"/>
5 <constructor-arg index="3">
6 <list>
7 <value>羽毛球</value>
8 <value>看电影</value>
9 <value>弹吉他</value>
10 </list>
11 </constructor-arg>
12 <constructor-arg index="4">
13 <map>
14 <entry key="数据结构" value="90"/>
15 <entry key="编译原理" value="85"/>
16 <entry key="离散数学" value="82"/>
17 </map>
18 </constructor-arg>
19 </bean>

  测试代码和输出结果同上。

  需要注意:我们定义的业务对象应该保留默认的构造函数。

  使用Annotation来定位Bean

  在Spring中,除了在xml配置文件中定义对象,我们还可以使用Annotation来定位,这位我们提供了很大的方便。

  这里我们使用的Annotation主要包括:@Resource/@Autowried/@Qualifier。

  来看下面的示例:

1 public class UserServiceBean2
2 {
3 private String userID;
4 private String userName;
5 @Resource(name="userDaoImpl")
6 private UserDao userDao1;
7 private UserDao userDao2;
8
9 @Autowired(required=false)
10 @Qualifier("userDaoImpl")
11 private UserDao userDao3;
12
13 @Autowired(required=false)
14 @Qualifier("userDaoImpl3")
15 private UserDao userDao4;
16
17 public void setUserID(String userID) {
18 this.userID = userID;
19 }
20 public String getUserID() {
21 return userID;
22 }
23 public void setUserName(String userName) {
24 this.userName = userName;
25 }
26 public String getUserName() {
27 return userName;
28 }
29 @Resource
30 public void setUserDao2(UserDao userDao2) {
31 this.userDao2 = userDao2;
32 }
33 public UserDao getUserDao2() {
34 return userDao2;
35 }
36
37 public void test()
38 {
39 userDao1.save();
40 userDao2.save();
41 System.out.println(userDao3.getClass().getName());
42 userDao3.save();
43 }
44 }

测试方法:

1 private static void annotationTest()
2 {
3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/annotation.xml");
4 UserServiceBean2 userService = (UserServiceBean2)ctx.getBean("userService");
5
6 userService.test();
7 }

  输出结果如下:

save() is called.
save() is called.
sample.spring.ioc.UserDaoImpl
save() is called.

  我们来对上面示例中出现的Annotation来进行说明。

1 @Resource(name="userDaoImpl")
2 private UserDao userDao1;

  这是定义在字段上的Annotation,是指userDao1使用xml配置文件中定义的名为“userDaoImpl”的bean进行填充。

1 @Autowired(required=false)
2 @Qualifier("userDaoImpl")
3 private UserDao userDao3;

  这是第二种类型的Annotation,它把Autowired和Qualifier组合在一起使用,Qualifier来设置bean的名称,Autowired来设置bean找不到时的行为,required为true时会抛出异常,required为false时会返回null。

1 @Resource
2 public void setUserDao2(UserDao userDao2) {
3 this.userDao2 = userDao2;
4 }

  这是作用在setter上的Annotation,@Resource 可以不写明name参数,这时Spring会首先按照名字然后按照数据类型的方式去定位bean。

  自动加载对象定义

  对于大型系统来说,我们可能会创建大量的类,如果这些类的声明都需要写在xml文件里的话,会产生额外大量的工作。

  Spring提供了一种简单的机制让我们的对象可以自动注册。

  我们可以在beans.xml中添加如下内容:

1 <context:component-scan base-package="sample.spring.ioc"/>

  然后我们可以在sample.spring.ioc包下的对象,添加@Component/@Service/@Controller/@repository,这样Spring会自动将带有这些Annotation的类进行注册。

  下面是一个示例:

1 @Service("userService")
2 public class UserServiceBean3
3 {
4 private String userID;
5 private String userName;
6 @Resource(name="userDaoImpl")
7 private UserDao userDao1;
8
9 @Autowired(required=true)
10 @Qualifier("userDaoImpl")
11 private UserDao userDao3;
12
13 public void setUserID(String userID) {
14 this.userID = userID;
15 }
16 public String getUserID() {
17 return userID;
18 }
19 public void setUserName(String userName) {
20 this.userName = userName;
21 }
22 public String getUserName() {
23 return userName;
24 }
25 // @Resource
26 // public void setUserDao2(UserDao userDao2) {
27 // this.userDao2 = userDao2;
28 // }
29 // public UserDao getUserDao2() {
30 // return userDao2;
31 // }
32
33 public void test()
34 {
35 userDao1.save();
36 // userDao2.save();
37 System.out.println(userDao3.getClass().getName());
38 userDao3.save();
39 }
40 }

这个类和上面定义的UserServiceBean2非常相似,需要注意在类前面添加的Annotation信息。

  我们不需要在xml文件中手动定义这个bean,Spring会进行自动注册,注册的bean名称是userService。

Spring 之 IOC的更多相关文章

  1. Spring的IOC和AOP之深剖

    今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...

  2. Spring框架IOC容器和AOP解析

    主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置  一.S ...

  3. Spring总结—— IOC 和 Bean 的总结

    一.Spring 官方文档中给出的 Spring 的整体结构. 二.我自己所理解的 Spring 整体结构图. 三.本次总结 Spring 核心部分 1.从上面图中可以看出,Beans 和 Conte ...

  4. spring的IOC和AOP

     spring的IOC和AOP 1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是 ...

  5. spring容器IOC创建对象<二>

    问题?spring是如何创建对象的?什么时候创建对象?有几种创建方式?测试对象是单例的还是多例的 ?对象的初始化和销毁? 下面的四大模块IOC的内容了!需要深刻理解 SpringIOC定义:把对象的创 ...

  6. Spring中IoC的入门实例

    Spring中IoC的入门实例 Spring的模块化是很强的,各个功能模块都是独立的,我们可以选择的使用.这一章先从Spring的IoC开始.所谓IoC就是一个用XML来定义生成对象的模式,我们看看如 ...

  7. Spring中IOC和AOP的详细解释

    我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式. IOC就是典型的工厂模式,通过s ...

  8. Spring的IoC应用

    IoC(Inversion of Control,控制反转) Spring的IoC应用是其框架的最大的特点,通过依赖注入可以大大降低代码之间的耦合度,从而实现代码和功能之间的分离.在代码中可以不直接和 ...

  9. Spring 实践 -IoC

    Spring 实践 标签: Java与设计模式 Spring简介 Spring是分层的JavaSE/EE Full-Stack轻量级开源框架.以IoC(Inverse of Control 控制反转) ...

  10. 挖坟之Spring.NET IOC容器初始化

    因查找ht项目中一个久未解决spring内部异常,翻了一段时间源码.以此文总结springIOC,容器初始化过程. 语言背景是C#.网上有一些基于java的spring源码分析文档,大而乱,乱而不全, ...

随机推荐

  1. 02 - Unit07:显示笔记下拉菜单、笔记的分享功能、笔记的删除功能

    显示笔记下拉菜单 笔记的分享功能 发送Ajax请求 绑定事件:绑定分享按钮单击事件 参数获取:笔记ID 发送请求:/share/add.do 服务器处理 ShareController ShareSe ...

  2. 根据给定文件编写Makefile文件 两种方法编译

    实例一 1.分析源文件代码依赖关系 mian.c #include "test1.h" #include "test2.h" #include <stdi ...

  3. WCF传输大数据 --断点续传(upload、download)

    using System; using System.IO; using System.Runtime.Serialization; using System.ServiceModel; namesp ...

  4. js 各种距离

    网页可见区域宽  document.body.clientWidth  网页可见区域高  document.body.clientHeight  网页可见区域宽(包括边线的宽)  document.b ...

  5. CentOS 环境下基于 Nginx uwsgi 搭建 Django 站点

    因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.请访问我的个人网站获取这篇文章的最新内容,CentOS 环境下基于 Nginx uwsgi 搭建 Django 站点 以下 ...

  6. Rhythmk 学习 Hibernate 08 - Hibernate annotation 关联关系注解

    1.一对一 (One to One)    共三种情况:     1.1 主键共享    1.2 外键共享 1.3 中间表关联 1.1  code: @Entity public class arti ...

  7. TMS Grid

    TMS Grid http://edn.embarcadero.com/article/42553

  8. 可视化库-Matplotlib基础设置(第三天)

    1.画一个基本的图 import numpy as np import matplotlib.pyplot as plt # 最基本的一个图,"r--" 线条加颜色, 也可以使用l ...

  9. Spring Boot实践——基础和常用配置

    借鉴:https://blog.csdn.net/j903829182/article/details/74906948 一.Spring Boot 启动注解说明 @SpringBootApplica ...

  10. docker redis

    https://www.cnblogs.com/cgpei/p/7151612.html 重启docker >systmctl restart docker >mkdir -p ~/red ...