Spring 之 IOC
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的更多相关文章
- Spring的IOC和AOP之深剖
今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...
- Spring框架IOC容器和AOP解析
主要分析点: 一.Spring开源框架的简介 二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置 一.S ...
- Spring总结—— IOC 和 Bean 的总结
一.Spring 官方文档中给出的 Spring 的整体结构. 二.我自己所理解的 Spring 整体结构图. 三.本次总结 Spring 核心部分 1.从上面图中可以看出,Beans 和 Conte ...
- spring的IOC和AOP
spring的IOC和AOP 1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是 ...
- spring容器IOC创建对象<二>
问题?spring是如何创建对象的?什么时候创建对象?有几种创建方式?测试对象是单例的还是多例的 ?对象的初始化和销毁? 下面的四大模块IOC的内容了!需要深刻理解 SpringIOC定义:把对象的创 ...
- Spring中IoC的入门实例
Spring中IoC的入门实例 Spring的模块化是很强的,各个功能模块都是独立的,我们可以选择的使用.这一章先从Spring的IoC开始.所谓IoC就是一个用XML来定义生成对象的模式,我们看看如 ...
- Spring中IOC和AOP的详细解释
我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式. IOC就是典型的工厂模式,通过s ...
- Spring的IoC应用
IoC(Inversion of Control,控制反转) Spring的IoC应用是其框架的最大的特点,通过依赖注入可以大大降低代码之间的耦合度,从而实现代码和功能之间的分离.在代码中可以不直接和 ...
- Spring 实践 -IoC
Spring 实践 标签: Java与设计模式 Spring简介 Spring是分层的JavaSE/EE Full-Stack轻量级开源框架.以IoC(Inverse of Control 控制反转) ...
- 挖坟之Spring.NET IOC容器初始化
因查找ht项目中一个久未解决spring内部异常,翻了一段时间源码.以此文总结springIOC,容器初始化过程. 语言背景是C#.网上有一些基于java的spring源码分析文档,大而乱,乱而不全, ...
随机推荐
- Python项目输出小类概率,机器学习
from pandas import read_csv import numpy as np from sklearn.datasets.base import Bunch import pickle ...
- postman-1版本区别、选择
postman基于乙醇在腾讯课堂的postman教程 postman特点: 1.便于开发:开发接口的时候需要快速的调用接口,以便调试 2.便于测试:测试的时候需要非常方便的调用接口,通过不同的参数去测 ...
- 1021 docker搭建mysql、网络模式、grid
1.搭建并连接mysql服务 1.1.mysql官方命令 https://hub.docker.com/_/mysql/ #下载mysql镜像: docker pull mysql #启动mysql: ...
- 使用Spring发送Email
配置Spring发送邮件 Spring发送邮件底层还是使用JavaMail,我在http://www.cnblogs.com/lz2017/p/6882925.html 中记录过关于JavaMail的 ...
- 学python着几个要搞清楚WSGI和uWSGI区别
1 WSGI是一种通信协议 2 uwsgi是一种线路协议而不是通信协议,在此常用于在uWSGI服务器与其他网络服务器的数据通信. 3 而uWSGI是实现了uwsgi和WSGI两种协议的Web服务器.
- mysql 获取一段时间的数据
用 sql 获取一段时间内的数据: SELECT * FROM EDI.edi_history WHERE timestampdiff(day, SYSDATE(), create_time_loc) ...
- java.lang.NoClassDefFoundError: weblogic/kernel/KernelStatus
solution Step: 1.In the classpath tab, be sure to add the wlclient.jar file located here \wlserver_1 ...
- C#委托(匿名函数)的各种变形写法
static void TestDelegate() { //类C++11风格:指定初始化容量20,使用初始化列表给部分成员赋值 ) { , , , , -, , }; ; i < lst. ...
- Cardboard Talk01 HeadTracker
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Android studio 3.0.0 | Cardboard 1.0 使用 Google 的 Cardboard开发V ...
- Python Geoip 获取IP地址经度、纬度
简介: 除了一些免费的 API 接口,例如 http://ipinfo.io/223.155.166.172 可以得到一些信息外,还可以通过 python-geoip 库来解决这个问题. shell ...