Bean 定义可以包含很多配置信息,包括构造函数参数、属性值和容器的一些具体信息,如初始化方法、销毁方法等。子 Bean 可以继承父 Bean 的配置数据,根据需要,子 Bean 可以重写值或添加其它值。
需要注意的是,Spring Bean 定义的继承与 Java 中的继承无关。您可以将父 Bean 的定义作为一个模板,其它子 Bean 从父 Bean 中继承所需的配置。
在配置文件中通过 parent 属性来指定继承的父 Bean。
下面使用 Eclipse IDE 演示 Bean 继承,步骤如下:
- 创建 SpringDemo 项目,并在 src 目录下创建 net.biancheng 包。
- 添加相应的 jar 包,可以参考《第一个Spring程序》一节。
- 在 net.biancheng 包下创建 HelloWorld、HelloChina 和 MainApp 类。
- 在 src 目录下创建 Spring 配置文件 Beans.xml。
- 运行 SpringDemo 项目。
HelloWorld 类代码如下。
package net.biancheng;
public class HelloWorld {
private String message1;
private String message2;
public void setMessage1(String message) {
this.message1 = message;
}
public void setMessage2(String message) {
this.message2 = message;
}
public void getMessage1() {
System.out.println("World Message1 : " + message1);
}
public void getMessage2() {
System.out.println("World Message2 : " + message2);
}
}
HelloChina 类代码如下。
package net.biancheng;
public class HelloChina {
private String message1;
private String message2;
private String message3;
public void setMessage1(String message) {
this.message1 = message;
}
public void setMessage2(String message) {
this.message2 = message;
}
public void setMessage3(String message) {
this.message3 = message;
}
public void getMessage1() {
System.out.println("China Message1 : " + message1);
}
public void getMessage2() {
System.out.println("China Message2 : " + message2);
}
public void getMessage3() {
System.out.println("China Message3 : " + message3);
}
}
在配置文件中,分别为 HelloWorld 中的 message1 和 message2 赋值。使用 parent 属性将 HelloChain 定义为 HelloWorld 的子类,并为 HelloChain 中的 message1 和 message3 赋值。Beans.xml 文件代码如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="helloWorld" class="net.biancheng.HelloWorld">
<property name="message1" value="Hello World!" />
<property name="message2" value="Hello World2!" />
</bean>
<bean id="helloChina" class="net.biancheng.HelloChina"
parent="helloWorld">
<property name="message1" value="Hello China!" />
<property name="message3" value="Hello China3!" />
</bean>
</beans>
MainApp 类代码如下。
package net.biancheng;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
objA.getMessage1();
objA.getMessage2();
HelloChina objB = (HelloChina) context.getBean("helloChina");
objB.getMessage1();
objB.getMessage2();
objB.getMessage3();
}
}
运行结果如下。
World Message1 : Hello World!
World Message2 : Hello World2!
China Message1 : Hello China!
China Message2 : Hello World2!
China Message3 : Hello China3!
由结果可以看出,我们在创建 helloChina 时并没有给 message2 赋值,但是由于 Bean 的继承,将值传递给了 message2。
您可以创建一个 Bean 定义模板,该模板只能被继承,不能被实例化。创建 Bean 定义模板时,不用指定 class 属性,而是指定 abstarct=“true” 将该 Bean 定义为抽象 Bean,如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="beanTeamplate" abstract="true">
<property name="message1" value="Hello World!" />
<property name="message2" value="Hello World2!" />
<property name="message3" value="Hello World3!" />
</bean>
<bean id="helloChina" class="net.biancheng.HelloChina"
parent="beanTeamplate">
<property name="message1" value="Hello China!" />
<property name="message3" value="Hello China!" />
</bean>
</beans>
Spring 依赖注入(Dependency Injection,DI)和控制反转含义相同,它们是从两个角度描述的同一个概念。使用依赖注入可以更轻松的管理和测试应用程序。
当某个 Java 实例需要另一个 Java 实例时,传统的方法是由调用者创建被调用者的实例(例如,使用 new 关键字获得被调用者实例),而使用 Spring 框架后,被调用者的实例不再由调用者创建,而是由 Spring 容器创建,这称为控制反转。
Spring 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,调用者通过 Spring 容器获得被调用者实例,这称为依赖注入。
依赖注入主要有两种实现方式,分别是 setter 注入(又称设值注入)和构造函数注入。具体介绍如下。
- 构造函数注入
指 IoC 容器使用构造函数注入被依赖的实例。可以通过调用带参数的构造函数实现依赖注入,每个参数代表一个依赖。
- setter 注入
指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 Bean 后,调用该 Bean 的 setter 方法,即可实现基于 setter 的 DI。
在 Spring 实例化 Bean 的过程中,首先会调用默认的构造方法实例化 Bean 对象,然后通过 Java 的反射机制调用 setXxx() 方法进行属性的注入。因此,setter 注入要求 Bean 的对应类必须满足以下两点要求。
必须提供一个默认的无参构造方法。
必须为需要注入的属性提供对应的 setter 方法。
使用 setter 注入时,在 Spring 配置文件中,需要使用 元素的子元素 为每个属性注入值。而使用构造注入时,在配置文件中,主要使用 标签定义构造方法的参数,使用其 value 属性(或子元素)设置该参数的值。
下面使用 标签实现构造函数注入。
在 标签中,包含 ref、value、type、index 等属性。value 属性用于注入基本数据类型以及字符串类型的值;ref 属性用于注入已经定义好的 Bean;type 属性用来指定对应的构造函数,当构造函数有多个参数时,可以使用 index 属性指定参数的位置,index 属性值从 0 开始。
下面使用 IDEA IDE 演示通过构造函数注入依赖项,步骤如下:
- 创建 SpringDemo 项目,并在 src 目录下创建 net.biancheng 包。
- 添加相应的 jar 包,可以参考《第一个Spring程序》一节。
- 在 net.biancheng 包下创建 Person、Man 和 MainApp 类。
- 在 src 目录下创建 Spring 配置文件 Beans.xml。
- 运行 SpringDemo 项目。
Person 类代码如下。
package net.biancheng;
public class Person {
private Man man;
public Person(Man man) {
System.out.println("在Person的构造函数内");
this.man = man;
}
public void man() {
man.show();
}
}
Man 类代码如下。
package net.biancheng;
public class Man {
private String name;
private int age;
public Man() {
System.out.println("在man的构造函数内");
}
public Man(String name, int age) {
System.out.println("在man的有参构造函数内");
this.name = name;
this.age = age;
}
public void show() {
System.out.println("名称:" + name + "\n年龄:" + age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Beans.xml 配置文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="man" class="net.biancheng.Man">
<constructor-arg value="bianchengbang" />
<constructor-arg value="12" type="int" />
</bean>
<bean id="person" class="net.biancheng.Person">
<constructor-arg ref="man" type="java.lang.String"/>
</bean>
</beans>
MainApp 类代码如下。
package net.biancheng;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Person person = (Person) context.getBean("person");
person.man();
}
}
运行结果如下。
在man的有参构造函数内
在Person的构造函数内
名称:bianchengbang
年龄:12
下面使用 标签实现 setter 注入。
在 标签中,包含 name、ref、value 等属性。name 用于指定参数名称;value 属性用于注入基本数据类型以及字符串类型的值;ref 属性用于注入已经定义好的 Bean。
在例 1 的基础上修改 Man 类的内容,代码如下。
package net.biancheng;
public class Man {
private String name;
private int age;
public Man() {
System.out.println("在man的构造函数内");
}
public Man(String name, int age) {
System.out.println("在man的有参构造函数内");
this.name = name;
this.age = age;
}
public void show() {
System.out.println("名称:" + name + "\n年龄:" + age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Beans.xml 配置文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="person" class="net.biancheng.Person">
<property name="man" ref="man" />
</bean>
<bean id="man" class="net.biancheng.Man">
<property name="name" value="bianchengbang" />
<property name="age" value="12" />
</bean>
</beans>
运行结果如下。
在man的构造函数内
在setMan方法内
名称:bianchengbang
年龄:12
Java 中在类内部定义的类称为内部类,同理在 Bean 中定义的 Bean 称为内部 Bean。注入内部 Bean 使用 和 中的 标签。如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="outerBean" class="...">
<property name="target">
<!-- 定义内部Bean -->
<bean class="..." />
</property>
</bean>
</beans>
内部 Bean 的定义不需要指定 id 和 name 。如果指定了,容器也不会将其作为区分 Bean 的标识符,反而会无视内部 Bean 的 scope 属性。所以内部 Bean 总是匿名的,而且总是随着外部 Bean 创建。
在实际开发中很少注入内部 Bean,因为开发者无法将内部的 Bean 注入外部 Bean 以外的其它 Bean。
下面使用 Eclipse IDE 演示如何注入内部 Bean,步骤如下:
- 创建 SpringDemo 项目,并在 src 目录下创建 net.biancheng 包。
- 添加相应的 jar 包,可以参考《第一个Spring程序》一节。
- 在 net.biancheng 包下创建 Person、Man 和 MainApp 类。
- 在 src 目录下创建 Spring 配置文件 Beans.xml。
- 运行 SpringDemo 项目。
Person 类代码如下。
package net.biancheng;
public class Person {
private Man man;
public Man getMan() {
return man;
}
public void setMan(Man man) {
System.out.println("在setMan方法内");
this.man = man;
}
public void man() {
man.show();
}
}
Man 类代码如下。
package net.biancheng;
public class Man {
private String name;
private int age;
public Man() {
System.out.println("在man的构造函数内");
}
public Man(String name, int age) {
System.out.println("在man的有参构造函数内");
this.name = name;
this.age = age;
}
public void show() {
System.out.println("名称:" + name + "\n年龄:" + age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Beans.xml 配置文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="person" class="net.biancheng.Person">
<property name="man">
<bean class="net.biancheng.Man">
<property name="name" value="bianchengbang" />
<property name="age" value="12" />
</bean>
</property>
</bean>
</beans>
MainApp 类代码如下。
package net.biancheng;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Person person = (Person) context.getBean("person");
person.man();
}
}
运行结果如下。
在man的有参构造函数内
在Person的构造函数内
名称:bianchengbang
年龄:12
如果需要传递类似于 Java Collection 类型的值,例如 List、Set、Map 和 properties,可以使用 Spring 提供的集合配置标签,如下表所示。
标签 | 说明 |
---|---|
用于注入 list 类型的值,允许重复 | |
用于注入 set 类型的值,不允许重复 | |
用于注入 key-value 的集合,其中 key-value 可以是任意类型 | |
用于注入 key-value 的集合,其中 key-value 都是字符串类型 |
下面使用 Eclipse IDE 演示如何注入集合,步骤如下:
- 创建 SpringDemo 项目,并在 src 目录下创建 net.biancheng 包。
- 添加相应的 jar 包,可以参考《第一个Spring程序》一节。
- 在 net.biancheng 包下创建 JavaCollection、Man 和 MainApp 类。
- 在 src 目录下创建 Spring 配置文件 Beans.xml。
- 运行 SpringDemo 项目。
JavaCollection 类代码如下。
package net.biancheng;
import java.util.*;
public class JavaCollection {
List manList;
Set manSet;
Map manMap;
Properties manProp;
public void setManList(List manList) {
this.manList = manList;
}
public List getManList() {
System.out.println("List Elements :" + manList);
return manList;
}
public void setManSet(Set manSet) {
this.manSet = manSet;
}
public Set getManSet() {
System.out.println("Set Elements :" + manSet);
return manSet;
}
public void setManMap(Map manMap) {
this.manMap = manMap;
}
public Map getManMap() {
System.out.println("Map Elements :" + manMap);
return manMap;
}
public void setManProp(Properties manProp) {
this.manProp = manProp;
}
public Properties getManProp() {
System.out.println("Property Elements :" + manProp);
return manProp;
}
}
MainApp 类代码如下。
package net.biancheng;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
JavaCollection jc = (JavaCollection) context.getBean("javaCollection");
jc.getManList();
jc.getManSet();
jc.getManMap();
jc.getManProp();
}
}
Beans.xml 配置文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="javaCollection" class="net.biancheng.JavaCollection">
<property name="manList">
<list>
<value>编程帮</value>
<value>百度</value>
<value>C语言中文网</value>
<value>C语言中文网</value>
</list>
</property>
<property name="manSet">
<set>
<value>编程帮</value>
<value>百度</value>
<value>C语言中文网</value>
<value>C语言中文网</value>
</set>
</property>
<property name="manMap">
<map>
<entry key="1" value="编程帮" />
<entry key="2" value="百度" />
<entry key="3" value="C语言中文网" />
<entry key="4" value="C语言中文网" />
</map>
</property>
<property name="manProp">
<props>
<prop key="one">编程帮</prop>
<prop key="one">编程帮</prop>
<prop key="two">百度</prop>
<prop key="three">C语言中文网</prop>
<prop key="four">C语言中文网</prop>
</props>
</property>
</bean>
</beans>
运行结果如下。
List Elements :[编程帮, 百度, C语言中文网, C语言中文网]
Set Elements :[编程帮, 百度, C语言中文网]
Map Elements :{1=编程帮, 2=百度, 3=C语言中文网, 4=C语言中文网}
Property Elements :{two=百度, one=编程帮, three=C语言中文网, four=C语言中文网}
也可以在集合元素中注入 Bean,如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="..." class="...">
<property name="manList">
<list>
<ref bean="man1" />
<ref bean="man2" />
<value>编程帮</value>
</list>
</property>
<property name="manSet">
<set>
<ref bean="man1" />
<ref bean="man2" />
<value>编程帮</value>
</set>
</property>
<property name="manMap">
<map>
<entry key="one" value="编程帮" />
<entry key="two" value-ref="man1" />
<entry key="three" value-ref="man2" />
</map>
</property>
</bean>
</beans>
Spring 会把属性的空参数直接当成空字符串来处理,如果您需要传递一个空字符串值,可以这样写:
<bean id = "..." class = "exampleBean">
<property name = "email" value = ""/>
</bean>
等效于以下代码
exampleBean.setEmail("")
如果需要传递 NULL 值, 元素用来处理 Null 值。
<bean id = "..." class = "exampleBean">
<property name = "email"><null/></property>
</bean>
等效于以下代码
exampleBean.setEmail(null)