依赖注入:让调用类对某一接口实现类的依赖关系由第三方注入,以移除调用类对某一接口实现类的依赖。
接下来将详细的向大家介绍Spring容器支持的三种依赖注入的方式以及具体配置方法:
• 属性注入方法
• 构造函数注入方法
• 工厂方法注入方法
一.属性注入
属性注入即通过setXXX()方法注入Bean的属性值或者依赖对象,由于属性注入方式具有可选择性和灵活高的优点,因此属性注入是实际中最常采用的注入方式。
Spring首先会调用bean的默认构造函数实例化bean对象,然后再通过反射的方法来调用set方法来注入属性值。
属性注入要求bean提供一个默认的构造函数,并且得为需要注入的属性提供set方法。
注意:所谓默认构造函数是指不带参的构造函数
JAVA中定义:
如果类中没有定义任何的构造函数,则JAVA虚拟机自动为其生成一个默认的构造函数。反之,如果类中显式的定义了构造函数,则JAVA虚拟机便不会在为其生成构造函数。
来看一段java示例代码:
Public class Car {
private String brand;
public void setBrand(String brand) {
this.brand = brand;
}
public String getBrand() {
return this.brand;
}
}
可以看到Car这个类有一个String类型的brand属性,并为其提供了setter和getter方法。
我们再来看看与之相对应的属性注入配置文件:
<bean id=“car” class=“com.jike.***.Car” >
<property name=“brand”>
<value>奔驰</value>
</property>
</bean>
其中每一个属性值对应一个property标签,name为属性的名称。
在bean实现类中拥有与其对应的实现方法setBrand()。
注意:Spring只会检查bean中是否有setter方法,而是否有对应的属性变量则不做具体要求,但按照约定俗成的规则我们最好为其设定相应的属性变量。
补充: Spring <property>元素的命名规范:
Spring配置文件中<property>元素所指定的属性名和Bean实现类的Setter方法满足Sun JavaBean的属性命名规范,即xxx的属性对应setXxx()的方法。一般情况下,java的属性变量名都以小写字母开头,但考虑到一些特殊意义的英文缩略词,java bean也允许一些大写字母开头的变量名。但必须满足特点的条件:
变量的前两个字母要么全部大写,要么全部小写
但以编程经验来说:最好属性名全部使用小写字母,方便编程。
对于使用属性注入方法来说,只能人为在配置文件中提供保证,而无法在语法级别提供保证。那么这时我们便要介绍如下这种注入方式:构造函数注入,通过这种方法便可以很好的满足要求。
二.构造函数注入
构造函数注入是除属性注入之外的另一种常用的注入方式,它保证一些必要的属性在Bean实例化时就得到了设置,并在实例化后就可以使用。
使用构造函数注入的前提是:bean必须提供带参的构造函数。
对于构造函数注入,配置文件可以有以下几种方式:
按类型匹配入参
按索引匹配入参
联合使用类型和索引匹配入参
通过自身类型反射匹配入参
1.按照类型匹配入参方式:
先来看一下bean 代码:
public class Car{
private String brand;
private double price;
public Car(String brand, double price) {
this.brand = brand;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
我们为bean编写配置文件
<bean id="Car" class="cn.lovepi.chapter02.reflect.Car">
<constructor-arg type="String">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg type="double">
<value>26666</value>
</constructor-arg>
</bean>
Spring的配置文件采用和元素标签顺序无关的配置策略。这种策略可以在一定程度上保证配置信息的确定性。
那么当bean中的构造函数的多个参数类型一样时,按照类型匹配入参的这种方式便会产生混淆,那么我们便来看一看另外一种方式:按照索引匹配入参。
2.按照索引匹配入参方法:
先来编写对应bean的代码:
public class Car{
private String brand;
private String corp;
private double price;
public Car(String brand, String corp, double price) {
this.brand = brand;
this.corp = corp;
this.price = price;
}
}
可以看到,在这个bean中的构造函数中存在两个String类型的参数,那么我们便使用索引来对其中的参数进行注入。
<bean id="Car" class="cn.lovepi.chapter02.reflect.Car">
<constructor-arg index="0" value="红旗CA72"/>
<constructor-arg index="1" value="中国一汽"/>
<constructor-arg index="2" value="2666"/>
</bean>
注意:
在属性注入时,Spring按java bean 的规范确定配置属性和对应的setter方法,并使用java 反射机制调用属性的setter方法完成属性注入。但java反射机制并不会记住构造函数的入参名。因此我们不能通过指定构造函数的入参名称来进行构造函数的配置,所以我们只能通过入参的类型及其索引来间接的完成构造函数的属性注入。
3.联合使用类型和索引匹配入参
在某些复杂的配置文件当中,需要使用type和index同时出马才能完成构造函数的参数注入。那么接下里我们看一下下面的这个例子:
public class Car{
private String brand;
private String corp;
private double price;
private int maxSpeed;
public Car(String brand, String corp, double price) {
this.brand = brand;
this.corp = corp;
this.price = price;
}
public Car(String brand, String corp, int maxSpeed) {
this.brand = brand;
this.corp = corp;
this.maxSpeed = maxSpeed;
}
}
在这个类中,有两个重载的构造函数,他们都有三个入参,在这种情况下使用type和index的方法都不能完成要求,这时候就需要联合他们两个属性一起使用了。
接下来我们来看一看相应的配置文件:
<bean id="Car" class="cn.lovepi.chapter02.reflect.Car">
<constructor-arg index="0" type="String">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg index="1" type="String">
<value>中国一汽</value>
</constructor-arg>
<constructor-arg index="2" type="int">
<value>200</value>
</constructor-arg>
</bean>
可以看到其实重点在于第三个入参的类型,所以我们在配置文件中对其指定了索引和类型,这样便使得Spring可以知道对那个构造函数进行参数注入了。
注意:
假如我们的配置文件中存在歧义问题,Spring容器是可以正常启动的。并不会报错,他将随机采用一个匹配的构造函数实例化bean。而随机选择的构造函数可能并不是用户所需要的,所以我们在编程时必须小心避免出现这种歧义情况的出现。
4.通过自身类型反射匹配入参
如果bean构造函数入参的类型是可辨别的,由于java反射机制可以获取构造函数入参的类型,即使构造函数的注入不提供类型和索引的信息,Spring依旧可以完成构造函数信息的注入。
下面的示例代码当中的Boss类的构造函数的入参类型就是可以辨别的:
public class Boss {
private String name;
private Car car;
private Office office;
public Boss(String name, Car car, Office office) {
this.name = name;
this.car = car;
this.office = office;
}
}
在其配置文件当中便可以直接配置入参值。
<bean id="Boss" class="cn.lovepi.chapter03.scope.Boss">
<constructor-arg>
<value>wang</value>
</constructor-arg>
<constructor-arg>
<ref bean="car"/>
</constructor-arg>
<constructor-arg>
<ref bean="office"/>
</constructor-arg>
</bean>
以上的几种方法都可以实现构造函数参数的注入,但是为了避免问题的发生,建议还是使用显示的index和type来配置构造函数的入参信息。
三.工厂方法注入方法
工厂方法是应用中被经常使用的设计模式,也是控制反转和单实例设计思想的主要实现方法。
工厂类负责创建一个或多个工厂类实例,工厂类方法一般以接口或抽象类变量的形式返回目标类实例。
工厂类对外屏蔽了目标类的实例化步骤,调用者甚至根本不用指定具体的目标类是什么。
由于Spring容器以框架的方法提供工厂方法的功能,并以透明的方式开放给开发者。因此很少需要手工编写工程方法。但在一些遗留系统或第三方类库中还是会碰到工厂方法,此时便可以使用Spring工厂注入的方法来进行Spring的注入。
Spring工厂注入的方法可以分为静态和非静态两种:
1.非静态工厂方式
有些工厂方法是非静态的,则必须实例化工厂类后才能调用工厂方法。
下面来看一个代码示例:
public class CarFactory{
public Car createHongQiCar(){
Car car=new Car();
car.setBrand("红旗CA72");
return car;
}
}
配置文件编写:
<bean id="carFactory" class="cn.lovepi.chapter02.reflect.CarFactory"/>
<bean id="car" factory-bean="carFactory"
factory-method="createHongQiCar">
</bean>
由于carFactory的工厂方法不是静态的,所以首先需要定义一个工厂类的bean,然后通过factory-bean这个属性来引用工厂bean实例。在通过属性factory-method来指定对应的工厂方法。
2.静态工厂方法
很多工厂类方法都是静态的,这意味在无须创建工厂类实例的情况下就可以调用工厂类方法。
因此静态工厂方法比非静态工厂方法的调用更加方便。
那么我们来看静态工厂方法的bean代码:
public class CarFactory{
public static Car createCar(){
Car car=new Car();
car.setBrand("红旗CA72");
return car;
}
}
则其配置文件为:
<bean id="car" class="cn.lovepi.chapter02.reflect.Car” factory-method="createCar"></bean>
总结:
Spring提供了三种可供选择的注入方式,在实际应用中,我们究竟应该选择哪种注入方式,并没有统一的标准,如下是一些可以参考的理由:
构造函数注入理由:
构造函数保证重要属性预先设置
无需提供每个属性Setter方法,减少类的方法个数
可更好的封装类变量,避免外部错误调用
属性注入理由:
属性过多时,构造函数变的臃肿可怕
构造函数注入灵活性不强,有时需要为属性注入null值
多个构造函数时,配置上产生歧义,复杂度升高
构造函数不利于类的继承和扩展
构造函数注入会引起循环依赖的问题
其实,Spring为我们注入参数提供了这些多方法那么他们必然有他们在某一问题上的优势性,那么我们只需按照我们具体的使用需求选择适合我们的方法来使用就好了,但在这里不推荐的方法就是工厂方法注入。
---------------------
作者:wangxin1248
来源:CSDN
原文:https://blog.csdn.net/icarus_wang/article/details/51588284
版权声明:本文为博主原创文章,转载请附上博文链接!