理解分析
依赖注入(Dependency Injection)即控制反转(Inversion of Control ,IoC),是spring的核心机制。
在传统的程序设计过程中,当某个实例(调用者)需要另一个实例(被调用者)时,通常由调用着来创建被调用者的实例。而在依赖注入的模式下,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也称为依赖注入。这说明Spring采用动态、灵活的方式来管理各种对象。
在机房收费系统里,当我们调用一个类的时候都是直接new的,就好比原始社会里需要斧子,就得自己去创建。
后面学了设计模式后,我们就运用了简单工厂设计模式来把这些类放到工厂里,用到的时候直接通过过程来寻找。就好比工业社会,工厂出现,我们可以直接购买,无需自己生产。但我们还是要找到商店,也就是还要定位,调用者和工厂耦合在一起了。
之后伟大的马克思为我们设想了共产主义社会,我们都不用定位工厂,坐等社会提供即可了。也就是说我们不用关心斧子的生产、定位工厂,实例之间的依赖关系由IOC容器负责软了,等待Spring依赖注入。
注入方式
经过一翻理解,算是把依赖注入理解了。依赖注入有两种方式:
设值注入
IOC容器使用属性的setter方法来注入被依赖的实例。
栗子:
Person:
package com.demo.person;
public interface Person {
public void userAxe();
}
Axe:
package com.demo.axe;
public interface Axe {
public String chop();
}
AxeImpl:
package com.demo.axe;
public class AxeImpl implements Axe {
public String chop() {
return "砍柴又慢又沉!";
}
}
PersonImpl
package com.demo.person;
import com.demo.axe.Axe;
public class PersonImpl implements Person {
private Axe axe;
public void userAxe() {
System.out.println(axe.chop());
}
public void setAxe(Axe axe) {
this.axe = axe;
}
}
spring配置文件beans.xml
<?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.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">
<!-- 配置person实例,其实现类是PersonImpl --!>
<bean id="perSon" class="com.demo.person.PersonImpl">
<property name="axe" ref="stoneAxe"/>
</bean>
<bean id="stoneAxe" class="com.demo.axe.AxeImpl"/>
</beans>
测试类:
package test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.demo.person.Person;
public class BeanTest {
public static void main(String[] args) {
BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
Person perSon=(Person)factory.getBean("perSon");
perSon.userAxe();
}
}
输出结果:
构造注入
IOC容器使用构造器来注入被依赖的实例。这种方式在构造实例时,已经为其完成了依赖关系的初始化。
栗子:接口和AxeImpl的代码和设置注入的相同
PersonImpl
package com.demo.person;
import com.demo.axe.Axe;
public class PersonImpl implements Person {
private Axe axe;
//默认的构造器
public PersonImpl(){
}
//构造注入所需的带参数的构造器
public PersonImpl(Axe axe){
this.axe=axe;
}
//实现person几口的useAxe方法
public void userAxe() {
//表明person对象依赖于axe对象
System.out.println(axe.chop());
}
}
spring配置文件beans.xml
<?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.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="perSon" class="com.demo.person.PersonImpl">
<constructor-arg ref="stoneAxe"/>
</bean>
<bean id="stoneAxe" class="com.demo.axe.AxeImpl"/>
</beans>
对比两种注入方式
在配置文件中设值注入用<property>标签实现,而构造注入用<constructor-arg>标签实现。
设值注入与传统的JavaBean的写法更为相似,更容易理解和接受,setter方法设定依赖关系显得更加直观自然。对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿难以阅读。而构造器在构造实例是,就要完成依赖关系的初始化,这样会影响系统的性能。而设值注入可以避免。在有某些属性可选的情况下,多参数的构造器更加笨重。
构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。对于依赖关系无须变化的Bean,因为没有 setter方法,所有的依赖关系全部在构造器内设定。因此,无须担心后续的代码对依赖关系产生破坏。构造注入的组件内部依赖关系完全透明,更符合高内聚的原则。
总结
依赖注入就是使用spring容器来管理实例之间的依赖关系,这样调用者就可以不用自己去new被调用者,也不用自己去定位工厂,坐等spring提供即可。