Spring依赖注入原理分析
在分析原理之前我们先回顾下依赖注入的概念:
我们常提起的依赖注入(dependency injection)和控制反转(inversion of control)是同一个概念。具体含义是:当某个角色(可能是一个java实例,调用者)需要另一个角色(另一个java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由spring容器来完成,然后注入调用者,因此也称为依赖注入。
其实简单的说,依赖注入起到的作用就是讲对象之间的依赖关系从原先的代码中解耦出来,通过配置文件或注解等方式加上spring框架的处理让我们对依赖关系灵活集中的进行管理。
下面谈谈spring是如何实现反转模式ioc或依赖注入模式di:
平时,我们需要生成一个对象,使用new语法,如一个类为a
public class a{
public void mymethod(){
system.out.println("hello");
}
}
如果我们在 b中调用a,那么如下代码:
public class b{
public void invoke(){
a a = new a();
a.mymethod();
}
}
每次执行invoke方法时,都要生成一个a对象,如果a对象代码较长,这是费时的事
情。于是有如下写法:
public class b{
a a = new a();
public void invoke(){
a.mymethod();
}
}
将a对象变成b的类属性。 如果我们不想在b中实现a的实例,也就是不想立即new a(),而是想通过外界传入, 注意,如果你想知道为什么,这里涉及到设计模式以及解耦等因素,进一步感兴趣者可学习 本站的gof 23 种设计模式。
如果想让a的实例从外界传入,有两种写法:
public class b{
a a;
public void seta(a a){
this.a = a;
}
public a geta(){
return a;
}
public void invoke(){
a.mymethod();
}
}
这种写法,a并没有被实例化,需要通过外界调用seta方法,将a的对象实例赋入b中. 或者通过b的构造函数传入,如下:
public class b{
a a;
public b(a a){
this.a = a;
}
public void invoke(){
a.mymethod();
}
}
上述两种写法在编程中是经常发生的,b作为调用者,a是被调用者,a的实例化不在 调用者b内部中完成,而是通过构造函数或setxxx方法赋值进来,这种方式我们称为依赖 性注射(ioc 模式),b 和a 的依赖联系是通过构造函数或setxxx 方法赋值进来,这样, 最大程度解耦了调用者b和被调用者a之间的耦合联系。
spring如何实现依赖注射?
上文提到:a的实例化不在调用者b内部中完成,而是通过构造函数或setxxx 方法赋 值进来,spring实际就是完成这个赋值的过程。 为了让spring自动完成b代码中的a的实例化,需要通过配置文件告诉spring有关a 的类的属性,这个配置是applicationcontext.xml文件。 在 applicationcontext.xml中,我们先定义javabeans为b的配置:
<beans>
<bean id="b" class="springsimple.b"/>
</beans>
这是最常用的javabeans的定义,id相当于对象名,当前文件应该是唯一。后来spring使用@component替代。
再在applicationcontext.xml定义a的配置如下:
<beans>
<bean id="b" class="springsimple.b"/>
<bean id="a" class="springsimple.a"/>
</beans>
这样我们告诉spring我们有两个javabeans,现在解决关键问题,b代码中还调用了a, 那么如何让spring将a的实例注射到b中?使用spring配置的property语法。具体配置如 下:
<beans>
<bean id="b" class="springsimple.b">
<property name="a"><ref local="a" /></property> <!— 增加这一行-->
</bean>
<bean id="a" class="springsimple.a" />
</beans>
增加一行说明:b 的属性a 指向了a,这样,spring 会知道b 中属性a 的实例就是 springsimple.a,在b实例化时将会将b中的a 实现实例化,这是通过seta方法注射进入。 注意,property name="a"中的a 是seta字符中去掉set 后的字符串,这个字符串第一个 必须是小写,例如,如果b中有setonea方法,那么,配置文件应该是property name="onea"。
在spring boot以后版本已经可以使用@autowire进行自动匹配,无需如此繁琐配置了。
参考: