上文Spring学习手册(3)—— bean实例化配置主要使用xml配置方式介绍bean配置,并介绍了四种bean实例化方式:默认构造器、带参数的构造器、静态工厂方法、实例化的工厂方法。上文的代码演示中我们也注意到了依赖类的一些注入。本文将主要了解学习Spring的依赖注入。
源代码下载地址
一、Spring依赖注入
企业级应用都不是由单个对象构成的,甚至一个简单的应用也是由多个对象相互协作完成工作的。Spring使用依赖注入(DI)的技术使得一个对象所依赖(协作)的对象只能由构造器参数、工厂方法参数或set方法设置属性的方式关联。Spring支持两种依赖注入的方式:构造器注入、set方法注入。其中工厂方法使用与构造器注入相似的方式注入依赖。
- 构造器参数注入
- set方法注入
二、构造器参数注入
当我们定义bean时,若需向构造器注入参数需使用<constructor-arg>
标签将依赖参数传递给构造器。该标签有以下属性可以进行设置:
属性 | 说明 |
---|---|
ref | 指向对象的引用 |
value | 值类型,一般为java基本类型 |
type | 表明注入对象的类型 |
index | 参数列表序列,从0开始 |
name | 使用形参名标示(3.0版本以后支持) |
为了更好的解释这些属性的用法,我们构建一个简单的例子来更好的理解构造器注入。
package example;
//构造器注入例子类
public class ConstructorDIExample {
private String name;
private int age;
private Address address;
public ConstructorDIExample(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString(){
return name+" "+age+" "+address.toString();
}
}
package example;
//辅助地址类,无具体意义。为了演示对象引用注入
public class Address{
public String toString(){
return "I'm the address class";
}
}
配置文件如下:
<bean id="address" class="example.Address"/>
<bean id="constructorDIExample" class="example.ConstructorDIExample">
<constructor-arg type="String" value="Json"/>
<constructor-arg index="1" value="20"/>
<constructor-arg name="address" ref="address">
</bean>
我们可以看到ConstructorDIExample
除构造方法外,无别的方式设置内部属性,因此在定义bean时,我们将<constructor-arg>
标签包含在<bean>
标签内设置构造期参数信息。
根据传入参数值类型的不同需要使用不同的属性:
- 当传入值为另一个定义的bean时,使用
ref
属性,值为该bean的id名或name值; - 当传入值为基本数据类型或String时,使用value属性
为了指明每个参数值所对应的形参,提供有type
、index
、name
三个属性,简单说明如下:
- type用来指明参数具体类型,主要用于设置基本数据类型或String类型参数时,如value="true"设置,需要type来告诉Spring将该值转换成String还是boolean类型;
- index 按照形参顺序设置值信息,以0开始计算。Spring将根据具体形参类型将值转换成制定类型;
- name 使用形参名设置参数值信息。
- 当无参数设置无奇异时,可省略type或index或name的使用。如构造器只包含Food和Time两个类型参数,而Food和Time为非基本数据类型,此时可直接食用ref设置。
静态工厂方法和实例工厂方法这两种实例化方法的参数注入方式与构造器实例化方式的注入方式相同。
三、Setter方法参数注入
当我们完成bean定义,需要通过setter方法设置对象内部属性,此时我们使用<property>
标签告诉Spring如何设置内部属性。该标签有以下属性可以进行设置:
属性 | 说明 |
---|---|
ref | 指向对象的引用 |
value | 值类型,一般为java基本类型 |
name | 需要设置的属性名 |
以上属性的用法与构造器上的属性设置方式相似,这里我们也简单构建一个例子来帮助我们理解下Setter注入方式。
package example;
public class SetterDIExample {
private int age;
private String name;
private Address address;
public SetterDIExample() {
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString(){
return name+" "+age+" "+address.toString();
}
}
<bean id="address" class="com.liangwei.learnspring.Address"/>
<bean id="setterExample" class="com.liangwei.learnspring.SetterDIExample">
<property name="age" value="20"/>
<property name="name" value="Json"/>
<property name="address" ref="address"/>
</bean>
Setter注入方式中的name
、value
、ref
属性的用法和构造器注入方式中的使用和意义相同,这里就不再重复说明。
⚠️:name的值第一个字母大写加上“set”前缀必须和类提供的seter方法一致。例如:前面例子设置age属性转变后为:setAge,则SetterDIExample类里面必须有setAge方法。
四、配置信息详解
使用<constructor-arg/>
和<property/>
元素标签可以为类设置协同类对象或某具体类型值。
内置类型注入
Java内置类型包括int
、long
、boolean
、String
等,该小节我们以Setter方法属性注入的方式展示内置类型的用法
测试代码如下:
public class BuildInTypeExample {
/* 年龄*/
private int age;
/* 体重 */
private float weight;
/* 姓名 */
private String name;
/* 是否已婚 */
private boolean married;
public void setAge(int age) {
this.age = age;
}
public void setWeight(float weight) {
this.weight = weight;
}
public void setName(String name) {
this.name = name;
}
public void setMarried(boolean married) {
this.married = married;
}
public String toString(){
return "age: "+ age +" weight: "+weight+" name: "+name+" isMarride: "+married;
}
}
配置信息如下:
<bean id="buildInTypeExample" class="com.liangwei.learnspring.BuildInTypeExample">
<property name="age" value="20"/>
<property name="weight" value="50.23"/>
<property name="name" value="snow"/>
<property name="married" value="false"/>
</bean>
内置类型注入使用value
属性,Spring会将String类型的value转换成相应的类型。
引用其他bean
该配置模式可参考Setter方法参数注入的配置方式
内部bean
- 内部bean不需要id或name定义,即使定义了IOC容器也会忽略;
- IOC容器也会忽略
scope
标记(该属性会在后面讲解);
Person类定义
public class Person {
/* 年龄 */
private int age;
/* 姓名 */
private String name;
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String toString(){
return "age: "+ age+" name: "+ name;
}
}
InnerClassExample类定义:
public class InnerClassExample {
private Person person;
public void setPerson(Person person) {
this.person = person;
}
public String toString(){
return person.toString();
}
}
配置信息:
<!-- 内部类注入 -->
<bean id="innerClassExample" class="com.liangwei.learnspring.InnerClassExample">
<property name="person">
<bean class="com.liangwei.learnspring.Person">
<property name="age" value="21"/>
<property name="name" value="Json"/>
</bean>
</property>
</bean>
容器
使用<list/>
、<set/>
、<map/>
、<props/>
可以提供Java容器类型List
、Set
、Map
、Properties
。
1. 强类型容器
容器类注入方式语法方式相对简单,可参考代码示例进行学习。
public class CollectionDIExample {
private List<String> names;
private Map<String,Integer> scores;
private Set<String> className;
private Properties properties;
public void setNames(List<String> names) {
this.names = names;
}
public void setScores(Map<String, Integer> scores) {
this.scores = scores;
}
public void setClassName(Set<String> className) {
this.className = className;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public List<String> getNames() {
return names;
}
public Map<String, Integer> getScores() {
return scores;
}
public Set<String> getClassName() {
return className;
}
public Properties getProperties() {
return properties;
}
}
配置文件:
<!-- 容器类注入 -->
<bean id="collectionDIExample" class="com.liangwei.learnspring.CollectionDIExample">
<property name="names">
<list>
<value>Json</value>
<value>Snow</value>
<value>Joe</value>
</list>
</property>
<property name="scores">
<map>
<entry key="Json" value="87"/>
<entry key="Snow" value="90"/>
<entry key="Joe" value="89"/>
</map>
</property>
<property name="className">
<set>
<value>Class One</value>
<value>Class Two</value>
<value>Class Three</value>
</set>
</property>
<property name="properties">
<props>
<prop key="baidu">www.baidu.com</prop>
<prop key="google">www.google.com</prop>
<prop key="bing">cn.bing.com</prop>
</props>
</property>
</bean>
2.容器合并
为演示容器合并注入方式,我们以Properties类进行举例说明,其他如Map、List等以此类推。
public class CollectionMergeExample {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public Properties getProperties() {
return properties;
}
}
配置数据:
<!-- 容器合并注入 -->
<bean id="parentCollection" class="com.liangwei.learnspring.CollectionMergeExample">
<property name="properties">
<props>
<prop key="baidu">www.baidu.com</prop>
<prop key="google">www.google.com</prop>
</props>
</property>
</bean>
<bean id="childCollection" parent="parentCollection">
<property name="properties">
<props merge="true">
<prop key="google">g.cn</prop>
<prop key="bing">cn.bing.com</prop>
</props>
</property>
</bean>
运行以下代码你将看到如下输出:
{google=www.google.com, baidu=www.baidu.com}
{google=g.cn, baidu=www.baidu.com, bing=cn.bing.com}
当使用容器合并注入时,我们注意到以下几点:
- 定义一个bean,其
parent
属性指向需要合并的bean的名字; - 需要合并的容器设置
merge
属性为true;
当我们如上设置时,Spring的IOC容器将会合并Java容器的数据设置,如例子中所体现的那样google的值被覆盖,并在原有的基础上增加了bing的数据。若merge属性不尽兴设置或设置为false,则不会进行数据合并,此时childCollection的数据输出将不包含baidu的数据键值对。有兴趣的可以更改代码试试。
Null注入
当我们想为一个类型赋值为null时,我们需要使用<null/>
标签。而使用“”往往是将空字符串为该参数赋值,如下代码演示所示:
public class NullExample {
private String nullString;
private String emptyString;
public String getNullString() {
return nullString;
}
public void setNullString(String nullString) {
this.nullString = nullString;
}
public String getEmptyString() {
return emptyString;
}
public void setEmptyString(String emptyString) {
this.emptyString = emptyString;
}
}
配置文件:
<bean id="nullExample" class="com.liangwei.learnspring.NullExample">
<property name="nullString">
<null/>
</property>
<property name="emptyString" value=""/>
</bean>
五、总结
本文详细介绍了IOC注入的配置方式:首先先概括性介绍IOC容器的两种主要注入方式:构造器注入和Setter方法注入,然后就各种常见数据类型和容器注入配置方式进行了解释和代码演示。至此我们通过XML文件配置方式可以实现bean定义、依赖对象注入和基本数据注入等工作。