spring(3)------控制反转(IOC)/依赖注入(DI)

一,spring核心概念理解

控制反转:

控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。

所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。

没有控制反转这种模式前,你创建一个对象,在什么地方用,你得单独通过关键字new出来用,

但现在可以不用这样,你把new对象的权利交给spring配置文件,通过配置文件来‘new‘,

就是权利的反转,你以前干的事,现在交给别人干了。

IoC是一个很大的概念,可以用不同的方式来实现。

其主要实现方式有两种:

(1)依赖查找(Dependency Lookup):容器提供回调接口和上下文环境给组件

(2)依赖注入(Dependency Injection):组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。

后者是时下最流行的IoC类型,其又有接口注入(Interface Injection),设值注入(Setter Injection)和构造子注入(Constructor Injection)三种方式。

控制反转和依赖注入到底是什么关系?

控制反转是个模式,是个大概念,而对控制反转最具体的解释就是依赖注入,前者是说个大范围,

后者是具体到这件事情的实质上。就好比你朋友说等你过生日一定要送你个礼物,等过生日时送了你一个手机,

而送礼物就是个大概念,好比控制反转,具体送了一个手机,就是依赖注入,但是两者说明的是送礼物的这件事。

当然了,依赖注入有三种方式,就好比你朋友可以选择送你个苹果手机或送你台宝马或送你套别墅三种注入方式,

概率没有必要咬文嚼字的去理解,先把车会开了,慢慢就会知道车是怎么组成的。

二,IoC概念实例理解

在写一个程序,有多中实现方式,你如果每次都通过改程序代码,通过new来实现不同的功能,耦合性太高,

IoC是为解决松耦合而生的,以下通过代码理解。

(1)创建接口和实现类

接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 讲不同语言
	 * @return
	 */
	public String doSay();
}

说中文实现类:

package com.lanhuigu.spring.impl;

public class ChHelloImpl implements ISayHello {
	private String msg;

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 讲汉语
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "中文:"+msg;
	}

}

说英文实现类:

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 讲英语
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}

(2)配置spring文件

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 讲英语
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}

(3)测试程序

package com.lanhuigu.spring.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.lanhuigu.spring.impl.ISayHello;

public class TestHelloWorld {
	@Test
	public void testMyHelloWorld(){
		//1.读取spring初始化的配置文件
		ApplicationContext acxt =
				new ClassPathXmlApplicationContext("/applicationContext.xml");
		//2.根据bean获取ISayHello实现类对象
		ISayHello sayHello = (ISayHello) acxt.getBean("sayHello");
		//3.调用接口方法
		System.out.println(sayHello.doSay());
	}
}

通过以上代码对IoC进行实例体会:

(1)在测试程序中,如果不使用IoC,你可以通过new来实现讲什么语言

ISayHello sayHello = new ChHelloImpl();//讲汉语

ISayHello sayHello = new EnHelloImpl();//讲英语

或许会觉得我就new就可以了,领导让我讲英语,我就new讲英语,领导要我讲汉语,我就讲汉语,来回注释掉就行了,

但是,如果一个应用程序中有一万个地方用到了这段代码, 你是不是得注释一万个地方,心里绝对10万个对领导不满,改来改去有病吧!

你或许还不服,我就注释掉一万个地方,如果有100万个地方呢,你是不是100万个‘草泥马‘在草原上奔腾啊!

这个时候IoC来为你解决烦恼,一剂药就能治好,再也不用担心领导让你说啥你确有苦说不出口了。

通过配置文件控制该讲什么语言:

<bean id="sayHello" class="com.lanhuigu.spring.impl.ChHelloImpl">

<!-- 将变量msg值依赖注入 -->

<property name="msg">

<value>测试</value>

</property>

</bean>

你的配置文件通过实现类配置让讲啥都行,class="com.lanhuigu.spring.impl.ChHelloImpl",

而应用代码不受影响:

ISayHello sayHello = (ISayHello) acxt.getBean("sayHello");

System.out.println(sayHello.doSay());

应用程序只管拿到的bean对象是啥,我就说啥,至于你容器怎么配置,应用程序不屌你,

而容器则配置自己的实现类,至于你应用程序实现成啥样,容器才不管,通过这样的关系

实现低耦合的实效,实现控制反转的职能。

三,依赖注入(DI)

依赖注入的含义:

让主键依赖于抽象,当组件要与其他实际对象发生关系时,通过抽象来注入依赖的实际对象。

依赖注入的三种实现方式分别是接口注入(interface injection),set方法注入(setter injection),构造注入(constructor injection)。

(1)接口注入(interface injection):

定义一个将英语的接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 讲英语
	 * @return
	 */
	public String doSay(EnHelloImpl en);
}

讲英语接口实现类:

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 讲英语
	 */
	@Override
	public String doSay(EnHelloImpl en) {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}

如果采用接口注入的方式,原本能说各种语言的接口,被定义为只能讲英语的接口,整个业务逻辑类依赖于这个接口。

在spring中接口注入是在IOC概念没有确立时定义的,现在spring中不推荐用接口注入,接口注入业务逻辑类依赖于接口,不是重点。

(2)set方法注入(setter injection):

set注入就是指在接受注入的类中定义一个set方法,并在参数中定义需要注入的参数。

为了能够将多种语言,接口注入方式是不适用的,我们把接口定义成开放的,这样每个实现类根据需要做自己

的业务逻辑,通过set方法传入参数。

接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 讲不同语言
	 * @return
	 */
	public String doSay();
}

实现类:

汉语实现类:

package com.lanhuigu.spring.impl;

public class ChHelloImpl implements ISayHello {
	private String msg;

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 讲汉语
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "中文:"+msg;
	}

}

英语实现类:

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 讲英语
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}

配置文件传入参数:

<?xml version="1.0" encoding="UTF-8"?>

<!--
  - Application context definition for JPetStore's business layer.
  - Contains bean references to the transaction manager and to the DAOs in
  - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation").
  -->
<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:aop="http://www.springframework.org/schema/aop"
		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/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<!-- 定义一个id为sayHello的bean,
	通过spring配置文件变换实现类,实现不同的功能,无需修改别的程序 -->
	<bean id="sayHello" class="com.lanhuigu.spring.impl.ChHelloImpl">
		<!-- 将变量msg值依赖注入 -->
		<property name="msg">
			<value>测试</value>
		</property>
	</bean>
</beans>

每一个接口实现类对应一种逻辑,spring配置文件负责调配。

配置文件bean的property有个默认属性name,这个name对应的是bean的class类对象中的属性名msg,

代码样式private String msg,value为属性值,通过类中的set方法,将配置文件中的属性值注入,

在类中通过get获取属性值。

(3)构造注入(constructor injection):

构造注入就是在接受注入的类中定义一个构造方法,并在参数中定义需要注入的元素。

咱们将讲英语改成构造方法注入:

接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 讲不同语言
	 * @return
	 */
	public String doSay();
}

实现类:

package com.lanhuigu.spring.impl;

public class ChHelloImpl implements ISayHello {
	private String msg;
	private String two;
	/**
	 * 构造方法注入
	 * @param msg
	 * @param two
	 */
	public ChHelloImpl(String msg,String two){
		this.msg = msg;
		this.two = two;
	}
	/*public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}*/
	/**
	 * 讲汉语
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "中文:"+msg+two;
	}

}

spring配置文件:

<?xml version="1.0" encoding="UTF-8"?>

<!--
  - Application context definition for JPetStore's business layer.
  - Contains bean references to the transaction manager and to the DAOs in
  - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation").
  -->
<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:aop="http://www.springframework.org/schema/aop"
		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/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<!-- 定义一个id为sayHello的bean,
	通过spring配置文件变换实现类,实现不同的功能,无需修改别的程序 -->
	<bean id="sayHello" class="com.lanhuigu.spring.impl.ChHelloImpl">
		<!-- 将变量msg值依赖注入 -->
		<!-- <property name="msg">
			<value>测试</value>
		</property> -->
		<!-- 构造放入注入,
		如果不写index,默认构造方法只有一个参数, 如果注入多个,就会报错;
		如果注入多个参数,可以通过index的下标指定与构造方法参数的对应关系,
		下标从0开始,0表示构造方法的第一个参数,依次类推
		-->
		<constructor-arg index="0">
			<value>测试</value>
		</constructor-arg>
		<constructor-arg index="1">
			<value>two arg</value>
		</constructor-arg>
	</bean>
</beans>

测试程序:

package com.lanhuigu.spring.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.lanhuigu.spring.impl.ISayHello;

public class TestHelloWorld {
	@Test
	public void testMyHelloWorld(){
		//1.读取spring初始化的配置文件
		ApplicationContext acxt =
				new ClassPathXmlApplicationContext("/applicationContext.xml");
		//2.根据bean获取ISayHello实现类对象
		ISayHello sayHello = (ISayHello) acxt.getBean("sayHello");
		//3.调用接口方法
		System.out.println(sayHello.doSay());
	}
}

输出结果:

中文:测试two arg

关于构造方法注入的注意点:

(1)在接受注入的构造方法中写需要注入的参数

(2)配置文件中构造属性constructor-arg不写index时默认一个参数,如果构造方法有多个参数,将报错

(3)如果注入多个参数,需在配置文件中constructor-arg通过index明确value对应构造方法中的参数情况

四,总结

依赖注入有三种注入方式,到底哪种注入方式好,一般推荐使用set注入

接口注入不考虑,构造方法注入通过上面的实例可以看出,如果注入参数太多,

构造方法中就会有一堆参数,看着都迷糊,而spring配置文件还得对参数做对应的传值,

如果注入类太多,参数太多,构造器配置越多,就会迷糊;

而set则是根据名字注入,能够避免构造方法注入的麻烦,但是,使用set方法注入是动态的注入参数,

可能被人篡改,构造方法注入在类启动时就完成,就不会出现篡改的可能,相对安全性高,二者各有

千秋,但是通常用set注入,构造方法注入酌情处理。

来注入的,这一点比构造方法注入好

时间: 2024-10-06 13:33:33

spring(3)------控制反转(IOC)/依赖注入(DI)的相关文章

Spring理论基础-控制反转和依赖注入

序第一次了解到控制反转(Inversion of Control)这个概念,是在学习Spring框架的时候.IOC和AOP作为Spring的两大特征,自然是要去好好学学的.而依赖注入(Dependency Injection,简称DI)却使得我困惑了挺久,一直想不明白他们之间的联系.控制反转控制反转顾名思义,就是要去反转控制权,那么到底是哪些控制被反转了?在2004年 Martin fowler 大神就提出了 "哪些方面的控制被反转了?" 这个问题,他总结出是依赖对象的获得被反转了.在

Spring 基础 控制反转和依赖注入

Spring框架两个最重要的知识点 1.IOC(控制反转)/DI(依赖注入):把整个项目中的所有对象交给Spring容器管理 IOC:取到对象 DI:对象的装配 依赖注入有两种方式: 1.      属性值注入方式 a.     普通类型 注入方式 b.bean注入方式 测试结果 在使用普通类型方式注入的时候,我们会有一些特殊字符,处理这种特殊字符时 我们会想使特殊字符保持原有的样子,这时候就要使用CDATA,就如上一张图片所示 2.构造参数注值方式 在一般情况,一个类会有一个默认的无参构造函数

spring的控制反转和依赖注入

控制反转(Inversion of Control,IOC) 我们首先先来了解一下控制二字,也就是在控制"正"转的情况下,在任何一个有请求作用的系统当中,至少需要有两个类互相配合工作,在一个入口类下使用new关键字创建另一个类的对象实例,这就好比在面向对象编程的思想下,"我"充当一个入口类,在这个入口类中,我每次吃饭的时候都要买一双一次性筷子(每一次使用都要new一次),在这样的关系下,是"我"(即调用者)每次都要"主动"去买

控制反转与依赖注入

关于控制反转和依赖注入的文章和书籍很多,对其定义也解释的也仁者见仁,这里就不赘述了,这是本人(只代表个人观点)理解之后用通俗的例子和平淡的话词为您解释,希望对您有所帮助: 控制反转(IoC/Inverse Of Control):   调用者不再创建被调用者的实例,由spring框架实现(容器创建)所以称为控制反转. 依赖注入(DI/Dependence injection) :   容器创建好实例后再注入调用者称为依赖注入. 当 某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个

Spring入门--控制反转(IOC)与依赖注入(DI)

    1.控制反转(Inversion of Control)与依赖注入(Dependency Injection) 控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理.所谓的"控制反转"概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器. IoC是一个很大的概念,可以用不同的方式来实现.其主要实现方式有两种:<1>依赖查找(Dependency Lookup)

IOC/DI控制反转与依赖注入

IOC/DI控制反转与依赖注入 IOC和DI表现的效果的是一样的只不过对于作用的对象不同,有了不一样的名字. 先用一个现实的例子来说明IOC/DI表现出来的效果. 上图,小冬的有一个吃的功能,营养师要求小冬要吃土豆,这个过程有很大的局限,也就是对于营养师来说,这个营养师只能要求小冬,并且还只能吃土豆,显然一个营养师只能给一个人去搭配营养,并且还然人家只能吃土豆,这样显然不可以!所以对此做个改进: 这一次营养师可以要求任何用能力吃的人,只要有能力吃,也就是实现吃的这一个能力人,就可以被营养师来要求

Spring框架之控制反转和依赖注入

学Spring框架必须理解控制反转和依赖注入.下面各自举一个例子,来说明. IOC(控制反转):应用本身创建和维护的依赖对象:现在交由外部容器(Spring)来创建和维护:这个控制权的转移: 就叫做控制反转. 第一步:配置applicationContextcreateproject.xml和applicationcontext.xml(总体) <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=

轻松了解Spring中的控制反转和依赖注入

我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达170平方米,重达30吨,到现如今的个人笔记本,事物更加轻量功能却更加丰富,这是事物发展过程中的一个趋势,在技术领域中同样也是如此,企业级JavaBean(Enterprise JavaBean ,EJB)在创建之初是非常成功,但是时间一久人们便开始追逐更加方便更加简易和轻量级的技术框架实现,于是Spring就应运而生,并且Sring一直开始不断地涉及到其他领域(这里就不再多详谈了),而Spring的精髓当中就包括控制反转和依赖注入. 浅

轻松了解Spring中的控制反转和依赖注入(二)

紧接上一篇文章<轻松了解Spring中的控制反转和依赖注入>讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作. 类之间的结构图如下 以下是代码 BeanFactor接口:在Spring源码中的定义是:持有对一定数量的Bean的定义,同时每个Bean都被唯一标识的对象(类),需要实现这个接口.根据对Bean的定义,该工厂将会返回一个包含Bean定义的对象的独立实例(原型设计模式),或者单例共享(一个不错的单例设计模式,)范