【java基础】IOC介绍及其简单实现
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找。依赖注入应用比较广泛,我们这里只介绍依赖注入。
一、IOC简介
控制反转IOC,它最主要反映的是与传统面向对象(OO)编程的不同。通常我们编程实现某种功能都需要几个对象相互作用,从编程的角度出发,也就是一个主对象要保存其他类型对象的引用,通过调用这些引用的方法来完成任务。如何获得其他类型的对象引用呢?一种方式是主对象内部主动获得所需引用(也就是通常我们使用的new一个对象);另一种方式是在主对象中设置setter 方法,通过调用setter方法或构造方法传入所需引用。后一种方式就叫IOC,也是我们常常所说的依赖注入DI。以下我们用一个简单的例子来说明传统OO编程与IOC编程的差别。
这个例子的目的是根据时间不同返回不同的问候字符串, 比如Good Morning, world或Good afternoon, World。
服务接口:
package cn.test.ioc;
public interface HelloIF {
String sayHello();
}
传统实现:
package cn.test.ioc; import java.util.Calendar; /**
* 传统实现(非IOC方式)
* @author Administrator
*
*/
public class HelloIFImpl implements HelloIF {
private Calendar cal; // 我们需要的引用 public HelloIFImpl() {
cal = Calendar.getInstance(); // 主动获取
} public String sayHello(){
if(cal.get(Calendar.AM_PM) == Calendar.AM){
return "Good morning, World";
}else{
return "Good afternoon, World";
}
} public static void main(String args[]){
HelloIFImpl hf = new HelloIFImpl();
System.out.println(hf.sayHello());
}
}
采用IOC方式:
package cn.test.ioc; import java.util.Calendar; /*
* IOC方式实现
*/
public class HelloIFImpl2 implements HelloIF {
private Calendar cal; // 我们需要的引用 public void setCal(Calendar cal) {
this.cal = cal;
} // 依赖注入 public String sayHello(){
if(cal.get(Calendar.AM_PM) == Calendar.AM){
return "Good morning, World";
}else{
return "Good afternoon, World";
}
} public static void main(String args[]){
HelloIFImpl2 hf = new HelloIFImpl2();
hf.setCal(Calendar.getInstance());
System.out.println(hf.sayHello());
}
}
在这里你也许会问:我看不出有太大差别,并且依赖注入还需要我先创建外部的Calendar对象,然后再传到HelloIFImpl对象中。但是,假如我们事先已经在类用new orderOracle(),但是后来由于需求变更,我们需要使用new orderSqlServer(),这样我们还需要修改所有使用orderOracle()的类,这样好麻烦。如果我们使用IOC方法编程并且使用了spring框架,这样我们只需要修改xml配置文件即可。
IoC则是一种 软件设计模式,它告诉你应该如何做,来解除相互依赖模块的耦合。控制反转(IoC),它为相互依赖的组件提供抽象,将依赖(低层模块)对象的获得交给第三方(系统)来控制,即依赖对象不在被依赖模块的类中直接通过new来获取。
二、IOC的一个应用举例
下面我们以一个struts2和Spring整合的例子来说明。
1)整合struts2和Spring
首先要整合Spring和Struts2,需要先要拷入Spring需要的jar包,既包括Spring本身的jar包,也包括Struts2的Spring插件。将以下几个jar包拷入到我们的web工程的WEB-INF\lib中:org.springframework.asm-3.0.5.RELEASE.jar ;spring-*.jar(struts2中lib包里所有的jar,共6个); struts2-spring-plugin-*.jar 。
2)编写逻辑层接口
package cn.test.springDemo; public interface SampleService {
public String getNameById(String userId);
}
3)编写逻辑层实现类
package cn.test.springDemo; public class SampleServiceImpl implements SampleService{
public String getNameById (String userId) {
//根据userId到数据层进行查询,获取相应的name
String name = "hello,"+ userId;
return name;
}
}
4)编写ACTION
package cn.test.springDemo; import com.opensymphony.xwork2.ActionSupport; public class SampleAction extends ActionSupport {
// 通过setter方式,由Spring来注入SampleService实例
private SampleService service; public void setService(SampleService service) {
this.service = service;
} private String name;
private String userId; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getUserId() {
return userId;
} public void setUserId(String userId) {
this.userId = userId;
} public String execute() throws Exception {
name = this.service.getNameById(userId);
return SUCCESS;
}
}
在execute方法中不再直接new一个SampleServiceImpl的实例了,而是声明了一个SampleSerivce类型的属性,并提供对应的setter方法,这个setter方法是留给Spring注入对象实例的时候调用的,可以不用提供getter方法。也就是说,现在的SampleAction已经不用知道逻辑层的具体实现了。
5)编写Spring的配置文件applicationContext.xml
要让Spring来管理SampleAction和SampleServiceImpl的实例,还需要新建一个Spring的配置文件。在src下新建一个applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean name="service" class="cn.test.springDemo.SampleServiceImpl" />
<bean name="sampleAction" class="cn.test.springDemo.SampleAction" scope="prototype" >
<property name="service" ref="sampleService"/>
</bean>
</beans>
这个xml的根元素是<beans>,在<beans>中声明了它的schema引用,除此之外,还有两个<bean>元素,定义了由Spring管理的SampleServiceImpl和SampleAction。
- 对于第一个<bean>元素来说
l name属性为它设置了一个名字
l class元素指定了它的实现类的全类名
- 对于第二个<bean>元素来说
l name属性和class属性的含义与第一个<bean>元素完全一样。
l scope属性,赋值为prototype(原型)。scope属性非常重要,它管理了注册在它里面的Bean的作用域。Spring容器默认的作用域是单例,即每次外界向Spring容器请求这个Bean,都是返回同一个实例;但是,Struts2的Action是需要在每次请求的时候,都要新建一个Action实例,所以,在配置对应Action的<bean>元素时,必须把它的scope属性赋值为prototype,以保证每次请求都会新建一个Action实例。
l <property>子元素。<property>元素的name属性为service,代表SampleAction这个类有一个setter方法叫setSampleService;<property>元素的ref属性为sampleService,代表Spring容器会将一个名为sampleService的已经存在的Bean,注入给sampleAction的service属性。
6)在web.xml中引用Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> <filter>
<filter-name>Struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
listener实现了当这个Web工程启动的时候,就去读取Spring的配置文件,这个类是由Spring提供的,这里只需要配置上去就可以了。上下文参数的配置里面,contextConfigLocation的值classpath*:applicationContext.xml,表明了所有出现在classpath路径下的applicationContext.xml文件,都是上面的这个Listener要读取的Spring配置文件。
7)编写struts.xml
就快要大功告成了,最后一步,来修改struts.xml,需要做两件事:
首先,添加常量struts.objectFactory,其值为spring,这就指定了Struts2使用Action的时候并不是自己去新建,而是去向Spring请求获取Action的实例。示例如下:
<constant name="struts.objectFactory" value="spring"/>
然后,<action>元素的class属性,现在并不是要填Action类的全类名了,而是要填一个在Spring配置文件中配置的Action的Bean的名字,也就是<bean>元素的name属性,很显然,需要的是sampleAction这个Bean。示例如下:
<package name="springPackage" extends="struts-default">
<action name="sampleActionName" class="sampleAction">
<result>/spring/success.jsp</result>
</action>
</package>
只有<action>元素的class属性变了,其他部分不变。来测试一下,运行:http://localhost:8080/struts2Deepen2/sampleActionName.action?userId=test。【其中,struts2Deepen2为web工程的名称】。运行一切正常,对吧,这也说明Struts2与Spring整合并不是为了实现新功能,而是为了让表现层组件和逻辑层组件解耦,SampleAction类不用再知道SampleServiceImpl这个具体实现了,只需要知道SampleService这个接口就可以了。
参考资料:
http://blog.csdn.net/Kettas2008/article/details/2447809
http://www.iteye.com/topic/1124526
http://www.cnblogs.com/liuhaorain/p/3747470.htm#title_4 【这篇文章详细介绍了DIP、IoC、DI以及IoC容器,推荐看看】
【java基础】IOC介绍及其简单实现的更多相关文章
- java基础学习02(简单的java程序)
简单的java程序 一.完成的目标 1. 理解java程序的基本组成 2. 如何对程序代码进行注释 3. java标识符的命名规则 4. 了解java中的关键字 5. 使用java定义变量或声明变量 ...
- Java基础学习-- 继承 的简单总结
代码参考:Java基础学习小记--多态 为什么要引入继承? 还是做一个媒体库,里面可以放CD,可以放DVD.如果把CD和DVD做成两个没有联系的类的话,那么在管理这个媒体库的时候,要单独做一个添加CD ...
- Java基础-内部类介绍
java内部类介绍 内部类一共分为4种 成员内部类 静态内部类 方法内部类 匿名内部类 下面我会为大家详细介绍每一个内部类!! 成员内部类 成员内部类就好像是外部类的一个成员属性,也是内部类中最常见的 ...
- Java基础知识介绍
数组的定义及初始化方式 数组对象创建没有() 一维数组 静态初始化: String[] books = {"Thinking in Java","Effective Ja ...
- IOC介绍及其简单实现
预备知识: Java反射原理,XML及其解析 IOC:Inversion of Control,控制反转,它最主要反映的是与传统面向对象(OO)编程的不同.通常我们编程实现某种功能都需要几个对象相 ...
- Java的spi介绍和简单应用
1.什么是java的spi SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制. 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一 ...
- java基础—抽象类介绍
一.抽象类介绍
- 【Java基础】异常的简单分类与处理
Java中所有的异常都继承自Throwable类,Throwable类的已知子类有Error和Exception. Error是指系统出现的错误,这种错误出现的时候,我们的程序无能为力,所以不需要进行 ...
- Java 基础 变量介绍
变量的声明和使用 概念: 变量是指内存中的一个存储区域,该区域要有自己的名称(变量名).类型(数据类型),该区域的数据可以在同一数据类型的范围内不断变化值: 变量的使用注意事项: Java中的变量必须 ...
随机推荐
- Effective Java 02 Consider a builder when faced with many constructor parameters
Advantage It simulates named optional parameters which is easily used to client API. Detect the inva ...
- ajaxFileUpload文件上传
一.简介 ajaxFileUpload是一种异步的文件上传控件,通过ajax进行文件上传,并获取上传处理结果.在很多时候我们需要使用到文件上传的功能,但是不需要使用那些强大的上传插件.此时就可以使用a ...
- 设计模式C#实现(八)——原型模式
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.(要创建一个对象,这个对象为实现原型接口,方法是原型克隆.克隆只是方法而不是原型模式的目的,创建对象才是目的) UML类图: ...
- linux: 获取监听指定端口的进程PID
在 linux 下经常需要杀死(重启)监听某端口的进程, 因此就写了一个小脚本, 通过 ss 命令获取监听制定端口的进程 PID, 然后通过 kill 命令结束掉进程: #!/bin/sh # set ...
- python split()函数使用拆分字符串 将字符串转化为列表
函数:split()Python中有split()和os.path.split()两个函数,具体作用如下:split():拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list ...
- B+树的特点
1.B+树是应文件系统产生的B树的变种.它依然是一颗多路查找树,与B树相比它的不同体现在: (1).如果非叶子节点包含n个关键码,则这个节点有n个子树. (2).非叶子节点仅包含关键码信息,叶子节点包 ...
- CentOS 6.5部署安装Memcached
1. Yum安装Memcache 查找memcached yum search memcached 该命令可以查询yum库中有关memcached的安装包信息,以下是搜寻结果截图: 安装 memc ...
- 用Java实现单链表的基本操作
笔试题中经常遇到单链表的考题,下面用java总结一下单链表的基本操作,包括添加删除节点,以及链表转置. package mars; //单链表添加,删除节点 public class ListNode ...
- MIT jos 6.828 Fall 2014 训练记录(lab 4)
源代码参见我的github: https://github.com/YaoZengzeng/jos Part A: Multiprocessor Support and Cooperative Mul ...
- 给定一个整数N,找出一个比N大且最接近N,但二进制权值与该整数相同 的数
1,问题描述 给定一个整数N,该整数的二进制权值定义如下:将该整数N转化成二进制表示法,其中 1 的个数即为它的二进制权值. 比如:十进制数1717 的二进制表示为:0000 0110 1011 01 ...