Spring 实战(第四版)
  • Spring 实战(第 4 版)
  • 第一部分 Spring 的核心
  • 第 1 章 Spring 之旅
    • 1.1 简化 Java 开发
      • 1.1.1 激发 POJO 的潜能
      • 1.1.2 依赖注入
      • 1.1.3 应用切面
      • 1.1.4 使用模板消除样式代码
    • 1.2 容纳你的 Bean
      • 1.2.1 使用应用上下文
      • 1.2.2 bean 的生命周期
    • 1.3 俯瞰 Spring 风景线
      • 1.3.1 Spring 模块
      • 1.3.2 Spring Portfolio
    • 1.4 Spring 的新功能
      • 1.4.1 Spring 3.1 新特性
      • 1.4.2 Spring 3.2 新特性
      • 1.4.3 Spring 4.0 新特性
    • 1.5 小结
  • 第 2 章 装配 Bean
    • 2.1 Spring 配置的可选方案
    • 2.2 自动化装配 bean
      • 2.2.1 创建可被发现的 bean
      • 2.2.2 为组件扫描的 bean 命名
      • 2.2.3 设置组件扫描的基础包
      • 2.2.4 通过为 bean 添加注解实现自动装配
      • 2.2.5 验证自动装配
    • 2.3 通过 Java 代码装配 bean
      • 2.3.1 创建配置类
      • 2.3.2 声明简单的 bean
      • 2.3.3 借助 JavaConfig 实现注入
    • 2.4 通过 XML 装配 bean
      • 2.4.1 创建 XML 配置规范
      • 2.4.2 声明一个简单的 <bean>
      • 2.4.3 借助构造器注入初始化 bean
      • 2.4.4 设置属性
    • 2.5 导入和混合配置
      • 2.5.1 在 JavaConfig 中引用 XML 配置
      • 2.5.2 在 XML 配置中引用 JavaConfig
    • 2.6 小结
  • 第 3 章 高级装配
    • 3.1 环境与 profile
      • 3.1.1 配置 profile bean
      • 3.1.2 激活 profile
    • 3.2 条件化的 bean
    • 3.3 处理自动装配的歧义性
      • 3.3.1 标示首选的 bean
      • 3.3.2 限定自动装配的 bean
    • 3.4 bean 的作用域
      • 3.4.1 使用会话和请求作用域
      • 3.4.2 在 XML 中声明作用域代理
    • 3.5 运行时值注入
      • 3.5.1 注入外部的值
      • 3.5.2 使用 Spring 表达式语言进行装配
    • 3.6 小结
  • 第 4 章 面向切面的 Spring
    • 4.1 什么是面向切面编程
      • 4.1.1 定义 AOP 术语
      • 4.1.2 Spring 对 AOP 的支持
    • 4.2 通过切点来选择连接点
      • 4.2.1 编写切点
      • 4.2.2 在切点中选择 bean
    • 4.3 使用注解创建切面
      • 4.3.1 定义切面
      • 4.3.2 创建环绕通知
      • 4.3.3 处理通知中的参数
      • 4.3.4 通过注解引入新功能
    • 4.4 在 XML 中声明切面
      • 4.4.1 声明前置和后置通知
      • 4.4.2 声明环绕通知
      • 4.4.3 为通知传递参数
      • 4.4.4 通过切面引入新的功能
    • 4.5 注入 AspectJ 切面
    • 4.6 小结
  • 第二部分 Web 中的 Spring
  • 第 5 章 构建 Spring Web 应用程序
    • 5.1 Spring MVC 起步
      • 5.1.1 跟踪 Spring MVC 的请求
      • 5.1.2 搭建 Spring MVC
      • 5.1.3 Spittr 应用简介
    • 5.2 编写基本的控制器
      • 5.2.1 测试控制器
      • 5.2.2 定义类级别的请求处理
      • 5.2.3 传递模型数据到视图中
    • 5.3 接受请求的输入
      • 5.3.1 处理查询参数
      • 5.3.2 通过路径参数接受输入
    • 5.4 处理表单
      • 5.4.1 编写处理表单的控制器
      • 5.4.2 校验表单
    • 5.5 小结
  • 第 6 章 渲染 Web 视图
    • 6.1 理解视图解析
    • 6.2 创建 JSP 视图
      • 6.2.1 配置适用于 JSP 的视图解析器
      • 6.2.2 使用 Spring 的 JSP 库
    • 6.3 使用 Apache Tiles 视图定义布局
      • 6.3.1 配置 Tiles 视图解析器
    • 6.4 使用 Thymeleaf
      • 6.4.1 配置 Thymeleaf 视图解析器
      • 6.4.2 定义 Thymeleaf 模板
    • 6.5 小结
  • 第 7 章 Spring MVC 的高级技术
    • 7.1 Spring MVC 配置的替代方案
      • 7.1.1 自定义 DispatcherServlet 配置
      • 7.1.2 添加其他的 Servlet 和 Filter
      • 7.1.3 在 web.xml 中声明 DispatcherServlet
    • 7.2 处理 multipart 形式的数据
      • 7.2.1 配置 multipart 解析器
      • 7.2.2 处理 multipart 请求
    • 7.3 处理异常
      • 7.3.1 将异常映射为 HTTP 状态码
      • 7.3.2 编写异常处理的方法
    • 7.4 为控制器添加通知
    • 7.5 跨重定向请求传递数据
      • 7.5.1 通过 URL 模板进行重定向
      • 7.5.2 使用 flash 属性
    • 7.6 小结
  • 第 8 章 使用 Spring Web Flow
    • 8.1 在 Spring 中配置 Web Flow
      • 8.1.1 装配流程执行器
      • 8.1.2 配置流程注册表
      • 8.1.3 处理流程请求
    • 8.2 流程的组件
      • 8.2.1 状态
      • 8.2.2 转移
      • 8.2.3 流程数据
    • 8.3 组合起来:披萨流程
      • 8.3.1 定义基本流程
      • 8.3.2 收集顾客信息
      • 8.3.3 构建订单
      • 8.3.4 支付
    • 8.4 保护 Web 流程
    • 8.5 小结
  • 第 9 章 保护 Web 应用
    • 9.1 Spring Security 简介
      • 9.1.1 理解 Spring Security 的模块
      • 9.1.2 过滤 Web 请求
      • 9.1.3 编写简单的安全性配置
    • 9.2 选择查询用户详细信息的服务
      • 9.2.1 使用基于内存的用户存储
      • 9.2.2 基于数据库表进行认证
      • 9.2.3 基于 LDAP 进行认证
      • 9.2.4 配置自定义的用户服务
    • 9.3 拦截请求
      • 9.3.1 使用 Spring 表达式进行安全保护
      • 9.3.2 强制通道的安全性
      • 9.3.3 防止跨站请求伪造
    • 9.4 认证用户
      • 9.4.1 添加自定义的登录页
      • 9.4.2 启用 HTTP Basic 认证
      • 9.4.3 启用 Remember-me 功能
      • 9.4.4 退出
    • 9.5 保护视图
      • 9.5.1 使用 Spring Security 的 JSP 标签库
      • 9.5.2 使用 Thymeleaf 的 Spring Security 方言
    • 9.6 小结
  • 第三部分 后端中的 Spring
  • 第 10 章 通过 Spring 和 JDBC 征服数据库
    • 10.1 Spring 的数据访问哲学
      • 10.1.1 了解 Spring 的数据访问异常体系
      • 10.1.2 数据访问模板化
    • 10.2 配置数据源
      • 10.2.1 使用 JNDI 数据源
      • 10.2.2 使用数据源连接池
      • 10.2.3 基于 JDBC 驱动的数据源
      • 10.2.4 使用嵌入式的数据源
      • 10.2.5 使用 profile 选择数据源
    • 10.3 在 Spring 中使用 JDBC
      • 10.3.1 应对失控的 JDBC 代码
      • 10.3.2 使用 JDBC 模板
    • 10.4 小结
  • 第 11 章 使用对象-关系映射持久化数据
    • 11.1 在 Spring 中集成 Hibernate
      • 11.1.1 声明 Hibernate 的 Session 工厂
      • 11.1.2 构建不依赖于 Spring 的 Hibernate 代码
    • 11.2 Spring 与 Java 持久化 API
      • 11.2.1 配置实体管理器工厂
      • 11.2.2 编写基于 JPA 的 Repository
    • 11.3 借助 Spring Data 实现自动化的 JPARepository
      • 11.3.1 定义查询方法
      • 11.3.2 声明自定义查询
      • 11.3.3 混合自定义的功能
    • 11.4 小结
  • 第 12 章 使用 NoSQL 数据库
    • 12.1 使用 MongoDB 持久化文档数据
      • 12.1.1 启用 MongoDB
      • 12.1.2 为模型添加注解,实现 MongoDB 持久化
      • 12.1.3 使用 MongoTemplate 访问 MongoDB
      • 12.1.4 编写 MongoDB Repository
    • 12.2 使用 Neo4j 操作图数据
      • 12.2.1 配置 Spring Data Neo4j
      • 12.2.2 使用注解标注图实体
      • 12.2.3 使用 Neo4jTemplate
      • 12.2.4 创建自动化的 Neo4j Repository
    • 12.3 使用 Redis 操作 key-value 数据
      • 12.3.1 连接到 Redis
      • 12.3.2 使用 Redis Template
      • 12.3.3 使用 key 和 value 的序列化器
    • 12.4 小结
  • 第 13 章 缓存数据
    • 13.1 启用对缓存的支持
      • 13.1.1 配置缓存管理器
    • 13.2 为方法添加注解以支持缓存
      • 13.2.1 填充缓存
      • 13.2.2 移除缓存条目
    • 13.3 使用 XML 声明缓存
    • 13.4 小结
  • 第 14 章 保护方法应用
    • 14.1 使用注解保护方法
      • 14.1.1 使用 @Secured 注解限制方法调用
      • 14.1.2 在 Spring Security 中使用 JSR-250 的 @RolesAllowed 注解
    • 14.2 使用表达式实现方法级别的安全性
      • 14.2.1 表述方法访问规则
      • 14.2.2 过滤方法的输入和输出
    • 14.3 小结
  • 第四部分 Spring 集成
  • 第 15 章 使用远程服务
    • 15.1 Spring 远程调用概览
    • 15.2 使用 RMI
      • 15.2.1 导出 RMI 服务
      • 15.2.2 装配 RMI 服务
    • 15.3 使用 Hessian 和 Burlap 发布远程服务
      • 15.3.1 使用 Hessian 和 Burlap 导出 bean 的功能
      • 15.3.2 访问 Hessian/Burlap 服务
    • 15.4 使用 Spring 的 HttpInvoker
      • 15.4.1 将 bean 导出为 HTTP 服务
      • 15.4.2 通过 HTTP 访问服务
    • 15.5 发布和使用 Web 服务
      • 15.5.1 创建基于 Spring 的 JAX-WS 端点
      • 15.5.2 在客户端代理 JAX-WS 服务
    • 15.6 小结
  • 第 16 章 使用 Spring MVC 创建 REST API
    • 16.1 了解 REST
      • 16.1.1 REST 的基础知识
      • 16.1.2 Spring 是如何支持 REST 的
    • 16.2 创建第一个 REST 端点
      • 16.2.1 协商资源表述
      • 16.2.2 使用 HTTP 信息转换器
    • 16.3 提供资源之外的其他内容
      • 16.3.1 发送错误信息到客户端
      • 16.3.2 在响应中设置头部信息
    • 16.4 编写 REST 客户端
      • 16.4.1 了解 RestTemplate 的操作
      • 16.4.2 GET 资源
      • 16.4.3 检索资源
      • 16.4.4 抽取响应的元数据
      • 16.4.5 PUT 资源
      • 16.4.6 DELETE 资源
      • 16.4.7 POST 资源数据
      • 16.4.8 在 POST 请求中获取响应对象
      • 16.4.9 在 POST 请求后获取资源位置
      • 16.4.10 交换资源
    • 16.5 小结
  • 第 17 章 Spring 消息
    • 17.1 异步消息简介
      • 17.1.1 发送消息
      • 17.1.2 评估异步消息的优点
    • 17.2 使用 JMS 发送消息
      • 17.2.1 在 Spring 中搭建消息代理
      • 17.2.2 使用 Spring 的 JMS 模板
      • 17.2.3 创建消息驱动的 POJO
      • 17.2.4 使用基于消息的 RPC
    • 17.3 使用 AMQP 实现消息功能
      • 17.3.1 AMQP 简介
      • 17.3.2 配置 Spring 支持 AMQP 消息
      • 17.3.3 使用 RabbitTemplate 发送消息
      • 17.3.4 接收 AMQP 消息
    • 17.4 小结
  • 第 18 章 使用 WebSocket 和 STOMP 实现消息功能
    • 18.1 使用 Spring 的低层级 WebSocket API
    • 18.2 应对不支持 WebSocket 的场景
    • 18.3 使用 STOMP 消息
      • 18.3.1 启用 STOMP 消息功能
      • 18.3.2 处理来自客户端的 STOMP 消息
      • 18.3.3 发送消息到客户端
    • 18.4 为目标用户发送消息
      • 18.4.1 在控制器中处理用户的消息
      • 18.4.2 为指定用户发送消息
    • 18.5 处理消息异常
    • 18.6 小结
  • 第 19 章 使用 Spring 发送 Email
    • 19.1 配置 Spring 发送邮件
      • 19.1.1 配置邮件发送器
      • 19.1.2 装配和使用邮件发送器
    • 19.2 构建丰富内容的 Email 消息
      • 19.2.1 添加附件
      • 19.2.2 发送富文本内容的 Email
    • 19.3 使用模板生成 Email
      • 19.3.1 使用 Velocity 构建 Email 消息
      • 19.3.2 使用 Thymeleaf 构建 Email 消息
    • 19.4 小结
  • 第 20 章 使用 JMX 管理 SpringBean
    • 20.1 将 Spring bean 导出为 MBean
      • 20.1.1 通过名称暴露方法
      • 20.1.2 使用接口定义 MBean 的操作和属性
      • 20.1.3 使用注解驱动的 MBean
      • 20.1.4 处理 MBean 冲突
    • 20.2 远程 MBean
      • 20.2.1 暴露远程 MBean
      • 20.2.2 访问远程 MBean
      • 20.2.3 代理 MBean
    • 20.3 处理通知
      • 20.3.1 监听通知
    • 20.4 小结
  • 第 21 章 借助 Spring Boot 简化 Spring 开发
    • 21.1 Spring Boot 简介
      • 21.1.1 添加 Starter 依赖
      • 21.1.2 自动配置
      • 21.1.3 Spring Boot CLI
      • 21.1.4 Actuator
    • 21.2 使用 Spring Boot 构建应用
      • 21.2.1 处理请求
      • 21.2.2 创建视图
      • 21.2.3 添加静态内容
      • 21.2.4 持久化数据
      • 21.2.5 尝试运行
    • 21.3 组合使用 Groovy 与 Spring Boot CLI
      • 21.3.1 编写 Groovy 控制器
      • 21.3.2 使用 Groovy Repository 实现数据持久化
      • 21.3.3 运行 Spring Boot CLI
    • 21.4 通过 Actuator 获取了解应用内部状况
    • 21.5 小结
Powered by GitBook
On this page

Was this helpful?

  1. 第 2 章 装配 Bean
  2. 2.4 通过 XML 装配 bean

2.4.3 借助构造器注入初始化 bean

在 Spring XML 配置中,只有一种声明 bean 的方式:使用 <bean> 元素并指定 class 属性。Spring 会从这里获取必要的信息来创建 bean。

但是,在 XML 中声明 DI 时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:

  • <constructor-arg> 元素

  • 使用 Spring 3.0 所引入的 c- 命名空间

两者的区别在很大程度就是是否冗长烦琐。可以看到,<constructor-arg> 元素比使用 c- 命名空间会更加冗长,从而导致 XML 更加难以读懂。另外,有些事情可以做到,但是使用 c- 命名空间却无法实现。

在介绍 Spring XML 的构造器注入时,我们将会分别介绍这两种可选方案。首先,看一下它们各自如何注入 bean 引用。

构造器注入 bean 引用

按照现在的定义,CDPlayer bean 有一个接受 CompactDisc 类型的构造器。这样,我们就有了一个很好的场景来学习如何注入 bean 的引用。

现在已经声明了 SgtPeppers bean,并且 SgtPeppers 类实现了 CompactDisc 接口,所以实际上我们已经有了一个可以注入到 CDPlayer bean 中的 bean。我们所需要做的就是在 XML 中声明 CDPlayer 并通过 ID 引用 SgtPeppers:

<bean id="cdPlayer" class="soundsystem.CDPlayer">
  <constructor-arg ref="compactDisc">
</bean>

当 Spring 遇到这个 <bean> 元素时,它会创建一个 CDPlayer 实例。<constructor-arg> 元素会告知 Spring 要将一个 ID 为 compactDisc 的 bean 引用传递到 CDPlayer 的构造器中。

作为替代的方案,你也可以使用 Spring 的 c- 命名空间。c- 命名空间是在 Spring 3.0 中引入的,它是在 XML 中更为简洁地描述构造器参数的方式。要使用它的话,必须要在 XML 的顶部声明其模式,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:c="http://www.springframework.org/schema/c"
  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.xsd" >
  
  ...
  
</beans>

在 c- 命名空间和模式声明之后,我们就可以使用它来声明构造器参数了,如下所示:

<bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc" />

在这里,我们使用了 c- 命名空间来声明构造器参数,它作为元素的一个属性,不过这个属性的名字有点诡异。图 2.1 描述了这个属性名是如何组合而成的。

属性名以 c: 开头,也就是命名空间的前缀。接下来就是要装配的构造器参数名,在此之后是 -ref,这是一个命名的约定,它会告诉 Spring,正在装配的是一个 bean 的引用,这个 bean 的名字是 compactDisc,而不是字面量 “compactDisc”。

很显然,使用 c- 命名空间属性要比使用元素简练得多。这是我很喜欢它的原因之一。除了更易读之外,当我在编写样例代码时,c- 命名空间属性能够更加有助于使代码的长度保持在书的边框之内。

在编写前面的样例时,关于 c- 命名空间,有一件让我感到困扰的事情就是它直接引用了构造器参数的名称。引用参数的名称看起来有些怪异,因为这需要在编译代码的时候,将调试标志(debug symbol)保存在类代码中。如果你优化构建过程,将调试标志移除掉,那么这种方式可能就无法正常执行了。

替代的方案是我们使用参数在整个参数列表中的位置信息:

<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_0-ref="compactDisc" />

这个 c- 命名空间属性看起来似乎比上一种方法更加怪异。我将参数的名称替换成了 0,也就是参数的索引。因为在 XML 中不允许数字作 为属性的第一个字符,因此必须要添加一个下画线作为前缀。

使用索引来识别构造器参数感觉比使用名字更好一些。即便在构建的时候移除掉了调试标志,参数却会依然保持相同的顺序。如果有多个构造器参数的话,这当然是很有用处的。在这里因为只有一个构造器参数,所以我们还有另外一个方案 —— 根本不用去标示参数:

<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_-ref="compactDisc" />

到目前为止,这是最为奇特的一个 c- 命名空间属性,这里没有参数索引或参数名。只有一个下画线,然后就是用 -ref 来表明正在装配的是一个引用。

我们已经将引用装配到了其他的 bean 之中,接下来看一下如何将字面量值(literal value)装配到构造器之中。

将字面量注入到构造器中

迄今为止,我们所做的 DI 通常指的都是类型的装配 —— 也就是将对象的引用装配到依赖于它们的其他对象之中 —— 而有时候,我们需要做的只是用一个字面量值来配置对象。为了阐述这一点,假设你要创建 CompactDisc 的一个新实现,如下所示:

BlankDisc.java
package soundsystem;

import java.util.List;

public class BlankDisc implements CompactDisc {

  private String title;
  private String artist;

  public BlankDisc(String title, String artist) {
    this.title = title;
    this.artist = artist;
  }

  public void play() {
    System.out.println("Playing " + title + " by " + artist);
  }

}

在 SgtPeppers 中,唱片名称和艺术家的名字都是硬编码的,但是这个 CompactDisc 实现与之不同,它更加灵活。像现实中的空磁盘一样,它可以设置成任意你想要的艺术家和唱片名。现在,我们可以将 已有的 SgtPeppers 替换为这个类:

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value="The Beatles" />
</bean>

我们再次使用 <constructor-arg> 元素进行构造器参数的注入。但是这一次我们没有使用 ref 属性来引用其他的 bean,而是使用了 value 属性,通过该属性表明给定的值要以字面量的形式注入到构造器之中。

如果要使用 c- 命名空间的话,这个例子又该是什么样子呢?第一种方案是引用构造器参数的名字:

<bean id="compactDisc" class="soundsystem.BlankDisc"
      c:_title="Sgt. Pepper's Lonely Hearts Club Band" 
      c:_artist="The Beatles" />

可以看到,装配字面量与装配引用的区别在于属性名中去掉了 -ref 后缀。与之类似,我们也可以通过参数索引装配相同的字面量值,如下所示:

<bean id="compactDisc" class="soundsystem.BlankDisc"
      c:_0="Sgt. Pepper's Lonely Hearts Club Band" 
      c:_1="The Beatles" />

XML 不允许某个元素的多个属性具有相同的名字。因此,如果有两个或更多的构造器参数的话,我们不能简单地使用下画线进行标示。但是如果只有一个构造器参数的话,我们就可以这样做了。为了完整地展现该功能,假设 BlankDisc 只有一个构造器参数,这个参数接受唱片的名称。在这种情况下,我们可以在 Spring 中这样声明它:

<bean id="compactDisc" class="soundsystem.BlankDisc"
      c:_="Sgt. Pepper's Lonely Hearts Club Band" />

在装配 bean 引用和字面量值方面,<constructor-arg> 和 c- 命名空间的功能是相同的。但是有一种情况是 <constructor-arg> 能够实现,c- 命名空间却无法做到的。接下来,让我们看一下如何将集合装配到构造器参数中。

装配集合

到现在为止,我们假设 CompactDisc 在定义时只包含了唱片名称和 艺术家的名字。如果现实世界中的 CD 也是这样的话,那么在技术上就不会任何的进展。CD 之所以值得购买是因为它上面所承载的音乐。大多数的 CD 都会包含十多个磁道,每个磁道上包含一首歌。

如果使用 CompactDisc 为真正的 CD 建模,那么它也应该有磁道列表的概念。请考虑下面这个新的 BlankDisc:

BlankDisc.java
package soundsystem;

import java.util.List;

public class BlankDisc implements CompactDisc {

  private String title;
  private String artist;
  private List<String> tracks;

  public BlankDisc(String title, String artist, List<String> tracks) {
    this.title = title;
    this.artist = artist;
    this.tracks = tracks;
  }

  public void play() {
    System.out.println("Playing " + title + " by " + artist);
    for (String track : tracks) {
      System.out.println("-Track: " + track);
    }
  }

}

这个变更会对 Spring 如何配置 bean 产生影响,在声明 bean 的时候,我们必须要提供一个磁道列表。

最简单的办法是将列表设置为 null。因为它是一个构造器参数,所以必须要声明它,不过你可以采用如下的方式传递 null 给它:

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value="The Beatles" />
    <constructor-arg><null/></constructor-arg>
</bean>

<null/> 元素所做的事情与你的期望是一样的:将 null 传递给构造器。这并不是解决问题的好办法,但在注入期它能正常执行。当调用 play() 方法时,你会遇到 NullPointerException 异常,因此这并不是理想的方案。

更好的解决方法是提供一个磁道名称的列表。要达到这一点,我们可以有多个可选方案。首先,可以使用元素将其声明为一个列 表:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:c="http://www.springframework.org/schema/c"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="compactDisc"
        class="soundsystem.BlankDisc"
        c:_0="Sgt. Pepper's Lonely Hearts Club Band"
        c:_1="The Beatles">
    <constructor-arg>
      <list>
        <value>Sgt. Pepper's Lonely Hearts Club Band</value>
        <value>With a Little Help from My Friends</value>
        <value>Lucy in the Sky with Diamonds</value>
        <value>Getting Better</value>
        <value>Fixing a Hole</value>
        <!-- ...other tracks omitted for brevity... -->
      </list>
    </constructor-arg>
  </bean>

</beans>

其中,<list> 元素是 <constructor-arg> 的子元素,这表明一个包含值的列表将会传递到构造器中。其中,<value> 元素用来指定列表中的每个元素。

与之类似,我们也可以使用 <ref> 元素替代 <value>,实现 bean 引用列表的装配。例如,假设你有一个 Discography 类,它的构造器如 下所示:

Discography.java
public Discography(String artist, List<CompactDisc> cds) { ... }

那么,你可以采取如下的方式配置 Discography bean:

<bean id="beatlesDiscography"
        class="soundsystem.Discography" >
  <constructor-arg>
    <list>
      <ref bean="sgtPeppers" />
      <ref bean="whiteAlbum" />
      <ref bean="hardDaysNight" />
      <ref bean="revolver" />
      ...
    </list>
  </constructor-arg>
</bean>

当构造器参数的类型是 java.util.List 时,使用 <list> 元素是合情合理的。尽管如此,我们也可以按照同样的方式使用 <set> 元素:

<bean id="compactDisc" class="soundsystem.BlankDisc" >
  <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
  <constructor-arg value="The Beatles" />
  <constructor-arg>
    <set>
      <value>Sgt. Pepper's Lonely Hearts Club Band</value>
      <value>With a Little Help from My Friends</value>
      <value>Lucy in the Sky with Diamonds</value>
      <value>Getting Better</value>
      <value>Fixing a Hole</value>
      <!-- ...other tracks omitted for brevity... -->
    </set>
  </constructor-arg>
</bean>

<set> 和 <list> 元素的区别不大,其中最重要的不同在于当 Spring 创建要装配的集合时,所创建的是 java.util.Set 还是 java.util.List。如果是 Set 的话,所有重复的值都会被忽略掉,存放顺序也不会得以保证。不过无论在哪种情况下,<set> 或 <list> 都可以用来装配 List、Set 甚至数组。

在装配集合方面,<constructor-arg> 比 c- 命名空间的属性更有优势。目前,使用 c- 命名空间的属性无法实现装配集合的功能。

使用 <constructor-arg> 和 c- 命名空间实现构造器注入时,它们之间还有一些细微的差别。但是到目前为止,我们所涵盖的内容已经足够了,尤其是像我之前所建议的那样,要首选基于 Java 的配置而不是 XML。因此,与其不厌其烦地花费时间讲述如何使用 XML 进行构造器注入,还不如看一下如何使用 XML 来装配属性。

Previous2.4.2 声明一个简单的 <bean>Next2.4.4 设置属性

Last updated 5 years ago

Was this helpful?

图 2.1 通过 Spring 的 c- 命名空间将 bean 引用注入到构造器参数中