首页 > 代码库 > spring(3)------控制反转(IOC)/依赖注入(DI)

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

一。spring核心概念理解

控制反转:

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

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


没有控制反转这样的模式前。你创建一个对象。在什么地方用。你得单独通过keywordnew出来用,

但如今能够不用这样,你把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注入。构造方法注入酌情处理。

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


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