什么是依赖注入

什么是依赖注入

Spring 能有效地组织J2EE应用各层的对象。无论
控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调、执行。Spring将各层的对象以松耦合的方式组织在一起,Action对象无须关心Service对象的详细实现,Service对 象无须关心持久层对象的详细实现,各层对象的调用全然面向接口。当系统须要重构时,代码的改写量将大大降低。

上面所说的一切都得宜于Spring的核心机制,
依赖
注入
依赖
注入让bean与bean之间以配置文件组织在一起,而不是以硬编码的方式耦合在一起。理解
依赖
注入

依赖注入(Dependency Injection)和控制反转(Inversion of Control)同一个概念。详细含义:当某个角色(可能一个Java实例,调用者)须要还有一个角色(还有一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完毕,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完毕,然后注入调用者,因此也称为依赖注入

无论

依赖
注入,还是控制反转,都说明Spring採用动态、灵活的方式来管理各种对象。对象与对象之间的详细实现互相透明。在理解
依赖
注入之前,看例如以下这个问题在各种社会形态里怎样解决:一个人(Java实例,调用者)须要一把斧子(Java实例,被调用者)。

(1)原始社会里,差点儿没有社会分工。须要斧子的人(调用者)仅仅能自己去磨一把斧子(被调用者)。相应的情形为:Java程序里的调用者自己创建被调用者。

(2)进入工业社会,工厂出现。斧子不再由普通人完毕,而在工厂里被生产出来,此时须要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。相应Java程序的简单工厂的设计模式。

(3)进入“按需分配”社会,须要斧子的人不须要找到工厂,坐在家里发出一个简单指令:须要斧子。斧子就自然出如今他面前。相应Spring的
依赖
注入

第一种情况下,Java实例的调用者创建被调用的Java实例,必定要求被调用的Java类出如今调用者的代码里。无法实现二者之间的松耦合。

另外一种情况下,调用者无须关心被调用者详细实现过程,仅仅须要找到符合某种标准(接口)的实例,就可以使用。此时调用的代码面向接口编程,能够让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者须要自己定位工厂,调用者与特定工厂耦合在一起。

第三种情况下,调用者无须自己定位工厂,程序执行到须要被调用者时,系统自己主动提供被调用者实例。其实,调用者和被调用者都处于Spring的管理下,二者之间的
依赖关系由Spring提供。

所谓
依赖
注入
指程序执行过程中,假设须要调用还有一个对象协助时,无须在代码中创建被调用者,而是
依赖于外部的
注入。Spring的
依赖
注入对调用者和被调用者差点儿没有不论什么要求,全然支持对POJO之间
依赖关系的管理。
依赖
注入通常有两种:

·设值
注入

·构造
注入

设值注入

  设值
注入
指通过setter方法传入被调用者的实例。这样的
注入方式简单、直观,因而在Spring的
依赖
注入里大量使用。看以下代码,
Person的接口

//定义Person接口
public interface Person
{
//Person接口里定义一个使用斧子的方法
public void useAxe();
}

然后
Axe的接口
//定义Axe接口
public interface Axe
{
//Axe接口里有个砍的方法
public void chop();
}

Person的实现类
//Chinese实现Person接口

public class Chinese implements Person
{
//面向Axe接口编程,而不是详细的实现类
private Axe axe;
//默认的构造器
public Chinese()
{}
//设值注入所需的setter方法
public void setAxe(Axe axe)
{
this.axe = axe;
}
//实现Person接口的useAxe方法
public void useAxe()
{
System.out.println(axe.chop());
}
}


Axe的第一个实现类
//Axe的第一个实现类 StoneAxe

public class StoneAxe implements Axe
{
//默认构造器
public StoneAxe()
{}
//实现Axe接口的chop方法
public String chop()
{
return “石斧砍柴好慢”;
}
}


以下採用Spring的配置文件将Person实例和Axe实例组织在一起。配置文件例如以下所看到的:
<!– 以下标准的XML文件头 –>
<?xml version=”1.0″ encoding=”gb2312″?>
<!– 以下一行定义Spring的XML配置文件的dtd –>
“http://www.springframework.org/dtd/spring-beans.dtd”>
<!– 以上三行对全部的Spring配置文件都同样的 –>
<!– Spring配置文件的根元素 –>
<BEANS>
<!—定义第一bean,该bean的idchinese, class指定该bean实例的实现类 –>
<BEAN class=lee.Chinese id=chinese>
<!– property元素用来指定须要容器注入的属性,axe属性须要容器注入此处设值注入,因此Chinese类必须拥有setAxe方法 –>
<property name=”axe”>
<!– 此处将还有一个bean的引用注入给chinese bean –>
<REF local=””stoneAxe”/”>
</property>
</BEAN>
<!– 定义stoneAxe bean –>
<BEAN class=lee.StoneAxe id=stoneAxe />
</BEANS>

从配置文件中,能够看到Spring管理bean的机灵性。bean与bean之间的
依赖关系放在配置文件中组织,而不是写在代码里。通过配置文件的 指定,Spring能精确地为每一个bean
注入属性。因此,配置文件中的bean的class元素,不能只
接口,而必须
真正的实现类。

Spring会自己主动接管每一个bean定义里的property元素定义。Spring会在运行无參数的构造器后、创建默认的bean实例后,调用相应 的setter方法为程序
注入属性值。property定义的属性值将不再由该bean来主动创建、管理,而改为被动接收Spring的
注入

每一个bean的id属性
该bean的惟一标识,程序通过id属性訪问bean,bean与bean的
依赖关系也通过id属性完毕。

以下看主程序部分:

public class BeanTest
{
//主方法,程序的入口
public static void main(String[] args)throws Exception
{
//由于独立的应用程序,显式地实例化Spring的上下文。
ApplicationContext ctx = new FileSystemXmlApplicationContext(“bean.xml”);
//通过Person bean的id来获取bean实例,面向接口编程,因此
//此处强制类型转换为接口类型
Person p = (Person)ctx.getBean(“chinese”);
//直接运行Person的userAxe()方法。
p.useAxe();
}
}

程序的执行结果例如以下:

石斧砍柴好慢

主程序调用Person的useAxe()方法时,该方法的方法体内须要使用Axe的实例,但程序里没有不论什么地方将特定的Person实例和Axe实 例耦合在一起。或者说,程序里没有为Person实例传入Axe的实例,Axe实例由Spring在执行期间动态
注入

Person实例不仅不须要了解Axe实例的详细实现,甚至无须了解Axe的创建过程。程序在执行到须要Axe实例的时候,Spring创建了Axe 实例,然后
注入给须要Axe实例的调用者。Person实例执行到须要Axe实例的地方,自然就产生了Axe实例,用来供Person实例使用。

调用者不仅无须关心被调用者的实现过程,连工厂定位都能够省略(真是按需分配啊!)。以下也给出使用Ant编译和执行该应用的简单脚本:

<?xml version=”1.0″?>
<!– 定义编译该项目的基本信息–>
<PROJECT name=”spring” default=”.” basedir=”.”>
<!– 定义编译和执行该项目时所需的库文件 –>
<PATH id=classpath>
<!– 该路径下存放spring.jar和其它第三方类库 –>
<FILESET dir=../../lib>
<INCLUDE name=”*.jar” />
</FILESET>
<!– 同一时候还须要引用已经编译过的class文件–>
<PATHELEMENT path=”.” />
</PATH>
<!– 编译所有的java文件–>
<TARGET description=”Compile all source code” name=”compile”>
<!– 指定编译后的class文件的存放位置 –>
<JAVAC debug=”true” destdir=”.”>
deprecation=”false” optimize=”false” failonerror=”true”>
<!– 指定须要编译的源文件的存放位置 –>
<SRC path=”.” />
<!– 指定编译这些java文件须要的类库位置–>
<CLASSPATH refid=”classpath” />
</JAVAC>
</TARGET>
<!– 执行特定的主程序 –>
<TARGET description=”run the main class” name=”run” depends=”compile”>
<!– 指定执行的主程序:lee.BeanTest。–>
<JAVA failonerror=”true” fork=”yes” classname=”lee.BeanTest”>
<!– 指定执行这些java文件须要的类库位置–>
<CLASSPATH refid=”classpath” />
</JAVA>
</TARGET>
</PROJECT>

假设须要改写Axe的实现类。或者说,提供还有一个实现类给Person实例使用。Person接口、Chinese类都无须改变。仅仅需提供还有一个Axe的实现,然后对配置文件进行简单的改动就可以。

Axe的还有一个实现例如以下:

//Axe的还有一个实现类 SteelAxe
public class SteelAxe implements Axe
{
//默认构造器
public SteelAxe()
{}
//实现Axe接口的chop方法
public String chop()
{
return “钢斧砍柴真快”;
}
}

然后,改动原来的Spring配置文件,在当中添加例如以下一行:
<!– 定义一个steelAxe bean–>
<BEAN class=lee.SteelAxe id=steelAxe />

该行又一次定义了一个Axe的实现:SteelAxe。然后改动chinese bean的配置,将原来传入stoneAxe的地方改为传入steelAxe。也就是将
<REF local=””stoneAxe”/”>

改成
<REF local=””steelAxe”/”>

此时再次运行程序,将得到例如以下结果:

钢斧砍柴真快

Person与Axe之间没有不论什么代码耦合关系,bean与bean之间的
依赖关系由Spring管理。採用setter方法为目标bean
注入属性的方式,称为设值
注入

业务对象的更换变得相当简单,对象与对象之间的
依赖关系从代码里分离出来,通过配置文件动态管理。

构造注入

  所谓构造
注入,指通过构造函数来完毕
依赖关系的设定,而不是通过setter方法。对前面代码Chinese类做简单的改动,改动后的代码例如以下:

//Chinese实现Person接口
public class Chinese implements Person
{
//面向Axe接口编程,而不是详细的实现类
private Axe axe;
//默认的构造器
public Chinese()
{}
//构造注入所需的带參数的构造器
public Chinse(Axe axe)
{
this.axe = axe;
}
//实现Person接口的useAxe方法
public void useAxe()
{
System.out.println(axe.chop());
}
}

此时无须Chinese类里的setAxe方法,构造Person实例时,Spring为Person实例
注入
依赖的Axe实例。构造
注入的配置文件也需做简单的改动,改动后的配置文件例如以下:
<!– 以下标准的XML文件头 –>
<xml version=”1.0″ encoding=”gb2312″?>
<!– 以下一行定义Spring的XML配置文件的dtd –>
“http://www.springframework.org/dtd/spring-beans.dtd”>
<!– 以上三行对全部的Spring配置文件都同样的 –>
<!– Spring配置文件的根元素 –>
<BEANS>
<!—定义第一个bean,该bean的idchinese, class指定该bean实例的实现类 –>
<BEAN class=lee.Chinese id=chinese>
</BEAN>
<!– 定义stoneAxe bean –>
<BEAN class=lee.SteelAxe id=steelAxe />
</BEANS>

运行效果与使用steelAxe设值
注入时的运行效果全然一样。差别在于:创建Person实例中Axe属性的时机不同——设值
注入
现创建一个默认的bean实例,然后调用相应的构造方法
注入
依赖关系。而构造
注入则在创建bean实例时,已经完毕了
依赖关系的
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/119268.html原文链接:https://javaforall.net

(0)
上一篇 2021年11月13日 上午11:00
下一篇 2021年11月13日 下午12:00


相关推荐

  • NLP中的 POS Tagging 和Chunking

    NLP中的 POS Tagging 和Chunking这篇文章将使用 NLTK 向您解释 NLP 中的词性标注 POS Tagging 和组块分析 Chunking 过程 词袋模型 Bag of Words 无法捕捉句子的结构 有时也无法给出适当的含义 词性标注和组块分析帮助我们克服了这个弱点 NLTK 多用于英文文本 所以这篇以英文解释 词性标注 POS Tagging 词性可以解释为一个词在句子中的使用方式 词性有八个主要组成部分 名词 代词 形

    2026年3月19日
    1
  • Spring的Bean实例化、属性注入、对象注入、复杂注入(基于xml配置方式)

    Spring的Bean实例化、属性注入、对象注入、复杂注入(基于xml配置方式)

    2021年9月26日
    43
  • plantuml 依赖_遇见PlantUML

    plantuml 依赖_遇见PlantUML前言来到公司实习也快一个月了,最大的体会就是,虽然大部分时间做的是简单的增删该查,但不同于在学校时写的Demo,你要充分考虑程序的鲁棒性(健壮性)、可扩展性(可维护性)、时间/空间复杂度等。因为是要实际上线的项目,你需要面面俱到,对团队负责。于是决定在完成组里任务之余,花时间提高自己的的编码规范、多思考程序设计的可扩展性、性能是否可观等。我觉得开发工程师和码农之间的区别是,不仅是复制粘贴和以实现功…

    2025年6月23日
    5
  • 即梦ai照片变脸使用教程最新

    即梦ai照片变脸使用教程最新

    2026年3月13日
    2
  • 科目一2013年

    科目一2013年我 17 号考试 旧题库看完了 这几天在看书和法规 根据前几天热心网友的反映 我做个梳理 希望对大家有用 也希望我能通过 如果只是把旧题库死记硬背下来 现在可能考 40 分 如果把旧题库全部做会 并能理解 可以考 60 分以上 如果你再把书看一遍 可以考 7080 分 如果你再把新法规看一遍 在网上找一下书上没有的标志图 车内仪表以及基本操作熟悉下 再钻研下 2013 元旦之后这几天前

    2026年3月19日
    4
  • tcp粘包是怎么产生的_tcp报文格式

    tcp粘包是怎么产生的_tcp报文格式tcp粘包是怎么产生的?1、什么是tcp粘包?发送方发送的多个数据包,到接收方缓冲区首尾相连,粘成一包,被接收。2、原因TCP协议默认使用Nagle算法可能会把多个数据包一次发送到接收方。应用程读取缓存中的数据包的速度小于接收数据包的速度,缓存中的多个数据包会被应用程序当成一个包一次读取。3、处理方法发送方使用TCP_NODELAY选项来…

    2022年8月11日
    11

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号