首页 > 代码库 > 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的依赖注入