第三章 bean 的配置
在本章中,我们将介绍以下内容:
- bean 定义的继承:
- 如何解决 bean 类的构造函数的参数:
- 如何配置原始类型 (如 int 、float 等) 、集合类型(如 java.util.List、java.util.Map)等以及自定义类型 (如 Address ) 等的 bean 属性和构造函数参数;
- 如何通过使用 p 命名空间和 c 命名空间分别指定 bean 属性和构造参数来使用应用程序上下文 XML 文件变得简洁;
- Spring 的 FactoryBean 接口,运行编写自己的工厂类来创建 bean 实例;
- 模块化 bean 配置。
bean 定义的继承
我们在第一章和第二章中看到,应用程序上下文 XML 文件中的 bean 定义指定了 bean 类及其依赖项的完全限定名称。在某些场景下,为了使 bean 定义不那么冗长,你可能希望 bean 定义从另一个 bean 定义继承配置信息。下面介绍 MyBank 应用中这样的一个场景。
MyBank——bean 定义继承实例
在上一章中,我们了解到 MyBank 应用通过 DAO 来访问数据库。假设 MyBank 应用定义了一个可以与数据库交互的 DatabaseOperations 类,因此 MyBank 应用中的所有 DAO 都依赖于 DatabaseOperations 类来执行数据库操作,如下图所示。
上图展示了 FixedDepositDao 和 PersonalBankingDao 类依赖于 DatabaseOperations 类。以下应用程序上下文 XML 文件展示了这些类的 bean 定义。
<bean id="databaseOperations"
class="sample.spring.chapter01.bankapp.utils.DatabaseOperations"></bean>
<bean id="personalBankingDao" class="sample.spring.chapter01.bankapp.dao.PersonalBankingDaoImpl">
<property name="databaseOperations" ref="databaseOperations" />
</bean>
<bean id="fixedDepositDao" class="sample.spring.chapter01.bankapp.dao.FixedDepositDaoImpl">
<property name="databaseOperations" ref="databaseOperations" />
</bean>
上面 xml 中,personalBankingDao 和 fixedDepositDao bean 定义都使用 databaseOperations 属性来引用 DatabaseOperations 实例。这意味着 PersonalBankingDaoImpl 和 FixedDepositDaoImpl 类都定义了一个 setDatabaseOperations 方法,以允许 Spring 容器注入 DatabaseOperations 实例。
如果应用程序中的 多个 bean 共享一组公共的配置 (属性、构造函数参数等),则可以创建一个 bean 定义,作为其他 bean 定义的父定义。在 personalBankingDao 和 fixedDepositDao bean定义中,公共的配置是 databaseOperations 属性。下面展示了 personalBankingDao 和 fixedDepositDao bean 定义如何从父 bean 定义继承 databaseOperations 属性。
<bean id="databaseOperations" class="sample.spring.chapter03.bankapp.utils.DatabaseOperations"></bean>
<bean id="daoTemplate" abstract="true">
<property name="databaseOperations" ref="databaseOperations" />
</bean>
<bean id="fixedDepositDao" parent="daoTemplate" class="sample.spring.chapter03.bankapp.dao.FixedDepositDaoImpl">
</bean>
<bean id="personalBankingDao" parent="daoTemplate"
class="sample.spring.chapter03.bankapp.dao.PersonalBankingDaoImpl"></bean>
在上面的 xml 中,daoTemplate bean 定义了 fixedDepositDao 和 personalBankingDao bean 定义共享的公共配置。由于 fixedDepositDao 和 personalBankingDao bean 定义都需要 databaseOperations 依赖项,daoTemplate bean 定义使用
如果
注意:
抽象 bean 不能作为其他 bean 定义的依赖项,也就是说,不能使用 <property> 或 <constructor-arg> 元素来引用抽象 bean 。
你可能已经注意到 daoTemplate bean 定义没有指定 class特性。如果父 bean 定义没有指定 class特性,则需要在子 bean 定义 (如 fixedDepositDao 和 personalBankingDao) 中指定 class 特性。注意,如果不指定 class 特性,则必须将 bean 定义为抽象的,以使 Spring 容器不会去尝试创建与之对应的 bean实例。
要验证 fixedDepositDao 和 personalBankingDao bean 定义是否继承了 daoTemplate bean 定义的 databaseOperations 属性。请执行下面的 java 代码。BankApp 类的 main 方法调用在 fixedDepositDao 和 personalBankingDao bean 中的方法,而这些 bean 调用 DatabaseOperations 实例上的方法。你会注意到,BankApp 的 main 方法成功运行。没有抛出任何异常。如果没有将 DatabaseOperations 实例注入 fixedDepositDao 和 personalBankingDao bean 中,那么代码将抛出 java.lang.NullPointerException。
package sample.spring.chapter03.bankapp;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sample.spring.chapter03.bankapp.controller.FixedDepositController;
import sample.spring.chapter03.bankapp.controller.PersonalBankingController;
import sample.spring.chapter03.bankapp.domain.FixedDepositDetails;
public class BankApp {
private static Logger logger = Logger.getLogger(BankApp.class);
public static void main(String args[]) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:META-INF/spring/applicationContext.xml");
FixedDepositController fixedDepositController = context
.getBean(FixedDepositController.class);
fixedDepositController.submit(context
.getBean(FixedDepositDetails.class).setDepositAmount(100)
.setEmail("someemail@somedomain.com").setId(1).setTenure(10));
logger.info("Your fixed deposit details : " + fixedDepositController.get());
PersonalBankingController personalBankingController = context
.getBean(PersonalBankingController.class);
logger.info(personalBankingController.getMiniStatement());
}
}
下图 显示了 fixedDepositDao 和 personalBankingDao bean 定义中,bean 定义继承是如何工作的
上图展示了 fixedDepositDao 和 personalBankingDao bean 定义从 daoTemplate bean 定义继承了 databaseOperations 属性(以 fixedDeopsitDa 和 personalBankingDao 标识的方框中的 property 属性)。上图还描述了 Spring 容器不会尝试创建 与 daoTemplate bean 定义相对应的bean 实例,因为他被标记为 abstract。
继承了什么
子 bean 定义从 父 bean 定义继承一下配置信息:
- 属性,通过
元素指定。 - 构造函数参数,通过
元素指定。 - 方法覆盖(见4,5节)
- 初始化和销毁方法(见 第五章);
- 工厂方法,通过
元素的 工厂方法特性指定(见2,3节,了解静态和实例工厂方法如何用于创建 bean)