首页 > 代码库 > Spring的依赖注入

Spring的依赖注入

1.  概念简介

  前面说过IoC容器实现控制反转的核心就是DI——依赖注入。对象之间的依赖关系无非会通过以下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。所以,IoC容器的工作就是创建bean时注入那些依赖关系。

  相对于由bean自己来控制其实例化、直接在构造器中指定依赖关系或则类似服务定位器(ServiceLocator)模式这3种自主控制依赖关系注入的方法来说,控制从根本上发生了倒转,这也正是控制反转(Inversionof Control, IoC) 名字的由来。

  应用DI原则后,代码将更加清晰。而且当bean自己不再担心对象之间的依赖关系(以及在何时何地指定这种依赖关系和依赖的实际类是什么)之后,实现更高层次的松耦合将更加容易。

  下面主要介绍DI的两种注入方式,即Setter注入和 构造器注入:

2.  Setter注入

  通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现基于setter的DI:

package com.bjpowernode.spring.manager;

import com.bjpowernode.spring.dao.UserDao;
import com.bjpowernode.spring.dao.UserDao4MysqlImpl;
import com.bjpowernode.spring.dao.UserDao4OracleImpl;

public class UserManagerImpl implements UserManager {
	
	private UserDao userDao;
	//无参构造器
	public UserManagerImpl() {
		super();
	}

	//setter方式注入
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	
	@Override
	public void addUser(String username, String password) {
		//原始的方式--由我们的程序负责服务(对象)管理
		//UserDao userDao = new UserDao4MysqlImp();
		//UserDao userDao = new UserDao4OracleImpl();
		userDao.addUser(username, password);
	}
}

  配置--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"
	     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.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
	
	<bean id="UserDao4MysqlImpl" class="com.bjpowernode.spring.dao.UserDao4MysqlImpl"/>
	
	<bean id="UserDao4OracleImpl" class="com.bjpowernode.spring.dao.UserDao4OracleImpl"/>
	
	<bean id ="UserManager" class="com.bjpowernode.spring.manager.UserManagerImpl">
		<!-- 
		<constructor-arg ref="UserDao4OracleImpl"/>
		 -->
		 
		<property name="userDao" ref="UserDao4OracleImpl"/>				
	</bean>
</beans>

3.  构造器注入

  基于构造器的DI通过调用带参数的构造器来实现,每个参数代表着一个协作者。此外,还可通过给静态工厂方法传参数来构造bean:

package com.bjpowernode.spring.manager;

import com.bjpowernode.spring.dao.UserDao;
import com.bjpowernode.spring.dao.UserDao4MysqlImpl;
import com.bjpowernode.spring.dao.UserDao4OracleImpl;

public class UserManagerImpl implements UserManager {
	
	private UserDao userDao;

	//构造方法方式注入
	public UserManagerImpl(UserDao userDao) {
		this.userDao = userDao;
	}
	
	@Override
	public void addUser(String username, String password) {
		//原始的方式--由我们的程序负责服务(对象)管理
		//UserDao userDao = new UserDao4MysqlImp();
		//UserDao userDao = new UserDao4OracleImpl();
		userDao.addUser(username, password);
	}
}

  配置--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"
	     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.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
	
	<bean id="UserDao4MysqlImpl" class="com.bjpowernode.spring.dao.UserDao4MysqlImpl"/>
	
	<bean id="UserDao4OracleImpl" class="com.bjpowernode.spring.dao.UserDao4OracleImpl"/>
	
	<bean id ="UserManager" class="com.bjpowernode.spring.manager.UserManagerImpl">
		<constructor-arg ref="UserDao4OracleImpl"/>
		 <!--
		<property name="userDao" ref="UserDao4OracleImpl"/>
		-->				
	</bean>
</beans>

4.  Setter注入实例

  无论是对象、还是属性,只要给入口(set方法),Spring就可以注入对象或属性。

  Spring能够以String类型提供值转换成各种内置类型,比如int、long、String、boolean等。实例如下:

  【class Bean1】

package com.bjpowernode.spring;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Bean1 {
	private String strValue;
	private int intValue;
	private List listValue;
	private Set setValue;
	private String[] arrayValue;
	private Map mapValue;
	
	public String getStrValue() {
		return strValue;
	}
	public void setStrValue(String strValue) {
		this.strValue = http://www.mamicode.com/strValue;>

  【配置--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"
	     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.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
	
	<bean id="bean1" class="com.bjpowernode.spring.Bean1">
		<property name="strValue" value=http://www.mamicode.com/"Hello_Spring"/>>

  【测试用例—InjectionTest】

  需要引入测试用例的jar包-- junit.jar,目录:spring-framework\lib\junit\ junit.jar

package test;

import junit.framework.TestCase;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.bjpowernode.spring.Bean1;

public class InjectionTest extends TestCase {
	private BeanFactory factory;
	@Override
	protected void setUp() throws Exception {
		factory = new ClassPathXmlApplicationContext("applicationContext.xml");//读取一个配置文件
	}

	@Override
	protected void tearDown() throws Exception {
		//结束时需要释放的资源
	}
	public void testInjection1(){
		Bean1 bean1=(Bean1)factory.getBean("bean1");
		System.out.println("bean1.strValue=http://www.mamicode.com/"+ bean1.getStrValue());>

  输出结果:



  可以看到Spring容器已将值注入到了Bean1的属性中。


5.  属性编辑器

  上面提到了,Spring能够以String类型提供值转换int、long、String、boolean等兼容类型,但它无法直接转换为Date等不兼容的类型,这样的转换需要手动写属性编辑器来完成。

  例如上面的例子中,给Bean1中加一个Date类型的属性,如下:

	private Date dateValue;
	
	public Date getDateValue() {
		return dateValue;
	}
	public void setDateValue(Date dateValue) {
		this.dateValue = http://www.mamicode.com/dateValue;>

  那么直接在Spring配置中单纯的配置如下的属性映射,是会报类型转换错误的:

<property name="dateValue" value=http://www.mamicode.com/"2014-11-27"/>

  还需要手动写一个属性编辑器,来完成字符串到Date类型之间的转换,如下【class UtilDatePropertyEditor】:

package com.bjpowernode.spring;

import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * java.util.Date属性编辑器
 */
public class UtilDatePropertyEditor extends PropertyEditorSupport {

	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		System.out.println("---UtilDatePropertyEditor.setAsText()--->"+text);
		try {
			Date date = new SimpleDateFormat("yyyy-MM-dd").parse(text);
			this.setValue(date);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new IllegalArgumentException();
		}
	}
}

  然后需要将属性编辑器注入到CustomEditorConfigurer类的customEditors属性(为Map类型)中。再添加一个Spring的核心配置文件【applicationContext-editor.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: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.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
	
	<bean id="customEditors" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="java.util.Date">
					<bean class="com.bjpowernode.spring.UtilDatePropertyEditor"/><!-- 内部bean(不用id属性) -->
				</entry>
			</map>
		</property>
	</bean>
	<!-- 
	<bean id="utilDatePropertyEditor" class="com.bjpowernode.spring.UtilDatePropertyEditor"></bean>
	 -->
</beans>

  然后更改测试用例即可,主要是构造BeanFactory时参数中添加刚添加的另一个Spring的配置文件:


String[] configLocations = new String[]{"applicationContext.xml","applicationContext-editor.xml"};
//factory = new ClassPathXmlApplicationContext("applicationContext.xml");//读取一个配置文件
factory = new ClassPathXmlApplicationContext(configLocations);//读取多个配置文件

  

     然后就可以正确地将字符串注入到Date类型的属性中。


6.  总结

  由于大量的构造器参数可能使程序变得笨拙,特别是当某些属性是可选的时候。因此通常情况下,Spring开发团队提倡使用setter注入。而且setter DI在以后的某个时候还可将实例重新配置(或重新注入)(JMX MBean就是一个很好的例子)。

  尽管如此,构造器注入,在某些特定的场景下还是很受青睐的。对于注入类型的选择并没硬性的规定。根据实际的业务场景,只要能适合你的应用,无论使用何种类型的DI都可以。

Spring的依赖注入