详解Spring的核心机制依赖注入
详解spring的核心机制依赖注入
对于一般的java项目,他们都或多或少有一种依赖型的关系,也就是由一些互相协作的对象构成的。spring把这种互相协作的关系称为依赖关系。如a组件调用b组件的方法,可称a组件依赖于b组件,依赖注入让spring的bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起
一、理解依赖注入
依赖注入(dependency injection) = 控制反转(inversion ofcontrol,ioc):当某个java实例(调用者)需另一个java实例(被调用者)时,在依赖注入模式下,创建被调用者的工作不再由调用者来完成,因此称为 控制反转 ;创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也称为 依赖注入
依赖注入:程序运行过程中,如需另一个对象协作(调用它的方法、访问他的属性)时,无须在代码中创建被调用者,而是依赖于外部容器的注入。spring的依赖注入对调用者和被调用者几乎无任何要求,完全支持对pojo间依赖关系的管理
依赖注入
设值注入:ioc容器使用属性的setter方法来注入被依赖的实例
构造注入:ioc容器使用构造器来注入被依赖的实例
理解依赖注入:
一个人(java实例,调用者)需要一把斧子(java实例,被调用者)
在原始社会里,几乎没有社会分工;需要斧子的人(调用者)只能自己去磨一把斧子(被调用者);对应情形为:java程序里的调用者自己创建被调用者,通常采用new关键字调用构造器创建一个被调用者
进入工业社会,工厂出现了,斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程;对应简单工厂设计模式,调用者只需定位工厂,无须管理被调用者的具体实现
进入“共产主义”社会,需要斧子的人甚至无须定位工厂,“坐等”社会提供即可;调用者无须关心被调用者的实现,无须理会工厂,等待spring依赖注入
二、设值注入
person接口: public interface person { // 定义使用斧子的方法 public void useaxe(); } spring推荐面向接口编程,这样可更好地让规范和实现分离,从而提供更好的解耦;对于一个java ee应用,不管是dao组件还是业务逻辑组件,都应该先定义一个接口,该接口定义了该组件应实现的功能,但功能的实现则由其实现类提供
axe接口: public interface axe { // axe接口里有个砍的方法 public string chop(); }
实现axe: public class stoneaxe implements axe { public string chop() { return "石斧砍柴好慢s"; } }
bean.xml:
<?xml version="1.0" encoding="utf-8"?> <!-- spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 --> <beans xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 配置chinese实例 --> <bean id="chinese" class="com.chinese"> <!-- 将stoneaxe注入给axe属性 --> <property name="axe" ref="stoneaxe" /> </bean> <!-- 配置stoneaxe实例 --> <bean id="stoneaxe" class="com.stoneaxe" /> </beans>
测试类:
public class beantest { public static void main(string[] args) { // 创建spring容器 applicationcontext ctx = new classpathxmlapplicationcontext("bean.xml"); // 获取chinese实例 person p = ctx.getbean("chinese", person.class); // 调用useaxe()方法 p.useaxe(); } }
spring采用xml作为配置文件,从spring2.0开始,spring即可采用dtd来定义配置文件的语义约束,也可用xml schema来定义(可利用spring配置文件的扩展性,进一步简化spring配置;还提供了一些新的标签;还允许程序员开发自定义的配置文件标签,让其他开发人员在spring配置文件中使用这些标签:通常由第三方供应商完成);
可在spring的projects目录的org.springframwork.beans、org.springframework.context等目录的\src\main\resources路径下找到各种*.xsd文件(spring配置文件的xml schema语义约束文件)
在配置文件中,spring配置bean实例通常会指定:
id :指定该bean的唯一标识,程序通过id属性值来访问该bean实例
class :指定该bean的实现类, 此处不可再用接口 ,必须使用实现类spring容器用xml解析器读取该属性,并利用反射来创建该实现类的实例
spring会自动接管每个<bean.../>定义里的<property.../>元素定义,spring会在调用无参构造器后、创建默认bean实例后、调用对应的setter方法为程序注入属性值
每个bean的id属性是该bean的唯一标识,程序通过id属性访问bean,bean与bean的依赖关系也通过id属性关联
bean与bean间的依赖关系由spring管理,spring采用setter方法为目标bean注入所依赖的bean,这种方式被称为 设值注入
使用spring ioc容器的3个基本要点:
应用程序的各组件面向接口编程
应用程序的各组件不再由程序主动产生,而是由spring容器来负责产生、并初始化
spring采用配置文件、或annotation来管理bean的实现类、依赖关系,spring容器则根据配置文件、利用反射来创建实例,并为之注入依赖关系
三、构造注入
在构造实例时,已经为其完成了依赖关系的初始化。这种利用构造器来设置依赖关系的方式,被称为构造注入
public class chinese implements person { private axe axe; // 默认的构造器 public chinese() { } // 构造注入所需的带参数的构造器 public chinese(axe axe) { this.axe = axe; } // 实现person接口的useaxe方法 @override public void useaxe() { // 调用axe的chop()方法 // 表明person对象依赖于axe对象 system.out.println(axe.chop()); } }
无须再提供设置axe属性的setter方法,仅仅提供了一个带axe属性的构造器,spring将通过该构造器为chinese注入所依赖的bean实例
<?xml version="1.0" encoding="utf-8"?> <!-- spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 --> <beans xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 配置chinese实例 --> <bean id="chinese" class="com.chinese"> <!-- 使用构造注入,为chinese实例注入stoneaxe实例 --> <constructor-arg ref="stoneaxe" /> </bean> <!-- 配置stoneaxe实例 --> <bean id="stoneaxe" class="com.stoneaxe" /> </beans>
<constructor-arg.../>元素指定了一个构造器参数,该参数类型是axe,这指定spring调用chinese类里带一个axe参数的构造器来创建chinese实例,因为使用了有参数的构造器创建实例,所以当bean实例被创建完成后,该bean的依赖关系已经设置完成
配置<constructor-arg.../>元素时可指定一个index属性,用于指定该构造参数值将作为第几个构造参数值;如index=“0”表明该构造参数值将作为第一个构造参数
执行效果与使用设置注入时的执行效果完全一样。区别在于:创建person实例中axe属性的时机不同-----设置注入是先通过无参数的构造器创建一个bean实例,然后调用对应的setter方法注入依赖关系;而构造注入则直接调用有参数的构造器,当bean实例创建完成后,已经完成了依赖关系的注入
四、两种注入方式的对比
相比之下,设值注入有如下优点:
与传统的javabean的写法更相似,程序开发人员更容易理解、接受、通过setter方法设定依赖关系显得更加直观、自然
对于复杂的依赖关系,若采用构造注入,会导致构造器过于臃肿,难以阅读;spring在创建bean实例时,需同时实例化其依赖的全部实例,因而导致性能下降
尤其是在某些属性可选的情况下,多参数的构造器更加笨重
相比之下,构造注入有如下优点:
可在构造器中决定依赖关系的注入顺序,优先依赖的优先注入
对于依赖关系无需变化的bean,构造注入更有用处;因为没有setter方法,所有的依赖关系全部在构造器内设定。因此,无须担心后续的代码对依赖关系产生破坏
依赖关系只能在构造器设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则
一般采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑设值注入
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!