├── pages ├── web │ ├── other.md │ ├── testing.md │ ├── webmvc.md │ ├── websocket.md │ └── overview.md ├── images │ ├── tx.png │ ├── prototype.png │ ├── singleton.png │ ├── aop-proxy-call.png │ ├── container-magic.png │ ├── oxm-exceptions.png │ ├── tx_prop_required.png │ ├── DataAccessException.png │ ├── tx_prop_requires_new.png │ ├── mvc-context-hierarchy.png │ └── aop-proxy-plain-pojo-call.png ├── core │ ├── null-safety.md │ ├── databuffers.md │ └── resources.md ├── overview │ └── overview.md └── integration │ ├── ejb.md │ ├── mail.md │ ├── appendix.md │ ├── scheduling.md │ ├── overview.md │ ├── cache.md │ ├── cci.md │ └── remoting.md ├── README.md └── SUMMARY.md /pages/web/other.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/web/testing.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/web/webmvc.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/web/websocket.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/images/tx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/tx.png -------------------------------------------------------------------------------- /pages/images/prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/prototype.png -------------------------------------------------------------------------------- /pages/images/singleton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/singleton.png -------------------------------------------------------------------------------- /pages/images/aop-proxy-call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/aop-proxy-call.png -------------------------------------------------------------------------------- /pages/images/container-magic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/container-magic.png -------------------------------------------------------------------------------- /pages/images/oxm-exceptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/oxm-exceptions.png -------------------------------------------------------------------------------- /pages/images/tx_prop_required.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/tx_prop_required.png -------------------------------------------------------------------------------- /pages/images/DataAccessException.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/DataAccessException.png -------------------------------------------------------------------------------- /pages/images/tx_prop_requires_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/tx_prop_requires_new.png -------------------------------------------------------------------------------- /pages/images/mvc-context-hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/mvc-context-hierarchy.png -------------------------------------------------------------------------------- /pages/images/aop-proxy-plain-pojo-call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zookeeperss/spring-docs/HEAD/pages/images/aop-proxy-plain-pojo-call.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Framework 中文文档 2 | 3 | [![GitHub stars](https://img.shields.io/github/stars/DocsHome/spring-docs.svg?style=social&label=Stars)](https://github.com/DocsHome/spring-docs) [![GitHub pull requests](https://img.shields.io/github/issues-pr/DocsHome/spring-docs.svg)](https://github.com/DocsHome/spring-docs) [![GitHub last commit](https://img.shields.io/github/last-commit/DocsHome/spring-docs.svg)](https://github.com/DocsHome/spring-docs) 4 | 5 | ![Spring Boot](https://spring.io/img/homepage/icon-spring-framework.svg) 6 | 为依赖注入,事务管理,Web应用程序,数据访问,消息传递等提供核心支持。 7 | 8 | 该项目为 Spring Framework文档翻译项目,基于 [Spring 5.1.3](https://spring.io/projects/spring-framework) 的官方文档进行翻译。 9 | 10 | ## 阅读方式 11 | 12 | [Github](https://github.com/DocsHome/spring-docs/blob/master/SUMMARY.md) | [Gitbook](https://www.gitbook.com/book/docshome/spring-docs) 13 | 14 | ## 项目状态 15 | 16 | 计划中…… 17 | 18 | ## 许可 19 | ![](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png) 20 | 21 | 本作品采用[知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-sa/4.0/)进行许可。 22 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Spring Framework 中文文档 2 | > #### Version 5.1.3.BUILD-SNAPSHOT 3 | > 4 | > 最新特性, 更新日志, 支持的版本, 以及其他话题,项目的发布追踪,都在项目的 [Github Wiki](https://github.com/spring-projects/spring-framework/wiki).进行维护 5 | 6 | ## [概述](https://github.com/DocsHome/spring-docs/blob/master/pages/overview/overview.md#overview) 7 | Spring的历史介绍,设计理念,反馈和贡献,以及入门指南. 8 | 9 | ## [核心组件](https://github.com/DocsHome/spring-docs/blob/master/pages/core/overview.md) 10 | 11 | IoC 容器, 事件, 资源, 国际化, 验证, 数据绑定, 类型转换, SpEL, AOP. 12 | 13 | ## [测试](https://github.com/DocsHome/spring-docs/blob/master/pages/test/overview.md) 14 | 15 | Mock objects,, TestContext框架, Spring MVC 测试, WebTestClient. 16 | 17 | ## [数据访问](https://github.com/DocsHome/spring-docs/blob/master/pages/dataaccess/overview.md) 18 | 19 | Transactions(事务支持), DAO support(DAO 支持), JDBC, ORM(对象关系映射), 编组 XML. 20 | 21 | ## [Web Servlet](https://github.com/DocsHome/spring-docs/blob/master/pages/web/overview.md) 22 | 23 | Spring MVC, WebSocket, SockJS, STOMP 消息. 24 | 25 | ## [Web Reactive框架](https://github.com/DocsHome/spring-docs/blob/master/pages/webreactive/web-reactive.md) 26 | 27 | Spring WebFlux, WebClient, WebSocket. 28 | 29 | ## [集成](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/overview.md) 30 | 31 | Remoting(远程调用), JMS(java消息服务), JCA(J2EE 连接器架构), JMX(Java管理扩展), Email(电子邮箱), Tasks(任务执行), Scheduling(调度), Cache(缓存). 32 | 33 | ## [语言](https://github.com/DocsHome/spring-docs/blob/master/pages/languages/overview.md) 34 | 35 | Kotlin, Groovy, Dynamic languages. -------------------------------------------------------------------------------- /pages/core/null-safety.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#null-safety)7\. null安全 4 | -------------------------- 5 | 6 | 尽管Java不允许使用类型系统来表示null安全,但Spring框架现在加入了`org.springframework.lang` 包,并提供了以下注解,用来声明API和字段的null特性: 7 | 8 | * [`@NonNull`](https://docs.spring.io/spring-framework/docs/5.1.3.BUILD-SNAPSHOT/javadoc-api/org/springframework/lang/NonNull.html): 注解指示特定参数,返回值或字段不能为`null`(对于`@NonNullApi`和`@NonNullFields` 适用的参数和返回值不需要) 9 | 10 | * [`@Nullable`](https://docs.spring.io/spring-framework/docs/5.1.3.BUILD-SNAPSHOT/javadoc-api/org/springframework/lang/Nullable.html):其中特定参数、返回值或字段可以为`null`. 11 | 12 | * [`@NonNullApi`](https://docs.spring.io/spring-framework/docs/5.1.3.BUILD-SNAPSHOT/javadoc-api/org/springframework/lang/NonNullApi.html): 在包级别将非null声明为参数和返回值的默认行为 13 | 14 | * [`@NonNullFields`](https://docs.spring.io/spring-framework/docs/5.1.3.BUILD-SNAPSHOT/javadoc-api/org/springframework/lang/NonNullFields.html): 在包级别将非null声明为字段的默认行为 15 | 16 | 17 | Spring框架用到这些注解,但它们也可以用于任意基于Spring的Java项目中,用来声明null安全的API和可选的null安全字段。null特性对于泛型类型参数、varargs参数和数组元素是不受支持的, 但可能会包含在即将发布的版本中,请参阅[SPR-15942](https://jira.spring.io/browse/SPR-15942)以获取最新信息。在Spring框架发行版(包括小版本)之间。可以将 fine-tuned 声明为null。在方法体内判定类型是否为null超出了它的能力范围。 18 | 19 | 像Reactor或者Spring Data这样的库也用到了null安全的API。 20 | 21 | 22 | 23 | ### [](#use-cases)7.1. Use cases 24 | 25 | Spring API除了为null提供了显式的声明外,IDE还可以使用这些注解(如IDEA或Eclipse)为与null安全相关的Java开发人员提供有用的警告,用于避免运行时出现`NullPointerException`。 26 | 27 | 在Kotlin项目中也使用到Spring API的null安全特性,因为Kotlin本身支持[null安全](https://kotlinlang.org/docs/reference/null-safety.html)。[Kotlin支持文档](languages.html#kotlin-null-safety)提供了更多的详细信息。 28 | 29 | 30 | 31 | ### [](#jsr-305-meta-annotations)7.2. JSR 305 元注解 32 | 33 | Spring注解是被[JSR 305](https://jcp.org/en/jsr/detail?id=305)注解的元注解(一个潜在的但广泛使用的JSR)。 JSR 305元注解允许工具供应商(如IDEA或Kotlin)以通用方式提供安全支持,而无需为Spring注解提供硬编码支持。 34 | 35 | 为了使用Spring的null安全API,其实不需要也不建议在项目类路径中添加JSR 305依赖项。而只有在其代码库中使用null安全标识的项目(例如Spring基本库) 即可,应该将`com.google.code.findbugs:jsr305:3.0.2` 添加到仅用于编译的Gradle配置或Maven提供的scope中,就可以避免编译警告了。 -------------------------------------------------------------------------------- /pages/overview/overview.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Spring Framework 概述 4 | 5 | **Spring 使创建 Java 企业应用程序变得更加容易。它提供了在企业环境中接受 Java 语言所需的一切,,并支持 Groovy 和 Kotlin 作为 JVM 上的替代语言,并可根据应用程序的需要灵活地创建多种体系结构。 6 | 从 Spring Framework 5.0 开始,Spring 需要 JDK 8(Java SE 8+),并且已经为 JDK 9 提供了现成的支持。** 7 | 8 | Spring支持各种应用场景, 在大型企业中, 应用程序通常需要运行很长时间,而且必须运行在 jdk 和应用服务器上,这种场景开发人员无法控制其升级周期。 其他可能作为一个单独的jar嵌入到服务器去运行,也有可能在云环境中。还有一些可能是不需要服务器的独立应用程序(如批处理或集成的工作任务)。 9 | 10 | Spring 是开源的。它拥有一个庞大而且活跃的社区,提供不同范围的,真实用户的持续反馈。这也帮助Spring不断地改进,不断发展。 11 | 12 | 13 | 14 | ## 1、"Spring"是什么? 15 | 16 | Spring在不同的背景下有这不同的含义。它可以指 Spring Framework项目本身,这也是创建他的初衷。随着时间的推移,其他的"Spring" 项目已经构建在Spring框架之上。大多数时候,当人们说 "Spring",他们指的是整个项目家族。本参考文档主要侧重于Spring的基本:也就是Spring框架本身。 17 | 18 | 整个Spring框架被分成多个模块,应用程序可以选择需要的部分。core是核心容器的模块,包括模块配置和依赖注入机制 。Spring框架还为不同的应用程序体系结构提供了基础支持,包括消息传递,事务数据和持久化以及Web,还包括基于Servlet的Spring MVC Web框架 ,以及Spring WebFlux响应式Web框架。 19 | 20 | 关于模块的说明: Spring Framework 的jar包允许部署到JDK 9的模块路径("Jigsaw")。为了在支持Jigsaw的应用程序中使用, Spring Framework 5的jar带有 "Automatic-Module-Name" 清单条目,它定义了稳定的语言级模块名称(例如"spring.code","spring.context"等)独立于jar部件名称(jar遵循相同的命名模式使用"-"号代替".", 例如"spring-core" 和 "spring-context")。当然,Spring Framework 的jar包在JDK 8和9的类路径上都能保持正常工作。 21 | 22 | 23 | 24 | ## 2、Spring和Spring框架的历史 25 | 26 | Spring 的初版发布在2003年,是为了克服早期 J2EE 规范的复杂性。虽然有些人认为 [J2EE](https://en.wikipedia.org/wiki/Java_Platform,_Enterprise_Edition) 和 Spring 是竞争的,但是Spring 实际上是对 Java EE 的补充。Spring编程模型不受 Java EE 的平台制约,相反 它与精心挑选的个别规范的java EE项 目结合: 27 | 28 | - Servlet API ([JSR 340](https://jcp.org/en/jsr/detail?id=340)) 29 | 30 | - WebSocket API ([JSR 356](https://www.jcp.org/en/jsr/detail?id=356)) 31 | 32 | - Concurrency Utilities ([JSR 236](https://www.jcp.org/en/jsr/detail?id=236)) 33 | 34 | - JSON Binding API ([JSR 367](https://jcp.org/en/jsr/detail?id=367)) 35 | 36 | - Bean Validation ([JSR 303](https://jcp.org/en/jsr/detail?id=303)) 37 | 38 | - JPA ([JSR 338](https://jcp.org/en/jsr/detail?id=338)) 39 | 40 | - JMS ([JSR 914](https://jcp.org/en/jsr/detail?id=914)) 41 | 42 | - as well as JTA/JCA setups for transaction coordination, if necessary. 43 | 44 | Spring框架还支持依赖注入([JSR 330](https://www.jcp.org/en/jsr/detail?id=330))和通用注解([JSR 250](https://jcp.org/en/jsr/detail?id=250)) 规范, 应用程序开发人员可以选择使用这些规范,而不是Spring Framework提供的Spring特定机制。 45 | 46 | 在Spring框架5.0版本中,Spring最低要求使用Java EE 7的版本(例如Servlet 3.1+, JPA 2.1+),同时在运行时能与使用Java EE 8的最新API集成(例如Servlet 4.0, JSON Binding API)。 这使得Spring能完全兼容Tomcat 8/9、WebSphere 9或者JBoss EAP 7等等。 47 | 48 | 随着时间的不断推移,Java EE在应用程序开发中越发重要,也不断发展、改善。在Java EE和Spring的早期,应用程序被创建为部署到服务器的应用。如今,在有Spring Boot的帮助后,应用可以创建在devops或云端。 而Servlet容器的嵌入和一些琐碎的东西也发生了变化。在Spring框架5中,WebFlux应用程序甚至可以不直接使用Servlet的API,并且可以在非Servlet容器的服务器(如Netty)上运行。 49 | 50 | Spring还在继续创新和发展,如今,除了Spring框架以外,还加入了其他项目,如:`Spring Boot`, `Spring Security`, `Spring Data`, `Spring Cloud`, `Spring Batch` 等等。请记住,每一个Spring项目都有自己的源代码库, 问题跟踪以及发布版本。请上[spring.io/projects](https://spring.io/projects)查看所有Spring家族的项目名单。 51 | 52 | 53 | 54 | 55 | 56 | ## 3、Spring的设计理念 57 | 58 | 当你学习一个框架时,不仅需要知道他是如何运作的,更需要知道他遵循什么样的原则,以下是 Spring 框架遵循的原则: 59 | 60 | - 提供各个层面的选择。Spring 允许您尽可能延迟设计决策。例如,您可以在不更改代码的情况下通过配置切换持久性功能。对于其他基础架构的问题以及与第三方API的集成也是如此。 61 | 62 | - 包含多个使用视角。Spring 的灵活性非常高,而不是规定了某部分只能做某一件事.他以不同的视角支持广泛的应用需求 63 | 64 | - 保持向后兼容。Spring 的发展经过了精心的设计,在不同版本之间保持与前版本的兼容。Spring 支持一系列精心挑选的 JDK 版本和第三方库,以方便维护依赖于 Spring 的应用程序和库。 65 | 66 | - 关心API的设计。Spring团队投入了大量的思考和时间来制作直观的API,并在许多版本和许多年中都保持不变。 67 | 68 | - 高标准的代码质量。Spring 框架提供了强有力的、精确的、即时的Javadoc。Spring这种要求干净、简洁的代码结构、包之间没有循环依赖的项目在Java界是少有的。 69 | 70 | 71 | 72 | 73 | ## 4、反馈和贡献 74 | 75 | 对于操作方法问题或诊断或调试问题,我们建议使用 Stackoverflow。Spring 在此网站上有一个专用的页面[questions page](https://spring.io/questions) ,列出了建议使用的标记。 如果你相当确定 Spring 框架存在问题,或者想建议添加功能等等,可以使用 [JIRA 的问题追踪](https://jira.spring.io/browse/spr)。 76 | 77 | 如果你想到了某问题的解决方案或者想建议,你可以在 [Github](https://github.com/spring-projects/spring-framework) 上提交请求。但是, 我们希望在此之前,你可以在问题跟踪页面先提交一波,在那里可以进行讨论并留下记录以作参考。 78 | 79 | 有关更多详情,请参阅 [CONTRIBUTING](https://github.com/spring-projects/spring-framework/blob/master/CONTRIBUTING.md) 项目页面 80 | 81 | 82 | 83 | ## 5、入门指南 84 | 85 | 如果你是刚开始使用 Spring 的话,希望你能使用基于 [Spring Boot](https://projects.spring.io/spring-boot/) 来编写应用程序,由此开始使用Spring框架。Spring Boot提供了一种快速(固定设置 )的方式来创建即时能用的 Spring 应用程序。它基于Spring框架,利用已有的约束配置,目的是让你的程序尽快跑起来。 86 | 87 | 你可以使用 [start.spring.io](https://start.spring.io/) 里的步骤来生成基本项目。或者参考 ["Getting Started" guides](https://spring.io/guides) ,例如开始创建RESTful风格的Web服务 。除了易于理解,这些指南都是非常注重任务,其中大多数是基于Spring Boot的。当然,里面还囊括了 Spring 其他很多项目,你可能在需要某些功能时用得上。 -------------------------------------------------------------------------------- /pages/core/databuffers.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#databuffers)8\. 数据缓冲区和编解码器 4 | ------------------------------ 5 | 6 | Java NIO虽然提供了`ByteBuffer`,但许多库在顶层构建自己的字节缓冲区API,尤其是对于重用缓冲区和/或使用直接缓冲区有利于性能的网络操作。 例如, Netty具有`ByteBuf`层次结构,Undertow使用XNIO,Jetty使用带有回调的池化字节缓冲区,等等。 `spring-core` 模块提供了一组抽象来处理各种字节缓冲API,如下所示: 7 | 8 | * [`DataBufferFactory`](#databuffers-factory)创建抽象数据缓冲区。. 9 | 10 | * [`DataBuffer`](#databuffers-buffer) DataBuffer表示可以[pooled](#databuffers-buffer-pooled)的字节缓冲区。 . 11 | 12 | * [`DataBufferUtils`](#databuffers-utils)为数据缓冲区提供实用程序方法。 13 | 14 | * [Codecs](#codecs) 将数据缓冲流解码或编码为更高级别的对象。 15 | 16 | 17 | 18 | 19 | ### [](#databuffers-factory)8.1. `DataBufferFactory` 20 | 21 | `DataBufferFactory` 以两种方式之一创建数据缓冲区: 22 | 23 | 1. 分配新的数据缓冲区,可选择预先指定容量(如果已知),即使`DataBuffer` 的实现可以按需增长和缩小,这也更有效。 24 | 25 | 2. 包装现有的 `byte[]` 或`java.nio.ByteBuffer`,并使用`DataBuffer`实现来修饰给定的数据,且不涉及分配。 26 | 27 | 28 | 请注意,WebFlux应用程序不直接创建`DataBufferFactory`,而是通过`ServerHttpResponse`或客户端的`ClientHttpRequest`访问它。 工厂类型取决于底层客户端或服务器,例如 Reactor Netty的`NettyDataBufferFactory` ,其他的`DefaultDataBufferFactory`。 29 | 30 | 31 | 32 | ### [](#databuffers-buffer)8.2. `DataBuffer` 33 | 34 | `DataBuffer` 接口提供与`java.nio.ByteBuffer`类似的操作,但也带来了一些额外的好处,其中一些受Netty `ByteBuf`的启发。 以下是部分好处清单: 35 | 36 | * 可以独立的读写,即不需要调用`flip()` 来在读写之间交替。 37 | 38 | * 与`java.lang.StringBuilder`一样,按需扩展容量。. 39 | 40 | * 通过 [`PooledDataBuffer`](#databuffers-buffer-pooled)Pooled 缓冲区和引用计数。 41 | 42 | * 以`java.nio.ByteBuffer`, `InputStream`或`OutputStream`的形式查看缓冲区。 43 | 44 | * 确定给定字节的索引或最后一个索引。 45 | 46 | 47 | 48 | 49 | ### [](#databuffers-buffer-pooled)8.3. `PooledDataBuffer` 50 | 51 | 正如Javadoc for [ByteBuffer](https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html)中所解释的,字节缓冲区可以是直接缓冲区,也可以是非直接缓冲区。 直接缓冲区可以驻留在Java堆之外,这样就无需复制本机I/O操作。 这使得直接缓冲区对于通过套接字接收和发送数据特别有用,但是创建和释放它们也更加昂贵,这导致了池化缓冲区的想法。 52 | 53 | `PooledDataBuffer`是`DataBuffer`的扩展,它有助于引用计数,这对于字节缓冲池是必不可少的。它是如何工作的? 当分配`PooledDataBuffer` 时,引用计数为1\. 调用 `retain()`递增计数,而对`release()`的调用则递减计数。只要计数大于0,就保证缓冲区不被释放。 当计数减少到0时,可以释放池化缓冲区,这实际上可能意味着缓冲区的保留内存返回到内存池。 54 | 55 | 请注意,不是直接对`PooledDataBuffer`进行操作,在大多数情况下,最好使用`DataBufferUtils`中的方法, 只有当它是`PooledDataBuffer`的实例时才应用release或retain到`DataBuffer`。 56 | 57 | 58 | 59 | ### [](#databuffers-utils)8.4. `DataBufferUtils` 60 | 61 | `DataBufferUtils` 提供了许多用于操作数据缓冲区的实用方法: 62 | 63 | * 将数据缓冲区流加入单个缓冲区中,可能只有零拷贝,例如 通过复合缓冲区,如果底层字节缓冲区API支持。 Join a stream of data buffers into a single buffer possibly with zero copy, e.g. via composite buffers, if that’s supported by the underlying byte buffer API. 64 | 65 | * 将 `InputStream` or NIO `Channel` 转化为 `Flux`, 反之亦然, 将`Publisher` 转化为 `OutputStream` 或 NIO `Channel`. 66 | 67 | * 如果缓冲区是`PooledDataBuffer`的实例,则释放或保留`DataBuffer` 的方法。 68 | 69 | * 从字节流中跳过或取出,直到特定的字节数。 70 | 71 | 72 | 73 | 74 | ### [](#codecs)8.5. Codecs 75 | 76 | `org.springframework.core.codec` 包提供以下策略接口: 77 | 78 | * `Encoder` 将`Publisher` 编码为数据缓冲区流。 79 | 80 | * `Decoder` 将 `Publisher`为更高级别的对象流。 81 | 82 | 83 | `spring-core`模块提供`byte[]`, `ByteBuffer`, `DataBuffer`, `Resource`, 和 `String`编码器和解码器实现。 The `spring-web`模块增加了 Jackson JSON, Jackson Smile, JAXB2, Protocol Buffers 和其他编码器和解码器。请参阅WebFlux部分中的[Codecs](web-reactive.html#webflux-codecs) 。 84 | 85 | 86 | 87 | ### [](#databuffers-using)8.6. 使用 `DataBuffer` 88 | 89 | 使用数据缓冲区时,必须特别注意确保缓冲区被释放,因为它们可能被[pooled](#databuffers-buffer-pooled)。我们将使用编解码器来说明它是如何工作的,但概念更普遍适用。 让我们看看内部编解码器必须在内部管理数据缓冲区。 90 | 91 | A `Decoder` 是在创建更高级别对象之前读取输入数据缓冲区的最后一个,因此必须按如下方式释放它们:: 92 | 93 | 1. 如果`Decoder`只是读取每个输入缓冲区并准备立即释放它,它可以通过 `DataBufferUtils.release(dataBuffer)`来实现。 94 | 95 | 2. 如果`Decoder`使用`Flux` 或`Mono` 运算符(如`flatMap`,`reduce`等)在内部预取和缓存数据项,或者正在使用诸如`filter`, `skip`和其他省略项的运算符, 则 `doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release)` 必须将:: release)添加到组合链中,以确保在丢弃之前释放这些缓冲区,可能还会导致错误或取消信号。 96 | 97 | 3. 如果 `Decoder` 以任何其他方式保持一个或多个数据缓冲区,则必须确保在完全读取时释放它们,或者在读取和释放高速缓存数据缓冲区之前发生错误或取消信号。 98 | 99 | 100 | 请注意, `DataBufferUtils#join`提供了一种安全有效的方法,可将数据缓冲区流聚合到单个数据缓冲区中。 同样,`skipUntilByteCount`和 `takeUntilByteCount`是解码器使用的其他安全方法。 101 | 102 | `Encoder`分配其他人必须读取(和释放)的数据缓冲区。 所以`Encoder`没什么可做的。 但是,如果在使用数据填充缓冲区时发生序列化错误,则`Encoder`必须注意释放数据缓冲区。 例如: 103 | 104 | DataBuffer buffer = factory.allocateBuffer(); 105 | boolean release = true; 106 | try { 107 | // serialize and populate buffer.. 108 | release = false; 109 | } 110 | finally { 111 | if (release) { 112 | DataBufferUtils.release(buffer); 113 | } 114 | } 115 | return buffer; 116 | 117 | `Encoder` 的使用者负责释放它接收的数据缓冲区。 在WebFlux应用程序中,Encoder的输出用于写入HTTP服务器响应或客户端HTTP请求, 在这种情况下,释放数据缓冲区是代码写入服务器响应或客户端的责任。 请求。 118 | 119 | 请注意,在Netty上运行时,可以使用调试选项来 [排除缓冲区泄漏](https://github.com/netty/netty/wiki/Reference-counted-objects#troubleshooting-buffer-leaks)。 -------------------------------------------------------------------------------- /pages/integration/ejb.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#ejb)2\. 企业级JavaBean(EJB) 集成 4 | ------------------------------- 5 | 6 | 作为轻量级容器, Spring通常被视为EJB的替代者。我们确实认为, 对于许多应用程序和用例来说, Spring作为一个容器, 加上其在事务、ORM 和JDBC访问方面的丰富支持功能, 比通过EJB容器和EJB实现等效功能更有选择。 7 | 8 | 但是, 重要的是要注意, 使用Spring不会阻止您使用EJB。实际上, Spring使访问EJB和在其中实现EJB和功能变得更加容易。此外, 通过使用Spring来访问EJB提供的服务, 可以在本地EJB、远程EJB或POJO(普通的Java对象)变体之间切换这些服务的实现, 而无需更改客户端代码。 9 | 10 | 在本章中, 我们来看看Spring如何帮助您访问和实现EJB。Spring在访问无状态会话bean(SLSBs)时提供了特定的值, 因此我们将从讨论这个开始。 11 | 12 | 13 | 14 | ### [](#ejb-access)2.1. 访问 EJB 15 | 16 | 本节介绍如何访问EJB. 17 | 18 | 19 | 20 | #### [](#ejb-access-concepts)2.1.1. 概念 21 | 22 | 若要在本地或远程无状态会话bean上调用方法, 客户端代码通常必须执行JNDI查找以获取(本地或远程)EJB主对象, 然后对该对象使用`create`方法调用以获取实际(本地或远程)EJB对象,然后在EJB上调用一个或多个方法。 23 | 24 | 为避免重复的低级代码,许多EJB应用程序使用Service Locator(服务定位器模式)和Business Delegate(业务委托模式)。这些都比在整个客户端代码中充满JNDI查找更好,但它们通常的实现具有明显的缺点: 25 | 26 | * 通常,使用EJB的代码依赖于Service Locator或Business Delegate单例,这使得很难进行测试。 27 | 28 | * 对于没有业务代表使用的服务定位器模式,应用程序代码仍然必须在EJB主目录上调用`create()`方法并处理生成的异常。 因此,它仍然与EJB API和EJB编程模型的复杂性联系在一起。 29 | 30 | * 实现业务委托模式通常会导致严重的代码重复,我们必须编写大量在EJB上调用相同方法的方法。 31 | 32 | 33 | Spring方法是允许创建和使用代理对象, 通常在Spring容器中配置, 作为无需代码的业务委派。您不需要在手动编码的业务委派中编写另一个服务定位器、另一个JNDI查找或重复方法, 除非实际上在这些代码中添加了实际值。 34 | 35 | 36 | 37 | #### [](#ejb-access-local)2.1.2. 访问本地的SLSBs 38 | 39 | 假设我们有一个需要使用本地EJB的Web控制器。我们将遵循最佳实践并使用EJB业务方法接口模式, 以便EJB的本地接口继承非EJB特定的业务方法接口。我们将此业务方法称为`MyComponent`接口。 以下示例显示了这样的接口: 40 | 41 | ```java 42 | public interface MyComponent { 43 | ... 44 | } 45 | ``` 46 | 47 | 使用业务方法接口模式的主要原因之一是确保本地接口中的方法签名和bean实现类之间的同步是自动的。另一个原因是, 它后来使我们更容易切换到POJO(普通、旧的Java对象)的服务实现,如果它有意义的这样做。 当然, 我们还需要实现本地 home 接口, 并提供实现`SessionBean`和`MyComponent` 业务方法接口的实现类。现在, 我们需要做的唯一的Java编码来将我们的Web层控制器与EJB实现挂钩, 就是在控制器上公开一个类型 `MyComponent`的setter方法。这将将引用保存为控制器中的实例变量: 48 | 49 | ```java 50 | private MyComponent myComponent; 51 | 52 | public void setMyComponent(MyComponent myComponent) { 53 | this.myComponent = myComponent; 54 | } 55 | ``` 56 | 57 | 随后, 我们可以在控制器中的任何业务方法中使用此实例变量。现在假设我们从Spring容器中获取控制器对象, 我们可以 (在同一上下文中)配置一个 `LocalStatelessSessionProxyFactoryBean`实例, 它将是EJB代理对象。 代理的配置以及控制器的 `myComponent`属性的设置是通过配置项完成的, 例如: 58 | 59 | ```java 60 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ``` 70 | 71 | 有很多工作发生在幕后, 这得益于Spring的AOP框架, 虽然你不被迫使用AOP的概念来享受结果。`myComponent` bean定义为EJB创建了一个代理, 它实现了业务方法接口。 EJB本地主页是在启动时缓存的, 因此只有一个JNDI查找。每次调用EJB 时, 代理都调用本地AOP上的`classname` 方法, 并调用AOP上相应的业务方法。 72 | 73 | `myController` bean定义将控制器类的`myComponent`属性设置为EJB代理。 74 | 75 | 或者(最好是在许多这样的代理定义的情况下), 考虑使用Spring的“jee” 命名空间中的`` 配置元素。 以下示例说明了如何执行此操作: 76 | 77 | ```xml 78 | 80 | 81 | 82 | 83 | 84 | ``` 85 | 86 | 此EJB访问机制提供了应用程序代码的巨大简化, Web层代码(或其他EJB客户端代码)不依赖于EJB的使用。如果要用POJO或mock对象或其他测试存根替换此EJB引用, 我们只需更改`myComponent` bean定义, 而不更改一行Java代码。此外, 我们还不必编写一行JNDI查找或其他EJB管道代码作为应用程序的一部分。 87 | 88 | 在实际应用中的基准和经验表明, 这种方法(涉及对目标EJB的反射调用)的性能开销是最小的, 通常在典型的使用中是无法检测到的。请记住, 无论如何, 我们不希望对EJB进行fine-grained调用, 因为在应用程序服务器中存在与EJB基础结构相关的成本。 89 | 90 | 关于JNDI查找有一个警告。在bean容器中, 此类通常作为单一实例最好使用(根本没有理由使其成为原型)。但是, 如果该bean容器pre-instantiates单变量(与各种XML `ApplicationContext`变体一样), 则在EJB容器加载目标EJB之前装载 bean容器时可能会出现问题。这是因为JNDI查找将在该类的`init()` 方法中执行, 然后缓存, 但EJB还没有被绑定到目标位置.解决方案是不pre-instantiate此工厂对象, 但允许它在首次使用时创建 .在XML容器中, 这是通过`lazy-init`属性来控制的。 91 | 92 | 虽然大多数Spring用户不感兴趣,但那些使用EJB编程AOP的人可能希望查看`LocalSlsbInvokerInterceptor`。 93 | 94 | 95 | 96 | #### [](#ejb-access-remote)2.1.3.访问远程的SLSBs 97 | 98 | 访问远程EJB与访问本地EJB基本相同, 只是使用了`SimpleRemoteStatelessSessionProxyFactoryBean`或 `` 配置元素。当然, 无论有无Spring, 远程调用语义都适用。在另一台计算机的另一个VM中对某个对象的方法的调用有时必须在使用方案和失败处理方面得到不同的处理。 99 | 100 | Spring的EJB客户端支持在非Spring方法上增加了一个优势。通常, EJB客户端代码在本地或远程调用EJB之间很容易来回切换是有问题的。这是因为远程接口方法必须声明它们抛出`RemoteException`, 而客户端代码必须处理此问题, 而本地接口方法则不这样做。 为本地EJB编写的客户端代码需要移动到远程EJB, 通常必须进行修改, 以便为远程异常添加处理, 为需要移动到本地EJB的远程EJB编写的客户端代码, 可以保持不变, 但会对远程异常进行大量不必要的处理, 或者需要修改以删除该代码。 使用Spring远程EJB代理, 您可以不在业务方法接口中声明任何抛出的`RemoteException`, 也不能实现EJB代码, 它具有一个完全相同的远程接口, 但它确实抛出了`RemoteException`, 并且依赖于代理来动态处理这两个接口, 就好像它们是相同的一样。 即 客户端代码不必处理已检查的`RemoteException`类。在EJB调用期间抛出的任何实际`RemoteException`都将引发为non-checked `RemoteAccessException`类, 这是`RuntimeException`的一个子类别。 然后, 可以将目标服务切换到本地EJB或远程EJB(甚至是普通Java对象) 实现之间, 而无需客户端代码知道或关心。当然, 这是可选的。没有什么能阻止您在业务界面中声明`RemoteExceptions`。 101 | 102 | 103 | 104 | #### [](#ejb-access-ejb2-ejb3)2.1.4. 访问EJB 2.x SLSBs 和 EJB 3 SLSBs 105 | 106 | 通过Spring访问EJB 2.x 会话bean和EJB 3会话bean在很大程度上是透明的。Spring的EJB访问器(包括 ``和 ``功能) 在运行时透明地适应实际组件。 如果找到了一个主接口(EJB 2.x 样式), 或者如果没有可用的主接口(EJB 3 样式), 则执行直接组件调用。 107 | 108 | Note: 对于EJB 3会话bean, 您可以有效地使用 `JndiObjectFactoryBean`/``, 因为完全可用的组件引用会在那里公开进行纯JNDI查找。 定义显式`` 或 ``查找只是提供一致且更显式的EJB访问配置。 -------------------------------------------------------------------------------- /pages/integration/mail.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#mail)6\. 电子邮件 4 | ----------------- 5 | 6 | 本节介绍如何使用Spring Framework发送电子邮件。 7 | 8 | 依赖库 9 | 10 | 以下JAR需要位于应用程序的类路径中才能使用Spring Framework的电子邮件库: 11 | 12 | * The [JavaMail](https://javaee.github.io/javamail/) library 13 | 14 | 15 | 该库可在Web上免费获取 - 例如,在Maven Central中以`com.sun.mail:javax.mail`的形式提供。 . 16 | 17 | Spring提供了一个发送电子邮件的高级抽象层,它向用户屏蔽了底层邮件系统的一些细节,同时代表客户端负责底层的资源处理。 18 | 19 | `org.springframework.mail`包是Spring Framework电子邮件支持的主要包,它包括了发送电子邮件的主要接口 `MailSender`,和值对象`SimpleMailMessage`,它封装了简单邮件的属性如`from` 和 `to`(以及许多其他邮件)。 此程序包还包含已检查异常的层次结构,这些异常提供较低级别邮件系统异常的更高抽象级别,根异常为`MailException`。 有关富邮件异常层次结构的详细信息,请参阅[javadoc](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/mail/MailException.html)。 20 | 21 | 为了使用JavaMail中的一些特色,比如MIME类型的信件,Spring提供了`MailSender`的一个子接口(内嵌了的),即`org.springframework.mail.javamail.JavaMailSender`。`JavaMailSender`还提供了一个回调接口`org.springframework.mail.javamail.MimeMessagePreparator`,用于准备`MimeMessage`。 22 | 23 | 24 | 25 | ### [](#mail-usage)6.1. Usage 26 | 27 | 假设我们有一个名为`OrderManager`的业务接口,如下例所示: 28 | 29 | ```java 30 | public interface OrderManager { 31 | 32 | void placeOrder(Order order); 33 | 34 | } 35 | ``` 36 | 37 | 进一步假设我们要求说明需要生成带有订单号的电子邮件消息并将其发送给下达相关订单的客户。 38 | 39 | 40 | 41 | #### [](#mail-usage-simple)6.1.1. `MailSender`与 `SimpleMailMessage`的基本用法 42 | 43 | 以下示例显示了当有人下订单时如何使用`MailSender`和`SimpleMailMessage`发送电子邮件: 44 | 45 | ```java 46 | import org.springframework.mail.MailException; 47 | import org.springframework.mail.MailSender; 48 | import org.springframework.mail.SimpleMailMessage; 49 | 50 | public class SimpleOrderManager implements OrderManager { 51 | 52 | private MailSender mailSender; 53 | private SimpleMailMessage templateMessage; 54 | 55 | public void setMailSender(MailSender mailSender) { 56 | this.mailSender = mailSender; 57 | } 58 | 59 | public void setTemplateMessage(SimpleMailMessage templateMessage) { 60 | this.templateMessage = templateMessage; 61 | } 62 | 63 | public void placeOrder(Order order) { 64 | 65 | // Do the business calculations... 66 | 67 | // Call the collaborators to persist the order... 68 | 69 | // Create a thread safe "copy" of the template message and customize it 70 | SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage); 71 | msg.setTo(order.getCustomer().getEmailAddress()); 72 | msg.setText( 73 | "Dear " + order.getCustomer().getFirstName() 74 | + order.getCustomer().getLastName() 75 | + ", thank you for placing order. Your order number is " 76 | + order.getOrderNumber()); 77 | try{ 78 | this.mailSender.send(msg); 79 | } 80 | catch (MailException ex) { 81 | // simply log it and go on... 82 | System.err.println(ex.getMessage()); 83 | } 84 | } 85 | 86 | } 87 | ``` 88 | 89 | 以下示例显示了上述代码的bean定义: 90 | 91 | ```xml 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | ``` 107 | 108 | 109 | 110 | #### [](#mail-usage-mime)6.1.2. 使用 `JavaMailSender` 和 `MimeMessagePreparator` 111 | 112 | 本节描述了使用`MimeMessagePreparator`回调接口的`OrderManager`的另一个实现。 在以下示例中,`mailSender`属性的类型为`JavaMailSender`,以便我们能够使用JavaMail `MimeMessage`类: 113 | 114 | ```java 115 | import javax.mail.Message; 116 | import javax.mail.MessagingException; 117 | import javax.mail.internet.InternetAddress; 118 | import javax.mail.internet.MimeMessage; 119 | 120 | import javax.mail.internet.MimeMessage; 121 | import org.springframework.mail.MailException; 122 | import org.springframework.mail.javamail.JavaMailSender; 123 | import org.springframework.mail.javamail.MimeMessagePreparator; 124 | 125 | public class SimpleOrderManager implements OrderManager { 126 | 127 | private JavaMailSender mailSender; 128 | 129 | public void setMailSender(JavaMailSender mailSender) { 130 | this.mailSender = mailSender; 131 | } 132 | 133 | public void placeOrder(final Order order) { 134 | // Do the business calculations... 135 | // Call the collaborators to persist the order... 136 | 137 | MimeMessagePreparator preparator = new MimeMessagePreparator() { 138 | public void prepare(MimeMessage mimeMessage) throws Exception { 139 | mimeMessage.setRecipient(Message.RecipientType.TO, 140 | new InternetAddress(order.getCustomer().getEmailAddress())); 141 | mimeMessage.setFrom(new InternetAddress("[email protected]")); 142 | mimeMessage.setText("Dear " + order.getCustomer().getFirstName() + " " + 143 | order.getCustomer().getLastName() + ", thanks for your order. " + 144 | "Your order number is " + order.getOrderNumber() + "."); 145 | } 146 | }; 147 | 148 | try { 149 | this.mailSender.send(preparator); 150 | } 151 | catch (MailException ex) { 152 | // simply log it and go on... 153 | System.err.println(ex.getMessage()); 154 | } 155 | } 156 | 157 | } 158 | ``` 159 | 160 | 以上的邮件代码是一个横切关注点,能被完美地重构为[自定义Spring AOP切面](core.html#aop)的候选者,这样它就可以在目标对象`OrderManager`的一些合适的连接点(joinpoint)中被执行了。 161 | 162 | Spring Framework的邮件支持附带标准的JavaMail实现。 有关更多信息,请参阅相关的javadoc。 163 | 164 | 165 | 166 | ### [](#mail-javamail-mime)6.2. 使用JavaMail `MimeMessageHelper` 167 | 168 | `org.springframework.mail.javamail.MimeMessageHelper`是处理JavaMail邮件时比较顺手组件之一。它可以让你摆脱繁复的JavaMail API。通过使用`MimeMessageHelper`,创建一个`MimeMessage`实例将非常容易。如下例所示: 169 | 170 | ```java 171 | // of course you would use DI in any real-world cases 172 | JavaMailSenderImpl sender = new JavaMailSenderImpl(); 173 | sender.setHost("mail.host.com"); 174 | 175 | MimeMessage message = sender.createMimeMessage(); 176 | MimeMessageHelper helper = new MimeMessageHelper(message); 177 | helper.setTo("[email protected]"); 178 | helper.setText("Thank you for ordering!"); 179 | 180 | sender.send(message); 181 | ``` 182 | 183 | 184 | 185 | #### [](#mail-javamail-mime-attachments)6.2.1. 发送附件和内部资源 186 | 187 | Multipart email 允许添加附件和内嵌资源(inline resources)。内嵌资源可能是你在信件中希望使用的图像或样式表,但是又不想把它们作为附件。 188 | 189 | 190 | 191 | ##### [](#mail-javamail-mime-attachments-attachment)附件 192 | 193 | 以下示例显示如何使用`MimeMessageHelper`发送包含单个JPEG图像附件的电子邮件: 194 | 195 | ```java 196 | JavaMailSenderImpl sender = new JavaMailSenderImpl(); 197 | sender.setHost("mail.host.com"); 198 | 199 | MimeMessage message = sender.createMimeMessage(); 200 | 201 | // use the true flag to indicate you need a multipart message 202 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 203 | helper.setTo("[email protected]"); 204 | 205 | helper.setText("Check out this image!"); 206 | 207 | // let's attach the infamous windows Sample file (this time copied to c:/) 208 | FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg")); 209 | helper.addAttachment("CoolImage.jpg", file); 210 | 211 | sender.send(message); 212 | ``` 213 | 214 | 215 | 216 | ##### [](#mail-javamail-mime-attachments-inline)内嵌资源 217 | 218 | 以下示例显示如何使用`MimeMessageHelper`发送带有内嵌图像的电子邮件: 219 | 220 | ```java 221 | JavaMailSenderImpl sender = new JavaMailSenderImpl(); 222 | sender.setHost("mail.host.com"); 223 | 224 | MimeMessage message = sender.createMimeMessage(); 225 | 226 | // use the true flag to indicate you need a multipart message 227 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 228 | helper.setTo("[email protected]"); 229 | 230 | // use the true flag to indicate the text included is HTML 231 | helper.setText("", true); 232 | 233 | // let's include the infamous windows Sample file (this time copied to c:/) 234 | FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg")); 235 | helper.addInline("identifier1234", res); 236 | 237 | sender.send(message); 238 | ``` 239 | 240 | 通过使用指定的 `Content-ID`(上例中的`identifier1234`)将内联资源添加到`MimeMessage`。 添加文本和资源的顺序非常重要。 请务必先添加文本,然后再添加资源。 如果你反过来这样做,它就行不通。 241 | 242 | 243 | 244 | #### [](#mail-templates)6.2.2. 使用模板库创建电子邮件内容 245 | 246 | 在之前的代码示例中,所有邮件的内容都是显式定义的,并通过调用`message.setText(..)`来设置邮件内容。 这种做法针对简单的情况或在上述的例子中没什么问题。因为在这里只是为了向你展示基础API。 247 | 248 | 但是,在典型的企业应用程序中,开发人员通常不会使用之前显示的方法创建电子邮件内容,原因如下: 249 | 250 | * 使用Java代码创建基于HTML的电子邮件内容非常繁琐且容易出错。 251 | 252 | * 显示逻辑和业务逻辑之间没有明确的区别。 253 | 254 | * 更改电子邮件内容的显示结构需要编写Java代码,重新编译,重新部署等。 255 | 256 | 257 | 通常,解决这些问题的方法是使用模板库(例如FreeMarker)来定义电子邮件内容的显示结构。这使您的代码仅负责创建要在电子邮件模板中呈现的数据并发送电子邮件。 当您的电子邮件内容变得相当复杂时,这绝对是一种最佳实践,而且,使用Spring Framework的FreeMarker支持类,它变得非常容易。 -------------------------------------------------------------------------------- /pages/integration/appendix.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#appendix)9\. 附录 4 | ------------------- 5 | 6 | 7 | 8 | ### [](#xsd-schemas)9.1. XML Schemas 9 | 10 | 附录的这一部分列出了与集成技术相关的XML Schemas。 11 | 12 | 13 | 14 | #### [](#xsd-schemas-jee)9.1.1. `jee` Schema 15 | 16 | `jee`元素处理与Java EE(Java Enterprise Edition)配置相关的问题,例如查找JNDI对象和定义EJB引用。 17 | 18 | 要使用`jee` schema中的元素,您需要在Spring XML配置文件的顶部包含以下前导码。 以下代码段中的文本引用了正确的schema,以便`jee`命名空间中的元素可供您使用:: 19 | 20 | ```xml 21 | 22 | 27 | 28 | 29 | 30 | ``` 31 | 32 | 33 | 34 | ##### [](#xsd-schemas-jee-jndi-lookup) (simple) 35 | 36 | 以下示例显示如何使用JNDI在没有`jee` schema的情况下查找数据源: 37 | 38 | ```xml 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ``` 47 | 48 | 以下示例显示如何使用JNDI使用`jee` schema查找数据源: 49 | 50 | ```xml 51 | 52 | 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | 60 | 61 | ##### [](#xsd-schemas-jee-jndi-lookup-environment-single)`` (使用单个JNDI环境设置) 62 | 63 | 以下示例显示如何使用JNDI查找没有`jee`的环境变量 : 64 | 65 | ```xml 66 | 67 | 68 | 69 | 70 | pong 71 | 72 | 73 | 74 | ``` 75 | 76 | 以下示例显示如何使用JNDI使用`jee`查找环境变量: 77 | 78 | ```xml 79 | 80 | ping=pong 81 | 82 | ``` 83 | 84 | 85 | 86 | ##### [](#xsd-schemas-jee-jndi-lookup-evironment-multiple)`` (多个JNDI环境设置) 87 | 88 | 以下示例显示如何使用JNDI在没有`jee`的情况下查找多个环境变量: 89 | 90 | ```xml 91 | 92 | 93 | 94 | 95 | song 96 | pong 97 | 98 | 99 | 100 | ``` 101 | 102 | The following example shows how to use JNDI to look up multiple environment variables with `jee`: 103 | 104 | ```xml 105 | 106 | 107 | 108 | sing=song 109 | ping=pong 110 | 111 | 112 | ``` 113 | 114 | 115 | 116 | ##### [](#xsd-schemas-jee-jndi-lookup-complex)`` (复杂) 117 | 118 | 以下示例显示如何使用JNDI使用`jee`查找多个环境变量: 119 | 120 | ```xml 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | ``` 130 | 131 | 以下示例显示如何使用JNDI在没有`jee`的情况下查找数据源和许多不同的属性: 132 | 133 | ```xml 134 | 141 | ``` 142 | 143 | 144 | 145 | ##### [](#xsd-schemas-jee-local-slsb)`` (简单) 146 | 147 | `` 元素配置对本地EJB Stateless SessionBean的引用。 148 | 149 | 以下示例显示如何在没有`jee`的情况下配置对本地EJB Stateless SessionBean的引用: 150 | 151 | ```xml 152 | 154 | 155 | 156 | 157 | ``` 158 | 159 | 以下示例显示如何使用`jee`配置对本地EJB Stateless SessionBean的引用: 160 | 161 | ```xml 162 | 164 | ``` 165 | 166 | 167 | 168 | ##### [](#xsd-schemas-jee-local-slsb-complex)`` (复杂) 169 | 170 | ``元素配置对本地EJB Stateless SessionBean的引用。 171 | 172 | 以下示例显示如何配置对本地EJB Stateless SessionBean的引用以及许多不带`jee`的属性: 173 | 174 | ```xml 175 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | ``` 184 | 185 | 以下示例显示如何使用`jee`配置对本地EJB Stateless SessionBean和许多属性的引用: 186 | 187 | ```xml 188 | 194 | ``` 195 | 196 | 197 | 198 | ##### [](#xsd-schemas-jee-remote-slsb) 199 | 200 | `` 元素配置对`remote`EJB Stateless SessionBean的引用。 201 | 202 | 以下示例显示如何在不使用`jee`的情况下配置对远程EJB Stateless SessionBean的引用 203 | 204 | ```xml 205 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | ``` 216 | 217 | 以下示例显示如何使用 `jee`配置对远程EJB Stateless SessionBean的引用: 218 | 219 | ```xml 220 | 228 | ``` 229 | 230 | 231 | 232 | #### [](#xsd-schemas-jms)9.1.2. `jms` Schema 233 | 234 | `jms`元素处理配置与JMS相关的bean,例如Spring的[Message Listener Containers](#jms-mdp)。 这些元素在[JMS命名空间支持](#jms-namespace)支持一节中详细介绍。 有关此支持和`jms`元素本身的完整详细信息,请参阅该章节。 235 | 236 | 为了完整性,要使用`jms` schema中的元素,您需要在Spring XML配置文件的顶部包含以下前导码。 以下代码段中的文本引用了正确的schema,以便您可以使用jms命名空间中的元素: 237 | 238 | ```xml 239 | 240 | 245 | 246 | 247 | 248 | ``` 249 | 250 | 251 | 252 | #### [](#xsd-schemas-context-mbe)9.1.3. 使用 `` 253 | 254 | [配置基于注解的MBean导出](#jmx-context-mbeanexport)中详细介绍了此元素。 255 | 256 | 257 | 258 | #### [](#xsd-schemas-cache)9.1.4. `cache` Schema 259 | 260 | 您可以使用`cache`元素来启用对Spring的`@CacheEvict`, `@CachePut`,和`@Caching`注释的支持。 它还支持基于声明的基于XML的缓存。 有关详细信息,请参阅[启用缓存注解](#cache-annotation-enable)和 [基于XML的声明性缓存](#cache-declarative-xml)。 261 | 262 | 要使用`cache` schema中的元素,需要在Spring XML配置文件的顶部包含以下前导码。 以下代码段中的文本引用了正确的schema,以便您可以使用`cache`命名空间中的元素: 263 | 264 | ```xml 265 | 266 | 271 | 272 | 273 | 274 | ``` -------------------------------------------------------------------------------- /pages/core/resources.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#resources)2\. 资源 4 | -------------------- 5 | 6 | 本章介绍Spring如何处理资源以及如何在Spring中使用资源。 它包括以下主题: 7 | 8 | * [简介](#resources-introduction) 9 | 10 | * [资源接口](#resources-resource) 11 | 12 | * [内置资源实现](#resources-implementations) 13 | 14 | * [`ResourceLoader`](#resources-resourceloader) 15 | 16 | * [`ResourceLoaderAware` 接口](#resources-resourceloaderaware) 17 | 18 | * [资源依赖](#resources-as-dependencies) 19 | 20 | * [应用上下文和资源路径](#resources-app-ctx) 21 | 22 | 23 | 24 | ### [](#resources-introduction)2.1. 简介 25 | 26 | 遗憾的是,Java的标准`java.net.URL`类和各种 `URL`前缀的标准处理程序不足以完全访问底层资源。例如,没有标准化的 `URL`实现可用于访问需要从类路径或相对于 `ServletContext`获取的资源。 虽然可以为专用 `URL`前缀注册新的处理程序(类似于`http:` :)这样的前缀的现有处理程序,但这通常非常复杂,并且`URL`接口仍然缺少一些理想的功能,例如检查当前资源是否存在的方法。 27 | 28 | 29 | 30 | ### [](#resources-resource)2.2. 资源接口 31 | 32 | Spring的`Resource`接口的目标是成为一个更强大的接口,用于抽象对底层资源的访问。 以下清单显示了`Resource`接口定义: 33 | 34 | ```java 35 | public interface Resource extends InputStreamSource { 36 | 37 | boolean exists(); 38 | 39 | boolean isOpen(); 40 | 41 | URL getURL() throws IOException; 42 | 43 | File getFile() throws IOException; 44 | 45 | Resource createRelative(String relativePath) throws IOException; 46 | 47 | String getFilename(); 48 | 49 | String getDescription(); 50 | 51 | } 52 | ``` 53 | 54 | 正如`Resource`接口的定义所示,它扩展了`InputStreamSource`接口。 以下清单显示了`InputStreamSource`接口的定义: 55 | 56 | ```java 57 | public interface InputStreamSource { 58 | 59 | InputStream getInputStream() throws IOException; 60 | 61 | } 62 | ``` 63 | 64 | `Resource` 接口中一些最重要的方法是: 65 | 66 | * `getInputStream()`: 用于定位和打开当前资源, 返回当前资源的`InputStream` ,预计每一次调用都会返回一个新的`InputStream`. 因此调用者必须自行关闭当前的输出流. 67 | 68 | * `exists()`: 返回`boolean`值,表示当前资源是否存在。 69 | 70 | * `isOpen()`: 返回`boolean`值,表示当前资源是否有已打开的输入流。如果为 `true`,那么`InputStream`不能被多次读取 ,只能在一次读取后即关闭以防止内存泄漏。除了`InputStreamResource`外,其他常用Resource实现都会返回`false`。 71 | 72 | * `getDescription()`: 返回当前资源的描述,当处理资源出错时,资源的描述会用于输出错误的信息。一般来说,资源的描述是一个完全限定的文件名称,或者是当前资源的真实URL。 73 | 74 | 75 | 其他方法允许您获取表示资源的实际`URL`或`File`对象(如果底层实现兼容并支持该功能)。 76 | 77 | 在Spring里, `Resource`抽象有着相当广泛的使用,例如,当需要某个资源时, `Resource`可以当作方法签名里的参数类型被使用。在Spring API中,有些方法(例如各种`ApplicationContext`实现的构造函数) 会直接采用普通格式的`String`路径来创建合适的`Resource`,调用者也可以通过在路径里带上指定的前缀来创建特定的`Resource`实现。 78 | 79 | 不但Spring内部和使用Spring的应用都大量地使用了`Resource`接口,而且开发者在应用代码中将它作为一个通用的工具类也是非常通用的。当你仅需要使用到`Resource`接口实现时, 可以直接忽略Spring的其余部分.虽然这样会与Spring耦合,但是也只是耦合一部分而已。使用这些`Resource`实现代替底层的访问是极其美好的。这与开发者引入其他库的目的也是一样的 80 | 81 | `Resource`抽象不会取代功能。 它尽可能地包裹它。 例如,`UrlResource`包装`URL`并使用包装的URL来完成其工作。 82 | 83 | 84 | 85 | ### [](#resources-implementations)2.3. 内置的资源实现 86 | 87 | Spring包括以下`Resource`实现: 88 | 89 | * [`UrlResource`](#resources-implementations-urlresource) 90 | 91 | * [`ClassPathResource`](#resources-implementations-classpathresource) 92 | 93 | * [`FileSystemResource`](#resources-implementations-filesystemresource) 94 | 95 | * [`ServletContextResource`](#resources-implementations-servletcontextresource) 96 | 97 | * [`InputStreamResource`](#resources-implementations-inputstreamresource) 98 | 99 | * [`ByteArrayResource`](#resources-implementations-bytearrayresource) 100 | 101 | 102 | 103 | 104 | #### [](#resources-implementations-urlresource)2.3.1. `UrlResource` 105 | 106 | `UrlResource` 封装了`java.net.URL`用来访问正常URL的任意对象。例如`file:` ,HTTP目标,FTP目标等。所有的URL都可以用标准化的字符串来表示,例如通过正确的标准化前缀。 可以用来表示当前URL的类型。 这包括`file:`,用于访问文件系统路径,`http:` :用于通过HTTP协议访问资源,`ftp:`:用于通过FTP访问资源,以及其他。 107 | 108 | 通过java代码可以显式地使用`UrlResource`构造函数来创建`UrlResource`,但也可以调用API方法来使用代表路径的String参数来隐式创建`UrlResource`。 对于后一种情况,JavaBeans `PropertyEditor`最终决定要创建哪种类型的`Resource`。如果路径字符串包含众所周知的(对于它,那么)前缀(例如 `classpath:`:),它会为该前缀创建适当的专用`Resource`。 但是,如果它无法识别前缀,则假定该字符串是标准URL字符串并创建`UrlResource`。 109 | 110 | 111 | 112 | #### [](#resources-implementations-classpathresource)2.3.2. `ClassPathResource` 113 | 114 | ClassPathResource代表从类路径中获取资源,它使用线程上下文加载器,指定类加载器或给定class类来加载资源。 115 | 116 | 当类路径上资源存于文件系统中时,`ClassPathResource`支持使用`java.io.File`来访问。但是当类路径上的资源位于未解压(没有被Servlet引擎或其他可解压的环境解压)的jar包中时, `ClassPathResource`就不再支持以`java.io.File`的形式访问。鉴于此,Spring中各种Resource的实现都支持以`java.net.URL`的形式访问资源。 117 | 118 | 可以显式使用`ClassPathResource`构造函数来创建`ClassPathResource`,但是更多情况下,是调用API方法使用的。即使用一个代表路径的`String`参数来隐式创建`ClassPathResource`。 对于后一种情况,将会由JavaBeans的`PropertyEditor`来识别路径中`classpath:`前缀,并创建`ClassPathResource`。 119 | 120 | 121 | 122 | #### [](#resources-implementations-filesystemresource)2.3.3. `FileSystemResource` 123 | 124 | `FileSystemResource`是用于处理`java.io.File`和`java.nio.file.Path`的实现,显然,它同时能解析作为`File`和作为`URL`的资源。 125 | 126 | 127 | 128 | #### [](#resources-implementations-servletcontextresource)2.3.4. `ServletContextResource` 129 | 130 | 这是`ServletContext`资源的 `Resource`实现,用于解释相关Web应用程序根目录中的相对路径。 131 | 132 | ServletContextResource完全支持以流和URL的方式访问资源,但只有当Web项目是解压的(不是以war等压缩包形式存在),而且该ServletContext资源必须位于文件系统中, 它支持以`java.io.File`的方式访问资源。无论它是在文件系统上扩展还是直接从JAR或其他地方(如数据库)(可以想象)访问,实际上都依赖于Servlet容器。 133 | 134 | 135 | 136 | #### [](#resources-implementations-inputstreamresource)2.3.5. `InputStreamResource` 137 | 138 | `InputStreamResource`是针对`InputStream`提供的`Resource`实现。在一般情况下,如果确实无法找到合适的`Resource`实现时,才去使用它。 同时请优先选择`ByteArrayResource`或其他基于文件的`Resource`实现,迫不得已的才使用它。 139 | 140 | 与其他`Resource` 实现相比,这是已打开资源的描述符。 因此,它从`isOpen()`返回`true`。 141 | 142 | 143 | 144 | #### [](#resources-implementations-bytearrayresource)2.3.6. `ByteArrayResource` 145 | 146 | 这是给定字节数组的`Resource`实现。 它为给定的字节数组创建一个`ByteArrayInputStream`。 147 | 148 | 当需要从字节数组加载内容时,ByteArrayResource会是个不错的选择,无需求助于单独使用的`InputStreamResource`。 149 | 150 | 151 | 152 | ### [](#resources-resourceloader)2.4.`ResourceLoader` 153 | 154 | `ResourceLoader`接口用于加载`Resource`对象,换句话说,就是当一个对象需要获取`Resource`实例时,可以选择实现`ResourceLoader`接口,以下清单显示了`ResourceLoader`接口定义:。 155 | 156 | ```java 157 | public interface ResourceLoader { 158 | 159 | Resource getResource(String location); 160 | 161 | } 162 | ``` 163 | 164 | 所有应用程序上下文都实现`ResourceLoader`接口。 因此,可以使用所有应用程序上下文来获取 `Resource`实例。 165 | 166 | 当在特殊的应用上下文中调用`getResource()`方法以及指定的路径没有特殊前缀时,将返回适合该特定应用程序上下文的`Resource`类型。 例如,假设针对`ClassPathXmlApplicationContext` 实例执行了以下代码片段: 167 | 168 | Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); 169 | 170 | 针对`ClassPathXmlApplicationContext`,该代码返回 `ClassPathResource`。如果对`FileSystemXmlApplicationContext` 实例执行相同的方法,它将返回`FileSystemResource`。 对于`WebApplicationContext`,它将返回`ServletContextResource`。 它同样会为每个上下文返回适当的对象。 171 | 172 | 因此,您可以以适合特定应用程序上下文的方式加载资源。 173 | 174 | 另一方面,您可以通过指定特殊的`classpath:`前缀来强制使用`ClassPathResource`,而不管应用程序上下文类型如何,如下例所示: 175 | 176 | ```java 177 | Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); 178 | ``` 179 | 180 | 同样,您可以通过指定任何标准`java.net.URL`前缀来强制使用`UrlResource` 。 以下对示例使用`file` 和 `http`前缀: 181 | 182 | ```java 183 | Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt"); 184 | 185 | Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt"); 186 | ``` 187 | 188 | 下表总结了将:`String`对象转换为`Resource`对象的策略: 189 | 190 | Table 10.资源字符串 191 | 192 | | 前缀 | 示例 | 解释 | 193 | | ---------- | ---------------------------------------------------- | ------------------------------------------------------------ | 194 | | classpath: | `classpath:com/myapp/config.xml` | 从类路径加载 | 195 | | file: | [file:///data/config.xml](file:///data/config.xml) | 从文件系统加载为`URL`。 另请参见[`FileSystemResource` 警告。](#resources-filesystemresource-caveats) | 196 | | http: | [http://myserver/logo.png](http://myserver/logo.png) | 作为`URL`加载。 | 197 | | (none) | `/data/config.xml` | 取决于底层的`ApplicationContext`。 | 198 | 199 | 200 | 201 | ### [](#resources-resourceloaderaware)2.5. `ResourceLoaderAware` 接口 202 | 203 | `ResourceLoaderAware`是一个特殊的标识接口,用来提供`ResourceLoader`引用的对象。以下清单显示了`ResourceLoaderAware`接口的定义: 204 | 205 | ```java 206 | public interface ResourceLoaderAware { 207 | 208 | void setResourceLoader(ResourceLoader resourceLoader); 209 | } 210 | ``` 211 | 212 | 当类实现`ResourceLoaderAware`并部署到应用程序上下文(作为Spring管理的bean)时,它被应用程序上下文识别为`ResourceLoaderAware`。 然后,应用程序上下文调用`setResourceLoader(ResourceLoader)`,将其自身作为参数提供(请记住,Spring中的所有应用程序上下文都实现了`ResourceLoader`接口)。 213 | 214 | 由于`ApplicationContext`实现了`ResourceLoader`,因此bean还可以实现 `ApplicationContextAware` 接口并直接使用提供的应用程序上下文来加载资源。 但是,通常情况下,如果您需要,最好使用专用的`ResourceLoader`接口。 代码只能耦合到资源加载接口(可以被认为是实用程序接口),而不能耦合到整个Spring `ApplicationContext`接口。 215 | 216 | 从Spring 2.5开始,除了实现`ResourceLoaderAware`接口,还可以采取另外一种替代方案-依赖`ResourceLoader`的自动装配。 “传统”构造函数和byType [自动装配](#beans-factory-autowire)模式都支持对`ResourceLoader`的装配。 前者是以构造参数的形式装配,后者作为setter方法的参数参与装配。如果为了获得更大的灵活性(包括属性注入的能力和多参方法),可以考虑使用基于注解的新型注入方式。 使用注解`@Autowired`标识`ResourceLoader`变量,便可将其注入到成员属性、构造参数或方法参数中。这些参数需要ResourceLoader类型。 有关更多信息,请参阅使用[Using `@Autowired`](#beans-autowired-annotation)。 217 | 218 | 219 | 220 | ### [](#resources-as-dependencies)2.6. 资源依赖 221 | 222 | 如果bean本身要通过某种动态过程来确定和提供资源路径,那么bean使用`ResourceLoader`接口来加载资源就变得更有意义了。假如需要加载某种类型的模板,其中所需的特定资源取决于用户的角色 。如果资源是静态的,那么完全可以不使用`ResourceLoader`接口,只需让bean公开它需要的`Resource`属性,并按照预期注入属性即可。 223 | 224 | 是什么使得注入这些属性变得如此简单?是因为所有应用程序上下文注册和使用一个特殊的`PropertyEditor` JavaBean,它可以将 `String` paths转换为`Resource`对象。 因此,如果`myBean`有一个类型为`Resource`的模板属性,它可以用一个简单的字符串配置该资源。如下所示: 225 | 226 | ```xml 227 | 228 | 229 | 230 | ``` 231 | 232 | 请注意,资源路径没有前缀。 因此,因为应用程序上下文本身将用作`ResourceLoader`, 所以资源本身通过`ClassPathResource`,`FileSystemResource`或`ServletContextResource`加载,具体取决于上下文的确切类型。 233 | 234 | 如果需要强制使用特定的 `Resource`类型,则可以使用前缀。 以下两个示例显示如何强制`ClassPathResource`和`UrlResource`(后者用于访问文件系统文件): 235 | 236 | ```xml 237 | 238 | 239 | 240 | ``` 241 | 242 | 243 | 244 | ### [](#resources-app-ctx)2.7. 应用上下文和资源路径 245 | 246 | 本节介绍如何使用资源创建应用程序上下文,包括使用XML的快捷方式,如何使用通配符以及其他详细信息。 247 | 248 | 249 | 250 | #### [](#resources-app-ctx-construction)2.7.1. 构造应用上下文 251 | 252 | 应用程序上下文构造函数(对于特定的应用程序上下文类型)通常将字符串或字符串数组作为资源的位置路径,例如构成上下文定义的XML文件。 253 | 254 | 当指定的位置路径没有带前缀时,那么从指定位置路径创建`Resource`类型(用于后续加载bean定义),具体取决于所使用应用上下文。 例如,请考虑以下示例,该示例创建`ClassPathXmlApplicationContext`: 255 | 256 | ```java 257 | ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); 258 | ``` 259 | 260 | bean定义是从类路径加载的,因为使用了`ClassPathResource`。 但是,请考虑以下示例,该示例创建 `FileSystemXmlApplicationContext`: 261 | 262 | ```java 263 | ApplicationContext ctx = 264 | new FileSystemXmlApplicationContext("conf/appContext.xml"); 265 | ``` 266 | 267 | 现在,bean定义是从文件系统位置加载的(在这种情况下,相对于当前工作目录)。 268 | 269 | 若位置路径带有classpath前缀或URL前缀,会覆盖默认创建的用于加载bean定义的`Resource`类型。请考虑以下示例: 270 | 271 | ```java 272 | ApplicationContext ctx = 273 | new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); 274 | ``` 275 | 276 | 使用`FileSystemXmlApplicationContext`从类路径加载bean定义。 但是,它仍然是`FileSystemXmlApplicationContext`。 如果它随后用作`ResourceLoader`,则任何未加前缀的路径仍被视为文件系统路径。 277 | 278 | 279 | 280 | ##### [](#resources-app-ctx-classpathxml)构造`ClassPathXmlApplicationContext`实例的快捷方式 281 | 282 | `ClassPathXmlApplicationContext`提供了多个构造函数,以利于快捷创建`ClassPathXmlApplicationContext`的实例。基础的想法是, 使用只包含多个XML文件名(不带路径信息)的字符串数组和一个Class参数的构造器,所省略路径信息`ClassPathXmlApplicationContext`会从`Class`参数中获取。 283 | 284 | 请考虑以下目录布局: 285 | 286 | com/ 287 | foo/ 288 | services.xml 289 | daos.xml 290 | MessengerService.class 291 | 292 | 以下示例显示如何实例化由名为`services.xml`和`daos.xml`(位于类路径中)的文件中定义的bean组成的`ClassPathXmlApplicationContext`实例: 293 | 294 | ```java 295 | ApplicationContext ctx = new ClassPathXmlApplicationContext( 296 | new String[] {"services.xml", "daos.xml"}, MessengerService.class); 297 | ``` 298 | 299 | 有关各种构造函数的详细信息,请参阅[`ClassPathXmlApplicationContext`](https://docs.spring.io/spring-framework/docs/5.1.3.BUILD-SNAPSHOT/javadoc-api/org/springframework/jca/context/SpringContextResourceAdapter.html)javadoc。 300 | 301 | 302 | 303 | #### [](#resources-app-ctx-wildcards-in-resource-paths)2.7.2. 使用通配符构造应用上下文 304 | 305 | 从前文可知,应用上下文构造器的资源路径可以是单一的路径(即一对一地映射到目标资源)。也可以使用高效的通配符。可以包含特殊的"classpath*:"前缀或ant风格的正则表达式(使用Spring的PathMatcher来匹配)。 306 | 307 | 通配符机制可用于组装应用程序的组件,应用程序里所有组件都可以在一个公用的位置路径发布自定义的上下文片段,那么最终的应用上下文可使用`classpath*:`。 在同一路径前缀(前面的公用路径)下创建,这时所有组件上下文的片段都会被自动装配。 308 | 309 | 请注意,此通配符特定于在应用程序上下文构造函数中使用资源路径(或直接使用 `PathMatcher`实用程序类层次结构时),并在构造时解析。 它与资源类型本身无关。 您不能使用`classpath*:`前缀来构造实际的`Resource`,,因为资源一次只指向一个资源。 310 | 311 | 312 | 313 | ##### [](#resources-app-ctx-ant-patterns-in-paths)Ant风格模式 314 | 315 | 路径位置可以包含Ant样式模式,如以下示例所示: 316 | 317 | /WEB-INF/*-context.xml 318 | com/mycompany/**/applicationContext.xml 319 | file:C:/some/path/*-context.xml 320 | classpath:com/mycompany/**/applicationContext.xml 321 | 322 | 当路径位置包含Ant样式模式时,解析程序遵循更复杂的过程来尝试解析通配符。解释器会先从位置路径里获取最靠前的不带通配符的路径片段, 并使用这个路径片段来创建一个`Resource`,并从中获取一个URL。 如果此URL不是`jar:`URL或特定于容器的变体(例如,在WebLogic中为`zip:`,在WebSphere中为`wsjar`,等等) 则从Resource里获取`java.io.File`对象,并通过其遍历文件系统。进而解决位置路径里通配符。 对于jar URL,解析器要么从中获取`java.net.JarURLConnection`, 要么手动解析jar URL,然后遍历jar文件的内容以解析通配符。 323 | 324 | 325 | 326 | ###### [](#resources-app-ctx-portability)可移植性所带来的影响 327 | 328 | 如果指定的路径定为文件URL(不管是显式还是隐式的),首先默认的`ResourceLoader`就是文件系统,其次通配符使用程序可以完美移植。 329 | 330 | 如果指定的路径是类路径位置,则解析器必须通过 `Classloader.getResource()`方法调用获取最后一个非通配符路径段URL。 因为这只是路径的一个节点(而不是末尾的文件),实际上它是未定义的(在`ClassLoader` javadoc中),在这种情况下并不能确定返回什么样的URL。 实际上,它始终会使用`java.io.File`来解析目录,其中类路径资源会解析到文件系统的位置或某种类型的jar URL,其中类路径资源解析为jar包的位置。 但是,这个操作就碰到了可移植的问题了。 331 | 332 | 如果获取了最后一个非通配符段的jar包URL,解析器必须能够从中获取`java.net.JarURLConnection`,或者手动解析jar包的URL,以便能够遍历jar的内容。 并解析通配符,这适用于大多数工作环境,但在某些其他特定环境中将会有问题,最后会导致解析失败,所以强烈建议在特定环境中彻底测试来自jar资源的通配符解析,测试成功之后再对其作依赖使用。 333 | 334 | 335 | 336 | ##### [](#resources-classpath-wildcards)The `classpath*:` 前缀 337 | 338 | 当构造基于XML文件的应用上下文时,位置路径可以使用`classpath*:`前缀。如以下示例所示: 339 | 340 | ```java 341 | ApplicationContext ctx = 342 | new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); 343 | ``` 344 | 345 | `classpath*:`的使用表示该类路径下所有匹配文件名称的资源都会被获取(本质上就是调用了`ClassLoader.getResources(…​)`方法,接着将获取到的资源装配成最终的应用上下文。 346 | 347 | 通配符类路径依赖于底层类加载器的`getResources()` 方法。由于现在大多数应用程序服务器都提供自己的类加载器实现,因此行为可能会有所不同,尤其是在处理jar文件时。 要在指定服务器测试`classpath*` 是否有效,简单点可以使用`getClass().getClassLoader().getResources("")`来加载类路径jar包里的文件。 尝试在两个不同的路径加载相同名称的文件,如果返回的结果不一致,就需要查看一下此服务器中与classloader设置相关的文档。 348 | 349 | 您还可以将`classpath*:` 前缀与位置路径的其余部分中的 `PathMatcher`模式组合在一起(例如,`classpath*:META-INF/*-beans.xml`)。 这种情况的解析策略非常简单,取位置路径最靠前的无通配符片段,然后调用`ClassLoader.getResources()`获取所有匹配到的类层次加载器加载资源,随后将`PathMatcher`的策略应用于每一个得到的资源。 350 | 351 | 352 | 353 | ##### [](#resources-wildcards-in-path-other-stuff)通配符的补充说明 354 | 355 | 请注意,除非所有目标资源都存在文件系统中,否则`classpath*:`与Ant样式模式结合,都只能在至少有一个确定了根路径的情况下,才能达到预期的效果。 这意味着`classpath*:*.xml`等模式可能无法从jar文件的根目录中检索文件,而只能从根目录中的扩展目录中检索文件。 356 | 357 | 问题的根源是JDK的`ClassLoader.getResources()`方法的局限性。当向`ClassLoader.getResources()`传入空串时(表示搜索潜在的根目录), 只能获取的文件系统的位置路径,即获取不了jar中文件的位置路径。Spring也会评估`URLClassLoader`运行时配置和jar文件中的`java.class.path`清单,但这不能保证导致可移植行为。 358 | 359 | 扫描类路径包需要在类路径中存在相应的目录条目。 使用Ant构建JAR时,请不要激活JAR任务的文件开关。 此外,在某些环境中,类路径目录可能不会基于安全策略公开 - 例如,JDK 1.7.0_45及更高版本上的独立应用程序(需要在清单中设置'Trusted-Library' 。 请参阅[http://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources](https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)))。 360 | 361 | 在JDK 9的模块路径(Jigsaw)上,Spring的类路径扫描通常按预期工作。 此处强烈建议将资源放入专用目录,避免上述搜索jar文件根级别的可移植性问题。 362 | 363 | 如果有多个类路径上都用搜索到的根包,那么使用`classpath:`和ant风格模式一起指定资源并不保证会找到匹配的资源。请考虑以下资源位置示例: 364 | 365 | com/mycompany/package1/service-context.xml 366 | 367 | 现在考虑一个人可能用来尝试查找该文件的Ant风格路径: 368 | 369 | classpath:com/mycompany/**/service-context.xml 370 | 371 | 这样的资源可能只在一个位置,但是当使用前面例子之类的路径来尝试解析它时,解析器会处理`getResource("com/mycompany");`返回的(第一个)URL。 当在多个类路径存在基础包节点`"com/mycompany"`时(如在多个jar存在这个基础节点),解析器就不一定会找到指定资源。因此,这种情况下建议结合使用`classpath*:` 和ant风格模式,`classpath*:`会让解析器去搜索所有包含基础包节点的类路径。 372 | 373 | 374 | 375 | #### [](#resources-filesystemresource-caveats)2.7.3. `FileSystemResource` 的警告 376 | 377 | 当`FileSystemResource`与`FileSystemApplicationContext`之间没有联系(即,当`FileSystemApplicationContext`不是实际的`ResourceLoader`时)时会按预期处理绝对路径和相对路径。 相对路径是相对与当前工作目录而言的,而绝对路径则是相对文件系统的根目录而言的。 378 | 379 | 但是,出于向后兼容性(历史)的原因,当`FileSystemApplicationContext`是`ResourceLoader`时,这会发生变化。`FileSystemApplicationContext`强制所有有联系的`FileSystemResource`实例将所有位置路径视为相对路径, 无论它们是否以'/'开头。 实际上,这意味着以下示例是等效的: 380 | 381 | ```java 382 | ApplicationContext ctx = 383 | new FileSystemXmlApplicationContext("conf/context.xml"); 384 | 385 | ApplicationContext ctx = 386 | new FileSystemXmlApplicationContext("/conf/context.xml"); 387 | ``` 388 | 389 | 以下示例也是等效的(即使它们有所不同,因为一个案例是相对的而另一个案例是绝对的): 390 | 391 | ```java 392 | FileSystemXmlApplicationContext ctx = ...; 393 | ctx.getResource("some/resource/path/myTemplate.txt"); 394 | 395 | FileSystemXmlApplicationContext ctx = ...; 396 | ctx.getResource("/some/resource/path/myTemplate.txt"); 397 | ``` 398 | 399 | 实际上,如果确实需要使用绝对路径,建议放弃使用`FileSystemResource`和`FileSystemXmlApplicationContext`,而强制使用 `file:`的`UrlResource`。 400 | 401 | ```java 402 | // actual context type doesn't matter, the Resource will always be UrlResource 403 | ctx.getResource("file:///some/resource/path/myTemplate.txt"); 404 | 405 | // force this FileSystemXmlApplicationContext to load its definition via a UrlResource 406 | ApplicationContext ctx = 407 | new FileSystemXmlApplicationContext("file:///conf/context.xml"); 408 | ``` 409 | -------------------------------------------------------------------------------- /pages/integration/scheduling.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#scheduling)7\. 执行任务和任务计划 4 | ---------------------------- 5 | 6 | Spring框架分别为异步执行、`TaskExecutor`的任务调度和`TaskScheduler`接口提供了抽象。Spring还具有支持线程池或委派到应用程序服务器环境CommonJ的接口实现。最终, 在Java SE 5、Java SE 6和Java EE有差异的环境都实现了一套公共的抽象接口。 7 | 8 | Spring还具有集成类,支持使用`Timer`(JDK自1.3以来的一部分)和Quartz Scheduler([http://quartz-scheduler.org](http://quartz-scheduler.org))进行调度。 您可以使用`FactoryBean`同时分别对`Timer`或`Trigger`实例进行可选引用来设置这两个调度程序。 此外,还提供了Quartz Scheduler和 `Timer`的便捷类,它允许您调用现有目标对象的方法(类似于正常的`MethodInvokingFactoryBean`操作)。 9 | 10 | 11 | 12 | ### [](#scheduling-task-executor)7.1. Spring `TaskExecutor` 抽象 13 | 14 | Executors是JDK中使用的线程池的名字,executor意思是无法保证底层的实现实际是一个池,一个executor可以是单线程的或者是同步的。Spring的抽象隐藏了Java SE和Java EE环境之间的实现细节。 15 | 16 | Spring的`TaskExecutor`接口和`java.util.concurrent.Executor`接口是相同的。实际上,他存在的主要原因是在使用线程池时对Java 5抽象的程度不同。该接口有一个 (`execute(Runnable task)`)方法,它根据线程池的语义和配置接受执行任务. 17 | 18 | 最初创建`TaskExecutor`是为了给其他Spring组件提供所需的线程池抽象。诸如`ApplicationEventMulticaster`,JMS的`AbstractMessageListenerContainer`和Quartz集成之类的组件都使用 `TaskExecutor`抽象来池化线程。 但是,如果您的bean需要线程池行为,您也可以根据自己的需要使用此抽象。 19 | 20 | 21 | 22 | #### [](#scheduling-task-executor-types)7.1.1. `TaskExecutor` 类型 23 | 24 | Spring包含许多TaskExecutor的预构建实现。很可能,你永远不需要实现自己的。 Spring提供的变体如下: 25 | 26 | * `SyncTaskExecutor`: 此实现不会异步执行调用。 相反,每次调用都发生在调用线程中。 它主要用于不需要多线程的情况,例如在简单的测试用例中。 27 | 28 | * `SimpleAsyncTaskExecutor`: 此实现不会重用任何线程。 相反,它为每次调用启动一个新线程。 但是,它确实支持并发限制,该限制会阻止任何超出限制的调用,直到释放一个插槽。 如果您正在寻找真正的池,请参阅此列表中稍后的`ThreadPoolTaskExecutor`。 29 | 30 | * `ConcurrentTaskExecutor`: 此实现是 `java.util.concurrent.Executor`对象的适配器。有一个可选的`ThreadPoolTaskExecutor`,它将 `Executor`配置参数作为bean属性公开。很少需要使用到`ConcurrentTaskExecutor`,但如果`ThreadPoolTaskExecutor`不够灵活,那么你就需要`ConcurrentTaskExecutor`。 31 | 32 | * `ThreadPoolTaskExecutor`: 这种实现是最常用的。 它公开了bean属性,用于配置`java.util.concurrent.ThreadPoolExecutor`并将其包装在`TaskExecutor`中。 如果您需要适应不同类型的`java.util.concurrent.Executor`,我们建议您使用`ConcurrentTaskExecutor`。 33 | 34 | * `WorkManagerTaskExecutor`: 此实现使用CommonJ `WorkManager`作为其后备服务提供程序,并且是在Spring应用程序上下文中在WebLogic或WebSphere上设置基于CommonJ的线程池集成的中心便利类。 35 | 36 | * `DefaultManagedTaskExecutor`: 此实现在JSR-236兼容的运行时环境(例如Java EE 7+应用程序服务器)中使用JNDI获取的`ManagedExecutorService`,为此目的替换CommonJ WorkManager。 37 | 38 | 39 | 40 | 41 | #### [](#scheduling-task-executor-usage)7.1.2. 使用 `TaskExecutor` 42 | 43 | Spring的 `TaskExecutor`实现用作简单的JavaBeans。 在下面的示例中,我们定义了一个使用`ThreadPoolTaskExecutor`异步打印出一组消息的bean: 44 | 45 | ```java 46 | import org.springframework.core.task.TaskExecutor; 47 | 48 | public class TaskExecutorExample { 49 | 50 | private class MessagePrinterTask implements Runnable { 51 | 52 | private String message; 53 | 54 | public MessagePrinterTask(String message) { 55 | this.message = message; 56 | } 57 | 58 | public void run() { 59 | System.out.println(message); 60 | } 61 | } 62 | 63 | private TaskExecutor taskExecutor; 64 | 65 | public TaskExecutorExample(TaskExecutor taskExecutor) { 66 | this.taskExecutor = taskExecutor; 67 | } 68 | 69 | public void printMessages() { 70 | for(int i = 0; i < 25; i++) { 71 | taskExecutor.execute(new MessagePrinterTask("Message" + i)); 72 | } 73 | } 74 | } 75 | ``` 76 | 77 | 如您所见,您可以将`Runnable`添加到队列中,而不是从池中检索线程并自行执行。 然后,`TaskExecutor`使用其内部规则来确定任务何时执行。 78 | 79 | 要配置`TaskExecutor`使用的规则,我们公开了简单的bean属性: 80 | 81 | ```xml 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | ``` 92 | 93 | 94 | 95 | ### [](#scheduling-task-scheduler)7.2. Spring `TaskScheduler` 抽象 96 | 97 | 除了`TaskExecutor`抽象之外,Spring 3.0还引入了一个`TaskScheduler`,它具有各种方法,可以在将来的某个时刻调度任务。 以下清单显示了`TaskScheduler`接口定义: 98 | 99 | ```java 100 | public interface TaskScheduler { 101 | 102 | ScheduledFuture schedule(Runnable task, Trigger trigger); 103 | 104 | ScheduledFuture schedule(Runnable task, Instant startTime); 105 | 106 | ScheduledFuture schedule(Runnable task, Date startTime); 107 | 108 | ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period); 109 | 110 | ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); 111 | 112 | ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period); 113 | 114 | ScheduledFuture scheduleAtFixedRate(Runnable task, long period); 115 | 116 | ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay); 117 | 118 | ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); 119 | 120 | ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay); 121 | 122 | ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); 123 | } 124 | ``` 125 | 126 | 最简单的方法是一个名为`schedule`的方法,它只接受`Runnable`和`Date`。 这会导致任务在指定时间后运行一次。 所有其他方法都能够安排任务重复运行。 通过这些方法,在简单的周期中需要以固定频率和固定时间间隔方法执行任务是实现的,但接受`Trigger`会更方便。 127 | 128 | 129 | 130 | #### [](#scheduling-trigger-interface)7.2.1. `Trigger` 接口 131 | 132 | `Trigger`接口基本上受到JSR-236的启发,从Spring 3.0开始,它尚未正式实现。Trigger的基本思想是可以基于过去的执行结果或甚至任意条件来确定执行时间。如果这些确定考虑到了前一次执行的结果, 则该信息在`TriggerContext`中可用。`Trigger` 接口本身非常简单:: 133 | 134 | ```java 135 | public interface Trigger { 136 | 137 | Date nextExecutionTime(TriggerContext triggerContext); 138 | } 139 | ``` 140 | 141 | `TriggerContext`是最重要的部分。 它封装了所有相关数据,如有必要,将来可以进行扩展。 `TriggerContext`是一个接口(默认情况下使用 `SimpleTriggerContext`实现)。 以下清单显示了`Trigger`实现的可用方法。 142 | 143 | ```java 144 | public interface TriggerContext { 145 | 146 | Date lastScheduledExecutionTime(); 147 | 148 | Date lastActualExecutionTime(); 149 | 150 | Date lastCompletionTime(); 151 | } 152 | ``` 153 | 154 | 155 | 156 | #### [](#scheduling-trigger-implementations)7.2.2. `Trigger` 实现 157 | 158 | Spring提供了两个`Trigger`接口的实现 , 最有趣的是`CronTrigger`。 它支持基于cron表达式调度任务。例如,以下任务被安排在每小时超过15分钟, 但仅在工作日的早上9时到下午5时"营业时间"内运行: 159 | 160 | ``` 161 | scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI")); 162 | ``` 163 | 164 | 另一个现成的实现是`PeriodicTrigger`,它接受一个固定的周期、一个可选的初始延迟值和一个布尔值来指示该期间是否应解释为固定速率或固定延迟。由于`TaskScheduler`接口已经定义了以固定速率或固定延迟来调度任务的方法,因此应该尽可能直接使用这些方法。 `PeriodicTrigger`实现的价值在于,它可以在依赖于`Trigger`抽象的组件中使用。例如,允许周期性触发器、cron-based触发器、甚至是可互换使用的自定义触发器实现,可能会很方便。此类组件可以利用依赖项注入,这样可以在外部配置此类`Triggers`,因此容易修改或扩展。 165 | 166 | 167 | 168 | #### [](#scheduling-task-scheduler-implementations)7.2.3. `TaskScheduler` 实现 169 | 170 | 与Spring的`TaskExecutor`抽象一样, `TaskScheduler`的主要好处是,依赖于调度行为的代码不必与特定的调度程序实现耦合。当在应用程序服务器环境中运行时,不应由应用程序本身直接创建线程,因此提供的灵活性尤其重要。对于这种情况, Spring提供了一个`TimerManagerTaskScheduler`,它委托给WebLogic或WebSphere上的CommonJ `TimerManager`,以及一个委托给Java EE 7+环境中的JSR-236 `ManagedScheduledExecutorService`的更新的`DefaultManagedTaskScheduler`。 两者通常都配置有JNDI查找。 171 | 172 | 每当外部线程管理不是必需的时候,更简单的替代方案是应用程序中的本地`ScheduledExecutorService`设置,可以通过Spring的`ConcurrentTaskScheduler`进行调整。 为方便起见,Spring还提供了一个`ThreadPoolTaskScheduler`,它在内部委托给`ScheduledExecutorService`,以提供沿`ThreadPoolTaskExecutor`行的公共bean样式配置。些变体适用于宽松应用程序服务器环境中的本地嵌入式线程池设置,特别是在Tomcat和Jetty上。 173 | 174 | 175 | 176 | ### [](#scheduling-annotation-support)7.3. 对调度和异步执行的注解支持 177 | 178 | Spring为任务调度和异步方法执行提供了注解支持 179 | 180 | 181 | 182 | #### [](#scheduling-enable-annotation-support)7.3.1. 启用调度注解 183 | 184 | 要启用对`@Scheduled`和`@Async`注解的支持,请将`@EnableScheduling`和`@EnableAsync`添加到您的`@Configuration` 类中。如下例所示:: 185 | 186 | ```java 187 | @Configuration 188 | @EnableAsync 189 | @EnableScheduling 190 | public class AppConfig { 191 | } 192 | ``` 193 | 194 | 您可以自由选择您的应用程序的相关注解。例如,如果您只需要支持`@Scheduled`,那么就省略`@EnableAsync`。对于更多的fine-grained控制,您可以另外实现[`SchedulingConfigurer`](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/scheduling/annotation/SchedulingConfigurer.html)和/或[`AsyncConfigurer`](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/scheduling/annotation/AsyncConfigurer.html)接口。有关详细信息,请参阅对应的javadocs。 195 | 196 | 如果您喜欢XML配置, 请使用`` 元素。如下: 197 | 198 | ```xml 199 | 200 | 201 | 202 | ``` 203 | 204 | 请注意,在上面的XML中,提供了一个执行器引用来处理与`@Async`注解的方法对应的那些任务,并提供了调度程序引用来管理那些用`@Scheduled`注解的方法。 205 | 206 | 处理`@Async`注解的默认建议模式是`proxy`,它允许仅通过代理拦截调用。 同一类中的本地调用不能以这种方式截获。 对于更高级的拦截模式,请考虑结合编译时或加载时编织切换到`aspectj`模式。 207 | 208 | 209 | 210 | #### [](#scheduling-annotation-support-scheduled)7.3.2. `@Scheduled` 注解 211 | 212 | 可以将 `@Scheduled`注解与触发器元数据一起添加到方法中。例如, 下面的方法每隔5秒调用一次固定的延迟, 这意味着该期间将从每次调用的完成时间计算: 213 | 214 | ```java 215 | @Scheduled(fixedDelay=5000) 216 | public void doSomething() { 217 | // something that should execute periodically 218 | } 219 | ``` 220 | 221 | 如果需要安装固定的速度来执行,只需简单的改变注解中的属性名即可执行。下面可以每5秒执行速度在每个调用开始后: 222 | 223 | ```java 224 | @Scheduled(fixedRate=5000) 225 | public void doSomething() { 226 | // something that should execute periodically 227 | } 228 | ``` 229 | 230 | 对于固定延迟和固定速率任务, 可以指定初始延迟, 指示在第一次执行该方法之前等待的毫秒数。如下面的 `fixedRate`示例所示: 231 | 232 | ```java 233 | @Scheduled(initialDelay=1000, fixedRate=5000) 234 | public void doSomething() { 235 | // something that should execute periodically 236 | } 237 | ``` 238 | 239 | 如果简单的周期调度不够表达你的意愿, 则可以提供cron表达式。例如, 以下任务将只在工作日执行。 240 | 241 | ```java 242 | @Scheduled(cron="*/5 * * * * MON-FRI") 243 | public void doSomething() { 244 | // something that should execute on weekdays only 245 | } 246 | ``` 247 | 248 | 你可以使用 `zone`属性来指定解析cron表达式的时区 249 | 250 | 请注意,要计划的方法必须具有void返回,并且不能期望为任何参数。如果该方法需要与应用程序上下文中的其他对象进行交互, 则通常是通过依赖项注入提供的。 251 | 252 | 在Spring 4.3框架中, 任何范围的bean都支持`@Scheduled` 方法。 253 | 254 | 请确保在运行时不会在同一个`@Scheduled`注解类上初始化多个实例,除非您确实希望为每个此类实例都安排回调。与此相关,请确保不要在bean类上使用`@Configurable`,并将其与容器`@Scheduled`并注册为常规的Spring bean。如果是这样, 程序将会双重初始化,否则,一旦通过容器,并通过@Configurable切面,每个`@Scheduled`方法的结果都将被调用两次。 255 | 256 | 257 | 258 | #### [](#scheduling-annotation-support-async)7.3.3. `@Async` 注解 259 | 260 | 可以在方法上提供`@Async`注解,以便发生该方法的异步调用。换言之,调用方将在调用时立即返回,并且该方法的实际执行将发生在已提交到Spring `TaskExecutor`的任务中。在最简单的情况下,注解可以应用于`void`返回方法。如下: 261 | 262 | ```java 263 | @Async 264 | void doSomething() { 265 | // this will be executed asynchronously 266 | } 267 | ``` 268 | 269 | 与使用`@Scheduled`注解的注解方法不同,这些方法可以有预期参数的,因为调用方将在运行时以“normal”方式调用它们,而不是由容器管理的计划任务。例如,以下是`@Async`注解的合法应用: 270 | 271 | ```java 272 | @Async 273 | void doSomething(String s) { 274 | // this will be executed asynchronously 275 | } 276 | ``` 277 | 278 | 即使返回值的方法也可以异步调用,但是,此类方法需要具有`Future`类型的返回值。这仍然提供了异步执行的好处,以便调用者可以在调用`Future`上的`get()`之前执行其他任务。以下示例显示如何在返回值的方法上使用`@Async` : 279 | 280 | ```java 281 | @Async 282 | Future returnSomething(int i) { 283 | // this will be executed asynchronously 284 | } 285 | ``` 286 | 287 | `@Async`方法不仅可以声明一个常规的`java.util.concurrent.Future`返回类型,而且还可能是spring的`org.springframework.util.concurrent.ListenableFuture` 或者如Spring 4.2版本后,存在于JDK8的`java.util.concurrent.CompletableFuture`。用于与异步任务进行更丰富的交互,以及通过进一步的处理步骤进行组合操作。 288 | 289 | `@Async`不能与生命周期回调(如 `@PostConstruct`)一起使用。若要异步初始化Spring bean,则当前必须使用单独的初始化Spring bean,然后在目标上调用`@Async`注解方法。如下: 290 | 291 | ```java 292 | public class SampleBeanImpl implements SampleBean { 293 | 294 | @Async 295 | void doSomething() { 296 | // ... 297 | } 298 | 299 | } 300 | 301 | public class SampleBeanInitializer { 302 | 303 | private final SampleBean bean; 304 | 305 | public SampleBeanInitializer(SampleBean bean) { 306 | this.bean = bean; 307 | } 308 | 309 | @PostConstruct 310 | public void initialize() { 311 | bean.doSomething(); 312 | } 313 | 314 | } 315 | ``` 316 | 317 | 没有直接的XML配置等价于`@Async`,因为这些方法应该首先设计为异步执行,而不是外部得来的。但是,您可以手动设置Spring的`AsyncExecutionInterceptor`与Spring AOP结合使用自定义切点。 318 | 319 | 320 | 321 | #### [](#scheduling-annotation-support-qualification)7.3.4. 使用 `@Async`的Executor的条件 322 | 323 | 默认情况下,在方法上指定`@Async`时,使用的执行程序是 [启用异步支持时配置](#scheduling-enable-annotation-support)的执行程序,例如,如果使用XML或`AsyncConfigurer`实现(如果有),则为“annotation-driven”元素。但是,当需要指示执行给定方法时,应使用非默认的执行器时,可以使用`@Async`注解的`value`属性。以下示例显示了如何执行此操作: 324 | 325 | ```java 326 | @Async("otherExecutor") 327 | void doSomething(String s) { 328 | // this will be executed asynchronously by "otherExecutor" 329 | } 330 | ``` 331 | 332 | 在这种情况下,`"otherExecutor"`可以是Spring容器中任何`Executor` bean的名称,也可以是与任何`Executor` 关联的限定符的名称(例如,使用``元素或Spring的`@Qualifier`注解指定) 333 | 334 | 335 | 336 | #### [](#scheduling-annotation-support-exception)7.3.5. 使用`@Async`的异常管理 337 | 338 | 当`@Async`方法有`Future`类型的返回值时,可以很容易地管理在方法执行期间引发的异常,因为当调用对`Future`结果的`get`时将引发此异常。但是,对于 `void`返回类型,异常是无法捕获的,无法传输的。对于这些情况,可以提供`AsyncUncaughtExceptionHandler`来处理此类异常。 以下示例显示了如何执行此操作:: 339 | 340 | ```java 341 | public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler { 342 | 343 | @Override 344 | public void handleUncaughtException(Throwable ex, Method method, Object... params) { 345 | // handle exception 346 | } 347 | } 348 | ``` 349 | 350 | 默认情况下,仅记录异常。 您可以使用`AsyncConfigurer`或``XML元素定义自定义`AsyncUncaughtExceptionHandler`。 351 | 352 | 353 | 354 | ### [](#scheduling-task-namespace)7.4. `task` 命名空间 355 | 356 | 从Spring 3.0开始,有一个用于配置`TaskExecutor`和`TaskScheduler`实例的XML命名空间。 357 | 358 | 359 | 360 | #### [](#scheduling-task-namespace-scheduler)7.4.1. 'scheduler' 元素 361 | 362 | 下面的元素将创建具有指定线程池大小的`ThreadPoolTaskScheduler` 实例: 363 | 364 | ```xml 365 | 366 | ``` 367 | 368 | 为`id`属性提供的值将用作池中线程名称的前缀,`scheduler` 元素相对非常简单。如果不提供`pool-size` 属性,则默认的线程池将只有一个线程。计划程序再也没有其他配置选项。 369 | 370 | 371 | 372 | #### [](#scheduling-task-namespace-executor)7.4.2. `executor` 元素 373 | 374 | 以下创建一个`ThreadPoolTaskExecutor` 实例: 375 | 376 | ```xml 377 | 378 | ``` 379 | 380 | 与[上面](#scheduling-task-namespace-scheduler)的调度程序一样,为`id`属性提供的值将用作池中线程名称的前缀。就池大小而言, `executor`元素支持比`scheduler`元素更多的配置选项。首先,`ThreadPoolTaskExecutor`的线程池本身更具可配置。执行器的线程池可能有不同的核心值和最大大小,,而不仅仅是单个的大小。如果提供了单个值,则执行器将具有固定大小的线程池(核心和最大大小相同)。 但是,`executor` 元素的`pool-size` 属性也接受以`min-max`形式的范围。以下示例将最小值设置为`5`,最大值设置为`25`: 381 | 382 | ```xml 383 | 387 | ``` 388 | 389 | 从该配置中可以看出,还提供了`queue-capacity` (队列容量)值。还应根据执行者的队列容量来考虑线程池的配置,有关池大小和队列容量之间关系的详细说明,请参阅[`ThreadPoolExecutor`](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html)的文档。 主要的想法是,在提交任务时,如果活动线程的数目当前小于核心大小,执行器将首先尝试使用一个空闲线程。如果已达到核心大小,则只要尚未达到其容量,就会将该任务添加到队列中。只有这样,如果已达到队列的容量,执行器将创建一个超出核心大小的新线程。如果还达到最大大小,,则执行器将拒绝该任务。 390 | 391 | 默认情况下,队列是无限制的,但一般不会这样配置,因为当所有池线程都运行时,如果将很多的任务添加到该队列中,则会导致`OutOfMemoryErrors`。此外,如果队列是无界的,则最大大小根本没有效果。由于执行器总是在创建超出核心大小的新线程之前尝试该队列,因此队列必须具有有限的容量,以使线程池超出核心大小(这就是为什么在使用无界队列时,固定大小池是唯一合理的情况)。 392 | 393 | 如上所述,在任务被拒绝时考虑这种情况。默认情况下,当任务被拒绝时,线程池执行程序会抛出`TaskRejectedException`。但是,拒绝策略实际上是可配置的。使用默认拒绝策略时抛出异常,即`AbortPolicy`实现。对于可以在高负载下跳过某些任务的应用程序,您可以改为配置`DiscardPolicy`或`DiscardOldestPolicy`。另一个适用于需要在高负载下限制提交任务的应用程序的选项是`CallerRunsPolicy`。该策略不是抛出异常或丢弃任务,而是强制调用submit方法的线程自己运行任务。这个想法是这样的调用者在运行该任务时很忙,并且不能立即提交其他任务。因此,它提供了一种简单的方法来限制传入的负载,同时保持线程池和队列的限制。通常,这允许执行程序“赶上”它正在处理的任务,从而释放队列,池中或两者中的一些容量。您可以从`executor`元素上的`rejection-policy`属性的可用值枚举中选择任何这些选项。 394 | 395 | 以下示例显示了一个`executor` 元素,其中包含许多属性以指定各种行为:: 396 | 397 | ```xml 398 | 403 | ``` 404 | 405 | 最后, `keep-alive`设置确定在终止之前线程可能保持空闲的时间限制(以秒为单位)。如果池中当前有多个线程的核心数目,则在等待此时间量后不处理任务,多余的线程将被终止。时间值为零将导致多余的线程在执行任务后立即终止,而不需要在任务队列中保留后续工作。以下示例将`keep-alive`值设置为两分钟:: 406 | 407 | ```xml 408 | 412 | ``` 413 | 414 | 415 | 416 | #### [](#scheduling-task-namespace-scheduled-tasks)7.4.3. 'scheduled-tasks' 元素 417 | 418 | Spring的任务命名空间的最强大功能是支持配置在Spring应用程序上下文中安排的任务。这遵循了类似于Spring中其他“method-invokers”的方法,例如由JMS命名空间提供的用于配置消息驱动的pojo。 基本上, `ref`属性可以指向任何Spring管理的对象, `method` 属性提供要在该对象上调用的方法的名称。 以下清单显示了一个简单示例:: 419 | 420 | ```xml 421 | 422 | 423 | 424 | 425 | 426 | ``` 427 | 428 | Tscheduler由外部元素引用,每个单独的任务都包括其触发器元数据的配置。在前面的示例中,该元数据定义了一个定期触发器,它具有固定的延迟,表示每个任务执行完成后等待的毫秒数。另一个选项是"`fixed-rate`",表示无论以前执行多长时间,方法的执行频率。此外, ,对于`fixed-delay`和`fixed-rate`任务,可以指定'initial-delay'参数,指示在第一次执行该方法之前等待的毫秒数。为了获得更多的控制,可以改为提供一个`cron`属性。下面是一个演示其他选项的示例: 429 | 430 | ```xml 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | ``` 439 | 440 | 441 | 442 | ### [](#scheduling-quartz)7.5. 使用Quartz的Scheduler 443 | 444 | Quartz使用`Trigger`, `Job`, 和 `JobDetail`等对象来进行各种类型的任务调度。关于Quartz的基本概念,请参阅[http://quartz-scheduler.org](http://quartz-scheduler.org)。为了让基于Spring的应用程序方便使用,Spring提供了一些类来简化quartz的用法。 445 | 446 | 447 | 448 | #### [](#scheduling-quartz-jobdetail)7.5.1. 使用`JobDetailFactoryBean` 449 | 450 | Quartz `JobDetail` 对象保存运行一个任务所需的全部信息。Spring提供一个叫作 `JobDetailFactoryBean`的类让JobDetail能对一些有意义的初始值(从XML配置)进行初始化,让我们来看个例子: 451 | 452 | ```xml 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | ``` 462 | 463 | job detail 配置拥有所有运行job(`ExampleJob`)的必要信息。 可以通过job的data map来指定timeout。Job的data map可以通过`JobExecutionContext`(在运行时传递给你)来得到,但是 `JobDetail`同时把从job的data map中得到的属性映射到实际job中的属性中去。 所以,如果`ExampleJob`中包含一个名为 `timeout`的属性,`JobDetail`将自动为它赋值: 464 | 465 | ```java 466 | package example; 467 | 468 | public class ExampleJob extends QuartzJobBean { 469 | 470 | private int timeout; 471 | 472 | /** 473 | * Setter called after the ExampleJob is instantiated 474 | * with the value from the JobDetailFactoryBean (5) 475 | */ 476 | public void setTimeout(int timeout) { 477 | this.timeout = timeout; 478 | } 479 | 480 | protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { 481 | // do the actual work 482 | } 483 | 484 | } 485 | ``` 486 | 487 | data map中的所有附加属性当然也可以使用的 488 | 489 | 使用`name`和`group`属性,你可以分别修改job在哪一个组下运行和使用什么名称。默认情况下,job的名称等于`JobDetailFactoryBean`的名称(在上面的例子中为`exampleJob`)。 490 | 491 | 492 | 493 | #### [](#scheduling-quartz-method-invoking-job)7.5.2. 使用 `MethodInvokingJobDetailFactoryBean` 494 | 495 | 通常情况下,你只需要调用特定对象上的一个方法即可实现任务调度。你可以使用`MethodInvokingJobDetailFactoryBean`准确的做到这一点: 496 | 497 | ```xml 498 | 499 | 500 | 501 | 502 | ``` 503 | 504 | 上面例子将调用`exampleBusinessObject`中的`doIt` 方法如下: 505 | 506 | ```java 507 | public class ExampleBusinessObject { 508 | 509 | // properties and collaborators 510 | 511 | public void doIt() { 512 | // do the actual work 513 | } 514 | } 515 | ``` 516 | 517 | ``` 518 | 519 | ``` 520 | 521 | 使用`MethodInvokingJobDetailFactoryBean`你不需要创建只有一行代码且只调用一个方法的job, 你只需要创建真实的业务对象来包装具体的细节的对象。 522 | 523 | 默认情况下,Quartz Jobs是无状态的,可能导致jobs之间互相的影响。如果你为相同的`JobDetail`指定两个Trigger, 很可能当第一个job完成之前,第二个job就开始了。如果`JobDetail`对象实现了`Stateful`接口,就不会发生这样的事情。 第二个job将不会在第一个job完成之前开始。为了使得jobs不并发运行,设置`MethodInvokingJobDetailFactoryBean`中的`concurrent`标记为`false`: 524 | 525 | ```xml 526 | 527 | 528 | 529 | 530 | 531 | ``` 532 | 533 | 默认情况下,jobs在并发的方式下运行。 534 | 535 | 536 | 537 | #### [](#scheduling-quartz-cron)7.5.3. 使用triggers和`SchedulerFactoryBean`来织入任务 538 | 539 | 我们已经创建了job details,jobs。我们同时回顾了允许你调用特定对象上某一个方法的便捷的bean。 当然我们仍需要调度这些jobs。这需要使用triggers和`SchedulerFactoryBean`来完成。 Quartz中提供了几个triggers,Spring提供了两个带有方便默认值的Quartz `FactoryBean`实现:`CronTriggerFactoryBean`和`SimpleTriggerFactoryBean`。 540 | 541 | Triggers也需要被调度。Spring提供`SchedulerFactoryBean`来公开一些属性来设置triggers。`SchedulerFactoryBean`负责调度那些实际的triggers 542 | 543 | 以下清单使用`SimpleTriggerFactoryBean`和`CronTriggerFactoryBean`: 544 | 545 | ```xml 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | ``` 561 | 562 | 现在我们创建了两个triggers,其中一个开始延迟10秒以后每50秒运行一次,另一个每天早上6点钟运行。 我们需要创建一个`SchedulerFactoryBean`来最终实现上述的一切: 563 | 564 | ```xml 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | ``` 574 | 575 | 更多的属性你可以通过`SchedulerFactoryBean`来设置,例如job details使用的日期, 用来订制Quartz的一些属性以及其它相关信息。 你可以查阅[`SchedulerFactoryBean`](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html)的Javadoc。 -------------------------------------------------------------------------------- /pages/integration/overview.md: -------------------------------------------------------------------------------- 1 | * [1\. Spring的远程处理和Web服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting) 2 | * [1.1. 使用RMI公开服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-rmi) 3 | * [1.1.1. 使用RmiServiceExporter暴露服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-rmi-server) 4 | * [1.1.2. 连接客户端和服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-rmi-client) 5 | * [1.2. 使用Hessian通过HTTP远程调用服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-caucho-protocols) 6 | * [1.2.1. 使用DispatcherServlet 连接Hessian](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-caucho-protocols-hessian) 7 | * [1.2.2. 使用HessianServiceExporter暴露您的Bean](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-caucho-protocols-hessian-server) 8 | * [1.2.3. 在客户端的服务中链接](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-caucho-protocols-hessian-client) 9 | * [1.2.4. 通过Hessian公开的服务来应用HTTP基本的验证](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-caucho-protocols-security) 10 | * [1.3. 使用HTTP调用公开服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-httpinvoker) 11 | * [1.3.1. 公开服务对象](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-httpinvoker-server) 12 | * [1.3.2. 在客户端链接服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-httpinvoker-client) 13 | * [1.4. Web Services](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-web-services) 14 | * [1.4.1. 使用JAX-WS公开基于Servlet的Web服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-web-services-jaxws-export-servlet) 15 | * [1.4.2. 使用JAX-WS公开独立Web服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-web-services-jaxws-export-standalone) 16 | * [1.4.3. 使用JAX-WS RI的Spring支持公开Web服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-web-services-jaxws-export-ri) 17 | * [1.4.4. 使用JAX-WS访问Web服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-web-services-jaxws-access) 18 | * [1.5. 通过JMS公开服务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-jms) 19 | * [1.5.1. 服务端的配置](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-jms-server) 20 | * [1.5.2. 客户端方面的配置](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-jms-client) 21 | * [1.6. AMQP](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-amqp) 22 | * [1.7. 选择技术时的注意事项](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#remoting-considerations) 23 | * [1.8. REST 端点](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-client-access) 24 | * [1.8.1. 使用 RestTemplate](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-resttemplate) 25 | * [初始化](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-resttemplate-create) 26 | * [URIs](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-resttemplate-uri) 27 | * [Headers](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-template-headers) 28 | * [Body](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-template-body) 29 | * [消息转换器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-message-conversion) 30 | * [Jackson JSON 视图](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-template-jsonview) 31 | * [Multipart](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-template-multipart) 32 | * [1.8.2. 使用 AsyncRestTemplate (已废弃)](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/remoting.md#rest-async-resttemplate) 33 | * [2\. 企业级JavaBean(EJB) 集成](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/ejb.md#ejb) 34 | * [2.1. 访问 EJB](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/ejb.md#ejb-access) 35 | * [2.1.1. 概念](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/ejb.md#ejb-access-concepts) 36 | * [2.1.2. 访问本地的SLSBs](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/ejb.md#ejb-access-local) 37 | * [2.1.3. 访问远程的SLSBs](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/ejb.md#ejb-access-remote) 38 | * [2.1.4. 访问EJB 2.x SLSBs 和 EJB 3 SLSBs](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/ejb.md#ejb-access-ejb2-ejb3) 39 | * [3\. JMS (Java 消息服务 )](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms) 40 | * [3.1. 使用 Spring JMS](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-using) 41 | * [3.1.1. 使用 JmsTemplate](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-jmstemplate) 42 | * [3.1.2. Connections](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-connections) 43 | * [缓存消息资源](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-caching-resources) 44 | * [使用 SingleConnectionFactory](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-connection-factory) 45 | * [使用 CachingConnectionFactory](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jdbc-connection-factory-caching) 46 | * [3.1.3. 目的地管理](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-destinations) 47 | * [3.1.4. 消息监听容器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-mdp) 48 | * [使用 SimpleMessageListenerContainer](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-mdp-simple) 49 | * [使用 DefaultMessageListenerContainer](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-mdp-default) 50 | * [3.1.5. 事务管理](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-tx) 51 | * [3.2. 发送消息](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-sending) 52 | * [3.2.1. 使用消息转换器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-msg-conversion) 53 | * [3.2.2. 使用 SessionCallback 和 ProducerCallback](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-callbacks) 54 | * [3.3. 接收消息](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-receiving) 55 | * [3.3.1. 同步接收](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-receiving-sync) 56 | * [3.3.2. 异步接收:消息驱动的POJO](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-asynchronousMessageReception) 57 | * [3.3.3. 使用SessionAwareMessageListener接口](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-receiving-async-session-aware-message-listener) 58 | * [3.3.4. Using MessageListenerAdapter](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-receiving-async-message-listener-adapter) 59 | * [3.3.5. 处理事务内的消息](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-tx-participation) 60 | * [3.4. 用于支持JCA消息端点](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-jca-message-endpoint-manager) 61 | * [3.5. 注解驱动监听器端点](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-annotated) 62 | * [3.5.1. 允许监听器端点注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-annotated-support) 63 | * [3.5.2. 编程端点注册](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-annotated-programmatic-registration) 64 | * [3.5.3. 注解端点方法签名](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-annotated-method-signature) 65 | * [3.5.4. 响应管理](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-annotated-response) 66 | * [3.6. JMS命名空间支持](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jms.md#jms-namespace) 67 | * [4\. JMX](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx) 68 | * [4.1. 公开你的bean给JMX](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-exporting) 69 | * [4.1.1. 创建一个MBeanServer](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-exporting-mbeanserver) 70 | * [4.1.2. 重用已有的MBeanServer](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-mbean-server) 71 | * [4.1.3. 延迟初始化的MBean](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-exporting-lazy) 72 | * [4.1.4. 自动注册MBeans](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-exporting-auto) 73 | * [4.1.5. 控制注册的行为](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-exporting-registration-behavior) 74 | * [4.2. 控制您的Bean的管理界面](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-interface) 75 | * [4.2.1. 使用MBeanInfoAssembler 接口](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-interface-assembler) 76 | * [4.2.2. 使用源码级别的元数据:Java注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-interface-metadata) 77 | * [4.2.3. 源码级别的元数据类型](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-interface-metadata-types) 78 | * [4.2.4. 使用AutodetectCapableMBeanInfoAssembler 接口](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-interface-autodetect) 79 | * [4.2.5. 使用Java接口定义管理接口](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-interface-java) 80 | * [4.2.6. 使用 MethodNameBasedMBeanInfoAssembler](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-interface-methodnames) 81 | * [4.3. 为你的bean控制ObjectName实例](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-naming) 82 | * [4.3.1. 从属性中读取ObjectName](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-naming-properties) 83 | * [4.3.2. 使用 MetadataNamingStrategy](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-naming-metadata) 84 | * [4.3.3. 配置基于注解的MBean导出](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-context-mbeanexport) 85 | * [4.4. 使用 JSR-160 连接器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-jsr160) 86 | * [4.4.1. 服务端的连接器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-jsr160-server) 87 | * [4.4.2. 客户端连接器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-jsr160-client) 88 | * [4.4.3. 基于 Hessian/SOAP的JMX](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-jsr160-protocols) 89 | * [4.5. 通过代理访问MBeans](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-proxy) 90 | * [4.6. 通知](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-notifications) 91 | * [4.6.1. 注册通知的监听器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-notifications-listeners) 92 | * [4.6.2.发布通知](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-notifications-publishing) 93 | * [4.7. 更多资源](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/jmx.md#jmx-resources) 94 | * [5\. JCA CCI](#https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci) 95 | * [5.1. 配置 CCI](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-config) 96 | * [5.1.1. 连接器配置](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-config-connector) 97 | * [5.1.2. 在Spring中配置ConnectionFactory](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-config-connectionfactory) 98 | * [5.1.3. 配置CCI连接](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-config-cci-connections) 99 | * [5.1.4. 使用单独的CCI连接](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-config-single-connection) 100 | * [5.2. 使用Spring的CCI访问支持](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-using) 101 | * [5.2.1. 记录转换](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-record-creator) 102 | * [5.2.2. 使用 CciTemplate](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-using-template) 103 | * [5.2.3. DAO支持](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-using-dao) 104 | * [5.2.4. 自动输出记录生成](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#automatic-output-generation) 105 | * [5.2.5. CciTemplate Interaction 总结](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#template-summary) 106 | * [5.2.6. 直接使用CCI连接和交互](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-straight) 107 | * [5.2.7. CciTemplate 用法示例](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-template-example) 108 | * [5.3. 将CCI访问建模为操作对象](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-object) 109 | * [5.3.1. 使用 MappingRecordOperation](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-object-mapping-record) 110 | * [5.3.2. 使用 MappingCommAreaOperation](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-object-mapping-comm-area) 111 | * [5.3.3. 自动的输出记录生成](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-automatic-record-gen) 112 | * [5.3.4. 总结](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-object-summary) 113 | * [5.3.5. MappingRecordOperation 使用例子](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-objects-mappring-record-example) 114 | * [5.3.6. MappingCommAreaOperation 的使用例子](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-objects-mapping-comm-area-example) 115 | * [5.4. 事务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cci.md#cci-tx) 116 | * [6\. 电子邮件](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail) 117 | * [6.1. Usage](#mail-usage) 118 | * [6.1.1. MailSender与 SimpleMailMessage的基本用法](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail-usage-simple) 119 | * [6.1.2. 使用 JavaMailSender 和 MimeMessagePreparator](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail-usage-mime) 120 | * [6.2.使用JavaMail MimeMessageHelper](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail-javamail-mime) 121 | * [6.2.1. 发送附件和内嵌资源](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail-javamail-mime-attachments) 122 | * [附件](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail-javamail-mime-attachments-attachment) 123 | * [内嵌资源](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail-javamail-mime-attachments-inline) 124 | * [6.2.2. 使用模板库创建电子邮件内容](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/mail.md#mail-templates) 125 | * [7\. 执行任务和任务计划](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling) 126 | * [7.1. Spring TaskExecutor 抽象](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-executor) 127 | * [7.1.1. TaskExecutor 类型](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-executor-types) 128 | * [7.1.2. 使用 TaskExecutor](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-executor-usage) 129 | * [7.2. Spring TaskScheduler 抽象](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-scheduler) 130 | * [7.2.1. Trigger 接口](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-trigger-interface) 131 | * [7.2.2. Trigger 实现](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-trigger-implementations) 132 | * [7.2.3. TaskScheduler 实现](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-scheduler-implementations) 133 | * [7.3. 对调度和异步执行的注解支持](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-annotation-support) 134 | * [7.3.1. 启用调度注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-enable-annotation-support) 135 | * [7.3.2. @Scheduled 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-annotation-support-scheduled) 136 | * [7.3.3. @Async 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-annotation-support-async) 137 | * [7.3.4. 使用 @Async的Executor的条件](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-annotation-support-qualification) 138 | * [7.3.5. 使用@Async的异常管理](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-annotation-support-exception) 139 | * [7.4. task 命名空间](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-namespace) 140 | * [7.4.1. 'scheduler' 元素](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-namespace-scheduler) 141 | * [7.4.2. executor 元素](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-namespace-executor) 142 | * [7.4.3. 'scheduled-tasks' 元素](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-task-namespace-scheduled-tasks) 143 | * [7.5. 使用Quartz的Scheduler](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-quartz) 144 | * [7.5.1. 使用JobDetailFactoryBean](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-quartz-jobdetail) 145 | * [7.5.2. 使用 MethodInvokingJobDetailFactoryBean](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-quartz-method-invoking-job) 146 | * [7.5.3. 使用triggers和SchedulerFactoryBean来织入任务](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/scheduling.md#scheduling-quartz-cron) 147 | * [8\. 缓存抽象](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache) 148 | * [8.1. 了解缓存抽象](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-strategies) 149 | * [8.2. 基于注解声明缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations) 150 | * [8.2.1. @Cacheable 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-cacheable) 151 | * [默认的键生成](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-cacheable-default-key) 152 | * [自定义键生成器](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-cacheable-key) 153 | * [默认的缓存解析](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-cacheable-default-cache-resolver) 154 | * [自定义缓存解析](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-cacheable-cache-resolver) 155 | * [同步缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-cacheable-synchronized) 156 | * [条件缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-cacheable-condition) 157 | * [可用的缓存SpEL表达式内容](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-spel-context) 158 | * [8.2.2. @CachePut 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-put) 159 | * [8.2.3. @CacheEvict 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-evict) 160 | * [8.2.4. @Caching 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-caching) 161 | * [8.2.5. @CacheConfig 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotations-config) 162 | * [8.2.6. 启用缓存注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotation-enable) 163 | * [8.2.7. 使用自定义的注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-annotation-stereotype) 164 | * [8.3. JCache (JSR-107) 注解](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-jsr-107) 165 | * [8.3.1. 特性总结](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-jsr-107-summary) 166 | * [8.3.2. 启用 JSR-107 支持](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#enabling-jsr-107-support) 167 | * [8.4. 声明式基于XML的缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-declarative-xml) 168 | * [8.5. 配置缓存的存储](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-store-configuration) 169 | * [8.5.1. 基于JDKConcurrentMap 缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-store-configuration-jdk) 170 | * [8.5.2. 基于Ehcache的缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-store-configuration-ehcache) 171 | * [8.5.3. Caffeine Cache](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-store-configuration-caffeine) 172 | * [8.5.4. 基于GemFire的缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-store-configuration-gemfire) 173 | * [8.5.5. JSR-107 缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-store-configuration-jsr107) 174 | * [8.5.6. 处理没有后端的缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-store-configuration-noop) 175 | * [8.6. 各种各样的后端缓存插件](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-plug) 176 | * [8.7. 我可以如何设置TTL/TTI/Eviction policy/XXX特性?](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/cache.md#cache-specific-config) 177 | * [9\. 附录](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#appendix) 178 | * [9.1. XML Schemas](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas) 179 | * [9.1.1. jee Schema](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee) 180 | * [ (simple)](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee-jndi-lookup) 181 | * [ (使用单个JNDI环境设置)](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee-jndi-lookup-environment-single) 182 | * [ (多个JNDI环境设置)](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee-jndi-lookup-evironment-multiple) 183 | * [ (复杂)](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee-jndi-lookup-complex) 184 | * [ (简单)](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee-local-slsb) 185 | * [ (复杂)](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee-local-slsb-complex) 186 | * [](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jee-remote-slsb) 187 | * [9.1.2. jms Schema](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-jms) 188 | * [9.1.3. 使用 ](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-context-mbe) 189 | * [9.1.4. cache Schema](https://github.com/DocsHome/spring-docs/blob/master/pages/integration/appendix.md#xsd-schemas-cache) -------------------------------------------------------------------------------- /pages/web/overview.md: -------------------------------------------------------------------------------- 1 | * [1\. Spring Web MVC](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc) 2 | * [1.1. DispatcherServlet](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-servlet) 3 | * [1.1.1. 上下文层次结构](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-servlet-context-hierarchy) 4 | * [1.1.2. 特殊的Bean类型](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-servlet-special-bean-types) 5 | * [1.1.3. Web MVC 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-servlet-config) 6 | * [1.1.4. Servlet 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-container-config) 7 | * [1.1.5. 处理流程](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-servlet-sequence) 8 | * [1.1.6. 拦截器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-handlermapping-interceptor) 9 | * [1.1.7. 异常](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-exceptionhandlers) 10 | * [解析链](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-excetionhandlers-handling) 11 | * [容器错误页面](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-customer-servlet-container-error-page) 12 | * [1.1.8. 视图解析](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-viewresolver) 13 | * [处理](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-viewresolver-handling) 14 | * [重定向](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-redirecting-redirect-prefix) 15 | * [转发](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-redirecting-forward-prefix) 16 | * [Content Negotiation](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-multiple-representations) 17 | * [1.1.9. 区域](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-localeresolver) 18 | * [Time Zone](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-timezone) 19 | * [Header 解析器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-localeresolver-acceptheader) 20 | * [Cookie 解析器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-localeresolver-cookie) 21 | * [Session 解析器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-localeresolver-session) 22 | * [Locale 拦截器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-localeresolver-interceptor) 23 | * [1.1.10. 主题](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-themeresolver) 24 | * [定义一个主题](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-themeresolver-defining) 25 | * [解析主题](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-themeresolver-resolving) 26 | * [1.1.11. Multipart 解析器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-multipart) 27 | * [Apache Commons `FileUpload`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-multipart-resolver-commons) 28 | * [Servlet 3.0](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-multipart-resolver-standard) 29 | * [1.1.12. 日志](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-logging) 30 | * [敏感数据](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-logging-sensitive-data) 31 | * [1.2. 过滤器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#filters) 32 | * [1.2.1. Form Data](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#filters-http-put) 33 | * [1.2.2. Forwarded Headers](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#filters-forwarded-headers) 34 | * [1.2.3. Shallow ETag](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#filters-shallow-etag) 35 | * [1.2.4. CORS](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#filters-cors) 36 | * [1.3. 注解控制器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-controller) 37 | * [1.3.1. 声明](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-controller) 38 | * [AOP 代理](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-proxying) 39 | * [1.3.2. Request Mapping](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping) 40 | * [URI 模式匹配](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-uri-templates) 41 | * [模式比较](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-pattern-comparison) 42 | * [后缀匹配](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-suffix-pattern-match) 43 | * [后缀匹配和RFD](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-rfd) 44 | * [消费者媒体类型](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-consumes) 45 | * [生产者媒体类型](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-produces) 46 | * [参数, 请求头](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-params-and-headers) 47 | * [HTTP HEAD, OPTIONS](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-head-options) 48 | * [自定义注解](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-composed) 49 | * [显式注册](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestmapping-registration) 50 | * [1.3.3. 程序处理方法](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-methods) 51 | * [方法参数](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-arguments) 52 | * [返回值](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-return-types) 53 | * [类型转换](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-typeconversion) 54 | * [矩阵变量](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-matrix-variables) 55 | * [`@RequestParam`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestparam) 56 | * [`@RequestHeader`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestheader) 57 | * [`@CookieValue`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-cookievalue) 58 | * [`@ModelAttribute`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-modelattrib-method-args) 59 | * [`@SessionAttributes`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-sessionattributes) 60 | * [`@SessionAttribute`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-sessionattribute) 61 | * [`@RequestAttribute`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestattrib) 62 | * [Redirect 属性](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-redirecting-passing-data) 63 | * [Flash 属性](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-flash-attributes) 64 | * [Multipart](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-multipart-forms) 65 | * [`@RequestBody`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-requestbody) 66 | * [HttpEntity](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-httpentity) 67 | * [`@ResponseBody`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-responsebody) 68 | * [ResponseEntity](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-responseentity) 69 | * [Jackson JSON](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-jackson) 70 | * [1.3.4. 数据绑定](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-modelattrib-methods) 71 | * [1.3.5. `DataBinder`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-initbinder) 72 | * [1.3.6. 异常](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-exceptionhandler) 73 | * [方法参数](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-exceptionhandler-args) 74 | * [返回值](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-exceptionhandler-return-values) 75 | * [REST API 异常](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-rest-exceptions) 76 | * [1.3.7. 控制器 Advice](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-controller-advice) 77 | * [1.4. URI 链接](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-uri-building) 78 | * [1.4.1. UriComponents](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#web-uricomponents) 79 | * [1.4.2. UriBuilder](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#web-uribuilder) 80 | * [1.4.3. URI 编码](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#web-uri-encoding) 81 | * [1.4.4. 相对请求](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-servleturicomponentsbuilder) 82 | * [1.4.5. 控制器链接](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-links-to-controllers) 83 | * [1.4.6. 视图链接](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-links-to-controllers-from-views) 84 | * [1.5. 异步请求](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async) 85 | * [1.5.1. `DeferredResult`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-deferredresult) 86 | * [1.5.2. `Callable`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-callable) 87 | * [1.5.3. 处理过程](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-processing) 88 | * [异常处理](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-exceptions) 89 | * [拦截异步请求](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-interception) 90 | * [Compared to WebFlux](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-vs-webflux) 91 | * [1.5.4. HTTP 流](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-http-streaming) 92 | * [Objects](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-objects) 93 | * [SSE](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-sse) 94 | * [Raw Data](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-output-stream) 95 | * [1.5.5. Reactive Types(响应式类型)](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-reactive-types) 96 | * [1.5.6. 断开](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-disconnects) 97 | * [1.5.7. 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-configuration) 98 | * [Servlet 容器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-configuration-servlet3) 99 | * [Spring MVC](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-ann-async-configuration-spring-mvc) 100 | * [1.6. CORS](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors) 101 | * [1.6.1. 简介](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors-intro) 102 | * [1.6.2. 处理](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors-processing) 103 | * [1.6.3. `@CrossOrigin`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors-controller) 104 | * [1.6.4. 全局配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors-global) 105 | * [Java 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors-global-java) 106 | * [XML 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors-global-xml) 107 | * [1.6.5. CORS 过滤器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-cors-filter) 108 | * [1.7. Web Security](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-web-security) 109 | * [1.8. HTTP 缓存](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-caching) 110 | * [1.8.1. `CacheControl`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-caching-cachecontrol) 111 | * [1.8.2. 控制器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-caching-etag-lastmodified) 112 | * [1.8.3. 静态资源](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-caching-static-resources) 113 | * [1.8.4. ETag 过滤器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-httpcaching-shallowetag) 114 | * [1.9. 视图技术](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view) 115 | * [1.9.1. Thymeleaf](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-thymeleaf) 116 | * [1.9.2. FreeMarker](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-freemarker) 117 | * [视图配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-freemarker-contextconfig) 118 | * [FreeMarker 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-views-freemarker) 119 | * [Form Handling](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-freemarker-forms) 120 | * [1.9.3. Groovy Markup](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-groovymarkup) 121 | * [配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-groovymarkup-configuration) 122 | * [例子](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-groovymarkup-example) 123 | * [1.9.4. 脚本视图](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-script) 124 | * [要求](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-script-dependencies) 125 | * [脚本模板](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-script-integrate) 126 | * [1.9.5. JSP 和 JSTL](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-jsp) 127 | * [视图解析](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-jsp-resolver) 128 | * [JSPs 和 JSTL](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-jsp-jstl) 129 | * [Spring的JSP标签库](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-jsp-tags) 130 | * [Spring的表单标签库](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-jsp-formtaglib) 131 | * [1.9.6. Tiles](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-tiles) 132 | * [依赖](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-tiles-dependencies) 133 | * [配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-tiles-integrate) 134 | * [1.9.7. RSS 和 Atom](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-feeds) 135 | * [1.9.8. PDF 和 Excel](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-document) 136 | * [文档视图简介](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-document-intro) 137 | * [PDF 视图](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-document-pdf) 138 | * [Excel 视图](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-document-excel) 139 | * [1.9.9. Jackson](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-jackson) 140 | * [基于Jackson 的JSON 视图](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-json-mapping) 141 | * [基于Jackson的XML视图](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-xml-mapping) 142 | * [1.9.10. XML编组](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-xml-marshalling) 143 | * [1.9.11. XSLT 视图](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-xslt) 144 | * [Beans](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-xslt-beandefs) 145 | * [控制器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-xslt-controllercode) 146 | * [转换](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-view-xslt-transforming) 147 | * [1.10. MVC 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config) 148 | * [1.10.1. 启用MVC配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-enable) 149 | * [1.10.2. MVC 配置 API](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-customize) 150 | * [1.10.3. 类型转换](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-conversion) 151 | * [1.10.4. 验证](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-validation) 152 | * [1.10.5. 拦截器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-interceptors) 153 | * [1.10.6. 内容类型](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-content-negotiation) 154 | * [1.10.7. 消息转换](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-message-converters) 155 | * [1.10.8. 视图控制器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-view-controller) 156 | * [1.10.9. 视图解析器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-view-resolvers) 157 | * [1.10.10. 静态资源](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-static-resources) 158 | * [1.10.11. 默认 Servlet](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-default-servlet-handler) 159 | * [1.10.12. 路径匹配](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-path-matching) 160 | * [1.10.13. 高级 Java 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-advanced-java) 161 | * [1.10.14. 高级 XML 配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-config-advanced-xml) 162 | * [1.11. HTTP/2](https://github.com/DocsHome/spring-docs/blob/master/pages/web/mvc.md#mvc-http2) 163 | * [2\. REST 客户端](https://github.com/DocsHome/spring-docs/blob/master/pages/web/webmvc.md#webmvc-client) 164 | * [2.1. `RestTemplate`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/webmvc.md#webmvc-resttemplate) 165 | * [2.2. `WebClient`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/webmvc.md#webmvc-webclient) 166 | * [3\. 测试](https://github.com/DocsHome/spring-docs/blob/master/pages/web/testing.md#testing) 167 | * [4\. WebSockets](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket) 168 | * [4.1. WebSocket简介](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-intro) 169 | * [4.1.1. HTTP与WebSocket](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-intro-architecture) 170 | * [4.1.2. 何时使用WebSockets](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-intro-when-to-use) 171 | * [4.2. WebSocket API](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-server) 172 | * [4.2.1. `WebSocketHandler`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-server-handler) 173 | * [4.2.2. WebSocket 握手](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-server-handshake) 174 | * [4.2.3. 部署](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-server-deployment) 175 | * [4.2.4. 服务器配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-server-runtime-configuration) 176 | * [4.2.5. Allowed Origins(允许来源)](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-server-allowed-origins) 177 | * [4.3. SockJS回调选项](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback) 178 | * [4.3.1. 简介](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback-sockjs-overview) 179 | * [4.3.2. 开启 SockJS](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback-sockjs-enable) 180 | * [4.3.3. IE 8 和 9](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback-xhr-vs-iframe) 181 | * [4.3.4. 心跳](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback-sockjs-heartbeat) 182 | * [4.3.5. 客户端断开连接](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback-sockjs-servlet3-async) 183 | * [4.3.6. SockJS 和 CORS](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback-cors) 184 | * [4.3.7. `SockJsClient`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-fallback-sockjs-client) 185 | * [4.4. STOMP](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp) 186 | * [4.4.1. 简介](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-overview) 187 | * [4.4.2. 优点](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-benefits) 188 | * [4.4.3. 启用STOMP](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-enable) 189 | * [4.4.4. WebSocket服务器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-server-config) 190 | * [4.4.5. 消息流](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-message-flow) 191 | * [4.4.6. 注解控制器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-handle-annotations) 192 | * [`@MessageMapping`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-message-mapping) 193 | * [`@SubscribeMapping`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-subscribe-mapping) 194 | * [`@MessageExceptionHandler`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-exception-handler) 195 | * [4.4.7. 发送消息](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-handle-send) 196 | * [4.4.8. 简单的消息代理](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-handle-simple-broker) 197 | * [4.4.9. 全功能的消息代理](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-handle-broker-relay) 198 | * [4.4.10. 连接到消息代理](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-handle-broker-relay-configure) 199 | * [4.4.11. 点作为分隔符](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-destination-separator) 200 | * [4.4.12. 验证](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-authentication) 201 | * [4.4.13. 基于token的验证](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-authentication-token-based) 202 | * [4.4.14. 用户的目的地](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-user-destination) 203 | * [4.4.15. 消息顺序](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-ordered-messages) 204 | * [4.4.16. 事件](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-appplication-context-events) 205 | * [4.4.17. 拦截器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-interceptors) 206 | * [4.4.18. STOMP 客户端](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-client) 207 | * [4.4.19. WebSocket 范围](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-websocket-scope) 208 | * [4.4.20. 性能](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-configuration-performance) 209 | * [4.4.21. 监控](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-stats) 210 | * [4.4.22. 测试](https://github.com/DocsHome/spring-docs/blob/master/pages/web/websocket.md#websocket-stomp-testing) 211 | * [5\. Other Web 框架](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#web-integration) 212 | * [5.1. 通用的配置](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#web-integration-common) 213 | * [5.2. JSF](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#jsf) 214 | * [5.2.1. Spring Bean 解析器](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#jsf-springbeanfaceselresolver) 215 | * [5.2.2. Using `FacesContextUtils`](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#jsf-facescontextutils) 216 | * [5.3. Apache Struts 2.x](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#struts) 217 | * [5.4. Tapestry 5.x](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#tapestry) 218 | * [5.5. 更多资源](https://github.com/DocsHome/spring-docs/blob/master/pages/web/other.md#web-integration-resources) -------------------------------------------------------------------------------- /pages/integration/cache.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#cache)8\. 缓存抽象 4 | ------------------ 5 | 6 | 从3.1版本开始,Spring框架为在现有的Spring应用程序透明地添加缓存提供了支持。 与[事务](data-access.html#transaction)支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的影响最小。 7 | 8 | 从Spring 4.1开始,通过[JSR-107注解](#cache-jsr-107)和更多自定义选项的支持,缓存抽象得到了显着改进。 9 | 10 | 11 | 12 | ### [](#cache-strategies)8.1. 了解缓存抽象 13 | 14 | Cache vs Buffer 15 | 16 | 术语“buffer” 和 “cache,”倾向于可互换使用。 但请注意,它们代表不同的东西。 缓冲区通常用作快速和慢速实体之间的数据的中间临时存储区。由于一方必须等待另一个影响性能的因素, 因此缓冲区会通过允许整个数据块同时移动, 而不是一小块来缓解这一问题。数据只在缓冲区中写入和读取一次。此外, 缓冲区至少对知道它的一方是可见的。 17 | 18 | 另一方面,理论上的缓存是隐藏的,任何一方都不知道缓存是否发生。它还可以提高性能,而且相同的数据可以快速读取多次。 19 | 20 | 对两者更多的解释是在[here](https://en.wikipedia.org/wiki/Cache_(computing)#The_difference_between_buffer_and_cache) . 21 | 22 | 缓存抽象的核心是将缓存应用于Java方法,从而根据缓存中可用的信息减少执行次数。 也就是说,每次调用目标方法时,抽象都会应用一种缓存行为,该行为检查该方法是否已针对给定参数执行。 如果已执行,则返回缓存的结果,而不必执行实际方法。 如果该方法尚未执行,则执行该方法,并将结果缓存并返回给用户,以便下次调用该方法时,返回缓存的结果。 这样,对于给定的一组参数,昂贵的方法(无论是CPU还是IO)只能执行一次,并且重用结果而不必再次实际执行该方法。 缓存逻辑是透明应用的,不会对调用者造成任何干扰。 23 | 24 | 此方法仅适用于保证为给定输入(或参数)返回相同输出(结果)的方法,无论它执行多少次。 25 | 26 | 其他与缓存相关的操作由抽象提供,如更新缓存内容的能力或删除所有条目中的一个。如果缓存处理在应用程序过程中可以更改的数据,这些都是很有用的。 27 | 28 | 就像Spring框架中的其他服务一样,缓存服务是抽象(不是缓存实现),需要使用实际存储来存储缓存数据,也就是说,抽象使开发人员不必编写缓存逻辑,但不提供实际的存储。此抽象由`org.springframework.cache.Cache`和`org.springframework.cache.CacheManager`接口具体化。 29 | 30 | Spring提供了一些抽象实现:基于JDK `java.util.concurrent.ConcurrentMap`的缓存,[Ehcache 2.x](http://ehcache.org/),Gemfire缓存,[Caffeine](https://github.com/ben-manes/caffeine/wiki)和符合JSR-107的缓存(例如Ehcache 3.x)。 有关插入其他缓存存储和提供程序的详细信息,请参阅[不同的后端缓存](#cache-plug)。 31 | 32 | 缓存抽象没有针对多线程和多进程环境的特殊处理,因为这些功能由缓存实现处理。 33 | 34 | 如果您具有多进程环境(即,部署在多个节点上的应用程序),则需要相应地配置缓存提供程序。 根据您的使用情况,几个节点上的相同数据的副本就足够了。 但是,如果在应用程序过程中更改数据,则可能需要启用其他传播机制。 35 | 36 | 在代码中直接添加缓存的经典流程有 get-if-not-found-then-proceed-and-put-eventually 这里没有用到锁。同时允许多线程同步时加载同一个缓存。同样对于回收缓存也是相似。但如果有多个线程试图更新或者回收数据的话,可能会用到旧数据。某些缓存为程序为该区域的操作提供了高级功能,请参阅您正在使用的缓存提供程序的文档以获取详细信息。 37 | 38 | 要使用缓存抽象,您需要注意两个方面: 39 | 40 | * 缓存声明:确定需要缓存的方法及其策略。 41 | 42 | * 缓存配置:存储数据并从中读取数据的后端缓存。 43 | 44 | 45 | 46 | 47 | ### [](#cache-annotations)8.2. 基于注解声明缓存 48 | 49 | 对于缓存声明,Spring的缓存抽象提供了一组Java注解: 50 | 51 | * `@Cacheable`: 触发缓存储存 52 | 53 | * `@CacheEvict`: 触发缓存回收 54 | 55 | * `@CachePut`: 在不影响方法执行的情况下更新缓存 56 | 57 | * `@Caching`: 重新在方法上应用多个缓存操作 58 | 59 | * `@CacheConfig`: 在类级别共享与缓存有关的常见设置 60 | 61 | 62 | 63 | 64 | #### [](#cache-annotations-cacheable)8.2.1. `@Cacheable` 注解 65 | 66 | 顾名思义,`@Cacheable` 用于标定可缓存的方法 , 即将结果存储到缓存中的方法,以便在后续调用中(使用相同的参数),缓存的值可以在不执行方法的情况下返回。最简单的,注解声明要求带注解的方法关联缓存的名称。如下: 67 | 68 | ```java 69 | @Cacheable("books") 70 | public Book findBook(ISBN isbn) {...} 71 | ``` 72 | 73 | 在上面的代码段中,方法`findBook`与名为`books`的缓存关联。每次调用该方法时,都会检查缓存以查看是否已经执行了调用,并且不会重复。在大多数情况下,只有一个缓存被声明,注解允许指定多个名称,以便使用一个以上的缓存。在这种情况下,在执行方法之前将检查每个缓存,如果至少命中一个缓存,则将返回关联的值。如下: 74 | 75 | 即使未实际执行缓存的方法,也会更新不包含该值的所有其他缓存。 76 | 77 | 以下示例在 `findBook` 方法上使用`@Cacheable`: 78 | 79 | ```java 80 | @Cacheable({"books", "isbns"}) 81 | public Book findBook(ISBN isbn) {...} 82 | ``` 83 | 84 | 85 | 86 | ##### [](#cache-annotations-cacheable-default-key)默认的键生成 87 | 88 | 由于缓存实质上是键-值存储区,因此需要将每个缓存方法的调用转换为适合缓存访问的键。缓存抽象使用基于以下算法使用简单的键生成器-`KeyGenerator`,做到开箱即用。: 89 | 90 | * 如果没有参数被指定,则返回`SimpleKey.EMPTY`. 91 | 92 | * 如果只有一个参数被指定,则返回实例 93 | 94 | * 如果多于一个参数被指定,则返回一个`SimpleKey`包含所有的参数 95 | 96 | 97 | 这种方法适用于大多数用例。只要参数有自然的键并且实现了有效的`hashCode()` 和 `equals()`方法,如果不是这样的话, 那么战略就需要改变。 98 | 99 | 要提供不同的默认键生成器,您需要实现`org.springframework.cache.interceptor.KeyGenerator`接口。 100 | 101 | 默认的键生成策略在Spring 4.0版本有点改变。早期的Spring版本使用的键生成策略,对于多个键参数,只考虑参数的`hashCode()` 而没有考虑到 `equals()`方法,这将导致一定的碰撞(见[SPR-10237](https://jira.spring.io/browse/SPR-10237))。新的 `SimpleKeyGenerator` 为这种情况使用复合键。 102 | 103 | 如果您希望继续使用前面的 key strategy, 则可以配置已弃用的`org.springframework.cache.interceptor.DefaultKeyGenerator`类或创建自定义的基于`KeyGenerator`的实现。 104 | 105 | 106 | 107 | ##### [](#cache-annotations-cacheable-key)自定义键生成器 108 | 109 | 由于缓存是具有普遍性的,因此目标方法很可能具有不同的签名,不能简单地映射到缓存结构的顶部。当目标方法有多个参数时,问题显得更突出,其中只有一部分是适合于缓存的(其余仅由方法逻辑使用)。例如: 110 | 111 | ``` 112 | @Cacheable("books") 113 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 114 | ``` 115 | 116 | 乍一看,虽然两个 `boolean`参数影响了book的发现方式,但它们对缓存没有用处。 如果两个中只有一个重要而另一个不重要怎么办? 117 | 118 | 对于这种情况,`@Cacheable`注解允许用户指定`key`的生成属性。开发人员可以使用[SpEL](core.html#expressions))来选择感兴趣的参数(或它们的嵌套属性)执行操作,甚至调用任意方法,而不必编写任何代码或实现任何接口。这是在默认生成器。 119 | 120 | 以下示例是各种SpEL声明(如果您不熟悉SpEL,请阅读 [Spring Expression Language](core.html#expressions)): 121 | 122 | ```java 123 | @Cacheable(cacheNames="books", key="#isbn") 124 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 125 | 126 | @Cacheable(cacheNames="books", key="#isbn.rawNumber") 127 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 128 | 129 | @Cacheable(cacheNames="books", key="T(someType).hash(#isbn)") 130 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 131 | ``` 132 | 133 | 上面的代码段显示了选择某个参数、某个属性甚至是任意(静态)方法是多么容易。 134 | 135 | 如果负责生成键的算法太特殊,或者需要共享,则可以在操作上定义一个自定义`keyGenerator`。为此,请指定要使用的`KeyGenerator` bean实现的名称。如下: 136 | 137 | ```java 138 | @Cacheable(cacheNames="books", keyGenerator="myKeyGenerator") 139 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 140 | ``` 141 | 142 | `key`和 `keyGenerator`参数是互斥的, 指定两者的操作将导致异常。 143 | 144 | 145 | 146 | ##### [](#cache-annotations-cacheable-default-cache-resolver)默认的缓存解析 147 | 148 | 缓存抽象使用一个简单的 `CacheResolver`(可即用)它使用`CacheManager`配置检索在操作级别定义的缓存。 149 | 150 | 为了提供不同默认缓存解析,需要实现`org.springframework.cache.interceptor.CacheResolver` 接口. 151 | 152 | 153 | 154 | ##### [](#cache-annotations-cacheable-cache-resolver)自定义缓存解析 155 | 156 | 默认的缓存解析适合于应用对于使用单个`CacheManager`,并且不需要复杂的解析要求。 157 | 158 | 对于应用使用不同的缓存管理,可以设置`cacheManager` 使用每个操作。如以下示例所示: 159 | 160 | ```java 161 | @Cacheable(cacheNames="books", cacheManager="anotherCacheManager") (1) 162 | public Book findBook(ISBN isbn) {...} 163 | ``` 164 | 165 | **1**、Specifying `anotherCacheManager`. 166 | 167 | 也可以完全以类似于 [key generation](#cache-annotations-cacheable-key)的方式替换`CacheResolver`。为每个缓存操作请求该解决方案, 使实现能够根据运行时参数实际解析要使用的缓存。以下示例显示如何指定 `CacheResolver`: 168 | 169 | ```java 170 | @Cacheable(cacheResolver="runtimeCacheResolver") (1) 171 | public Book findBook(ISBN isbn) {...} 172 | ``` 173 | 174 | **1**、Specifying the `CacheResolver`. 175 | 176 | Spring 4.1版本以后, 缓存注解的 `value`属性不再是强制性的了,因为无论注解的内容如何,`CacheResolver`都可以提供此特定信息。 177 | 178 | 与 `key`和`keyGenerator`似,`cacheManager`和`cacheResolver`参数是互斥的,同时指定两者的操作将导致异常,因为`CacheResolver`实现忽略了自定义`CacheManager`。这可能不是你所期望的。 179 | 180 | 181 | 182 | ##### [](#cache-annotations-cacheable-synchronized)同步缓存 183 | 184 | 在多线程环境中,对于同一参数(通常在启动时),可能会同时调用某些操。默认情况下,缓存抽象不会锁定任何内容,并且可能会多次计算相同的值,从而无法达到缓存的目的。 185 | 186 | 对于这些特定情况,可以使用`sync`属性指示基础缓存提供程序在计算值时锁定缓存项。因此,只有一个线程将忙于计算该值,而其余的则被阻塞,直到在缓存中更新该项。以下示例显示了如何使用`sync`属性: 187 | 188 | ```java 189 | @Cacheable(cacheNames="foos", sync=true) (1) 190 | public Foo executeExpensiveOperation(String id) {...} 191 | ``` 192 | 193 | **1**、Using the `sync` attribute. 194 | 195 | 这是一个可选的功能,您喜欢的缓存库可能不支持它。核心框架提供的所有`CacheManager` 实现都支持它。有关详细信息,请参阅缓存提供程序的文档, 196 | 197 | 198 | 199 | ##### [](#cache-annotations-cacheable-condition)条件缓存 200 | 201 | 有时,一个方法做缓存可能不是万能的(例如,它可能依赖于给定的参数)。缓存注解通过`condition`参数支持此类功能,它采用 `SpEL` 表达式,并将其计算为`true`或`false`。如果为`true`,则方法执行缓存。如果不是,则它的行为就像不缓存的方法一样。 每次不管缓存中的值是什么或使用了什么参数,都将执行它。例如,仅当参数名称的长度小于32时,才会缓存以下方法: 202 | 203 | ```java 204 | @Cacheable(cacheNames="book", condition="#name.length() < 32") (1) 205 | public Book findBook(String name) 206 | ``` 207 | 208 | **1**、Setting a condition on `@Cacheable`. 209 | 210 | 此外, 除了`condition`参数外, 还可以使用`unless` 参数来决定不想缓存的值。与 `condition`不同,, `unless`在调用方法后计算表达式。扩展上一示例。也许我们只想缓存平装书,如下例所示: 211 | 212 | ```java 213 | @Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback") (1) 214 | public Book findBook(String name) 215 | ``` 216 | 217 | **1**、Using the `unless` attribute to block hardbacks. 218 | 219 | 缓存抽象支持`java.util.Optional`,仅在其存在时将其内容用作缓存值。 `#result`总是引用业务实体而从不支持包装器,因此前面的示例可以重写如下: 220 | 221 | ```java 222 | @Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback") 223 | public Optional findBook(String name) 224 | ``` 225 | 226 | 请注意,`result`仍然引用 `Book` 而不是`Optional`。 由于它可能为`null`,我们应该使用安全导航操作符。 227 | 228 | 229 | 230 | ##### [](#cache-spel-context)可用的缓存SpEL表达式内容 231 | 232 | 每个`SpEL`表达式都可以用于再次指定的上下文值。除了在参数中生成外,框架还提供专用的与缓存相关的元数据(如参数名称)。下表列出了可用于[`context`](core.html#expressions-language-ref)的项,以便可以使用这些项进行键和条件计算。 233 | 234 | Table 11. 缓存SpEL可用的元数据 235 | 236 | | 名字 | 位置 | 描述 | 例子 | 237 | | ---- | ---- | ---- | ---- | 238 | | `methodName` | Root object | 正在调用的方法的名称 | `#root.methodName` | 239 | | `method` | Root object | 正在调用的方法 | `#root.method.name` | 240 | | `target` | Root object | 正在调用的目标对象 | `#root.target` | 241 | | `targetClass` | Root object | 被调用的目标的类 | `#root.targetClass` | 242 | | `args` | Root object | 用于调用目标的参数(作为数组) | `#root.args[0]` | 243 | | `caches` | Root object | 执行当前方法的高速缓存的集合 |`#root.caches[0].name` | 244 | | Argument name | Evaluation context | 任何方法参数的名称。 如果名称不可用(可能由于没有调试信息),参数名称也可以在`#a<#arg>`下获得,其中 `#arg`代表参数索引(从0开始)。 | `#iban` or `#a0` (you can also use `#p0` or `#p<#arg>` notation as an alias). | 245 | | `result` | Evaluation context | 方法调用的结果(要缓存的值)。 仅在`unless`表达式,缓存`cache put`表达式(计算`key`)或缓存`cache evict`表达式(当`beforeInvocation`为 `false`时)中可用。 对于受支持的包装器(例如`Optional`), `#result`引用实际的对象,而不是包装器。 | `#result` | 246 | 247 | 248 | 249 | 250 | #### [](#cache-annotations-put)8.2.2. `@CachePut` 注解 251 | 252 | 对于需要更新缓存而不影响方法执行的情况,可以使用`@CachePut`注解。即,将始终执行该方法,并将其结果放入缓存(根据`@CachePut`选项)。它支持与`@Cacheable`相同的选项,,并且应用于缓存填充,而不是方法流优化。以下示例使用`@CachePut`注解: 253 | 254 | ```java 255 | @CachePut(cacheNames="book", key="#isbn") 256 | public Book updateBook(ISBN isbn, BookDescriptor descriptor) 257 | ``` 258 | 259 | 通常强烈建议不要在同一方法上使用`@CachePut`和`@Cacheable` 注解,因为它们具有不同的行为。当后者导致使用缓存跳过方法执行时,前者强制执行以执行缓存更新。这会导致意外的行为,并且除了特定的某些情况(例如,有排除彼此的条件的注解)之外, 应避免此类声明。还要注意,此类条件不应依赖于结果对象(即`#result`变量),因为它们是预先验证的,以确认排除。 260 | 261 | 262 | 263 | #### [](#cache-annotations-evict)8.2.3. `@CacheEvict` 注解 264 | 265 | 抽象缓存不仅允许缓存存储区的填充,而且还可以回收。此过程对于从缓存中删除陈旧或未使用的数据非常有用。相比于`@Cacheable`,注解 `@CacheEvict`执行缓存回收的区分方法,即充当从缓存中移除数据的触发器的方法。就像它的同级注解一样, `@CacheEvict`需要指定一个(或多个)受该操作影响的缓存,允许自定义缓存和键解析或指定的条件,但除此之外 ,还具有一个额外的参数`allEntries`,它指示是否需要进行缓存范围的回收,而不是只执行一项(基于键): 266 | 267 | ```java 268 | @CacheEvict(cacheNames="books", allEntries=true) (1) 269 | public void loadBooks(InputStream batch) 270 | ``` 271 | 272 | **1**、Using the `allEntries` attribute to evict all entries from the cache. 273 | 274 | 当需要清除整个缓存区域时,此选项会派上用场。然后将每个条目回收(这将需要很长时间,因为它的效率很低),所有的条目都在一个操作中被移除,如上所示。请注意,框架将忽略此方案中指定的任何键,因为它不适用(整个缓存被回收的不仅仅是一个条目)。 275 | 276 | 您还可以通过使用`beforeInvocation`属性指示回收是在(默认)之后还是在方法执行之前进行的。前者提供与注解的其余部分相同的语义:一旦方法成功完成,就会执行缓存上的操作(在本例中为回收)。如果方法未执行(因为它可能被缓存)或抛出异常,则不会发生回收。 后者(`beforeInvocation=true`)导致回收始终在调用方法之前发生。 这在回收不需要与方法结果相关联的情况下非常有用。 277 | 278 | 必须注意的是,`void`方法可以与`@CacheEvict`一起使用-因为这些方法充当触发器,所以返回值被忽略(因为它们不与缓存交互)。`@Cacheable` 将数据添加/更新到缓存中的情况并非如此,因此需要重新请求结果。 279 | 280 | 281 | 282 | #### [](#cache-annotations-caching)8.2.4. `@Caching` 注解 283 | 284 | 在某些情况下,需要指定相同类型的多个注解(例如`@CacheEvict`或`@CachePut`)。例如,因为条件或键表达式在不同的缓存之间是不同的。 `@Caching`允许在同一方法上使用多个嵌套的`@Cacheable`、`@CachePut`和`@CacheEvict`。以下示例使用两个`@CacheEvict`注解: 285 | 286 | ```java 287 | @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") }) 288 | public Book importBooks(String deposit, Date date) 289 | ``` 290 | 291 | 292 | 293 | #### [](#cache-annotations-config)8.2.5. `@CacheConfig` 注解 294 | 295 | 到目前为止,我们已经看到缓存操作提供了许多自定义选项,您可以为每个操作设置这些选项。但是,如果这些自定义选项适用于该类的所有操作,则可以对其进行配置。例如,指定要用于该类的每个缓存操作的缓存的名称可以由单个类级别定义替换。这就是`@CacheConfig`发挥作用的地方。 以下示例使用`@CacheConfig`设置缓存的名称: 296 | 297 | ```java 298 | @CacheConfig("books") (1) 299 | public class BookRepositoryImpl implements BookRepository { 300 | 301 | @Cacheable 302 | public Book findBook(ISBN isbn) {...} 303 | } 304 | ``` 305 | 306 | **1**、Using `@CacheConfig` to set the name of the cache. 307 | 308 | `@CacheConfig`是一个类级注解,它允许共享缓存名称、自定义`KeyGenerator`、自定义`CacheManager`以及最终的自定义`CacheResolver`。将此注解放在类上不会打开任何缓存操作。 309 | 310 | 操作级自定义项将始终覆盖在`@CacheConfig`上设置的自定义项。因此,每个缓存操作都提供了三个级别的自定义项: 311 | 312 | * 全局配置, 适用于`CacheManager`, `KeyGenerator`. 313 | 314 | * 在类级别, 使用@ `@CacheConfig`. 315 | 316 | * 在操作级别 317 | 318 | 319 | 320 | 321 | #### [](#cache-annotation-enable)8.2.6. 启用缓存注解 322 | 323 | 值得注意的是,即使声明缓存注解不会自动触发它们的操作(如Spring中的许多项),该功能也必须以声明方式启用(这意味着如果您怀疑缓存是罪魁祸首,您可以通过只删除一个配置行而不是代码中的所有注解)。 324 | 325 | 要启用缓存注解,请将注解`@EnableCaching`添加到您的`@Configuration`类之一上: 326 | 327 | ```java 328 | @Configuration 329 | @EnableCaching 330 | public class AppConfig { 331 | } 332 | ``` 333 | 334 | 在XML的配置中,可以使用`cache:annotation-driven` 元素: 335 | 336 | ```xml 337 | 343 | 344 | 345 | 346 | ``` 347 | 348 | `cache:annotation-driven` 元素和`@EnableCaching` 注解允许指定各种选项,从而影响通过AOP将缓存行为添加到应用程序的方式。此配置与[`@Transactional`](data-access.html#tx-annotation-driven-settings)很类似: 349 | 350 | 处理缓存注释的默认建议模式是`proxy`,它允许仅通过代理拦截调用。 同一类中的本地调用不能以这种方式截获。 对于更高级的拦截模式,请考虑结合编译时或加载时编织切换到`aspectj`模式。 351 | 352 | 有关实现`CachingConfigurer`所需的高级自定义(使用Java配置)的更多详细信息,请参阅[javadoc](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/cache/annotation/CachingConfigurer.html)。 353 | 354 | Table 12. Cache 注解设置 355 | 356 | | XML属性| 注解属性 |默认值 |描述 | 357 | | ---- | ---- | ---- | ---- | 358 | | `cache-manager` | N/A (see the [`CachingConfigurer`](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/cache/annotation/CachingConfigurer.html) javadoc) | 要使用的缓存管理器的名称。 使用此缓存管理器(或未设置的`cacheManager`)在后台初始化默认的`CacheResolver`。 要获得更精细的缓存fine-grained 管理,请考虑设置'cache-resolver'属性。 | `cacheManager` | 359 | | `cache-resolver` | N/A (see the [`CachingConfigurer`](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/cache/annotation/CachingConfigurer.html) javadoc) | A `SimpleCacheResolver` using the configured `cacheManager`. | 用于解析后端缓存的CacheResolver的bean名称。 此属性不是必需的,只需要指定为'cache-manager'属性的替代。 | 360 | | `key-generator` | N/A (see the [`CachingConfigurer`](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/cache/annotation/CachingConfigurer.html) javadoc) | `SimpleKeyGenerator` | 要使用的自定义键生成器的名称。 | 361 | | `error-handler` | N/A (see the [`CachingConfigurer`](https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/javadoc-api/org/springframework/cache/annotation/CachingConfigurer.html) javadoc) | `SimpleCacheErrorHandler` | 要使用的自定义缓存错误处理程序的名称。 默认情况下,在缓存相关操作期间抛出的任何异常都会在客户端返回。 | 362 | | `mode` | `mode` | `proxy` | 默认模式(`proxy`)使用Spring的AOP框架处理要注释的注释bean(遵循代理语义,如前所述,仅适用于通过代理进入的方法调用)。 替代模式(`aspectj`)用Spring的AspectJ缓存方面编织受影响的类,修改目标类字节代码以应用于任何类型的方法调用。 AspectJ编织需要在类路径中使用`spring-aspects.jar`以及启用加载时织入(或编译时织入)。 (有关如何设置加载时织入的详细信息,请参阅[Spring configuration](core.html#aop-aj-ltw-spring)。) | 363 | | `proxy-target-class` | `proxyTargetClass` | `false` | 仅适用于代理模式。 控制为使用`@Cacheable`或`@CacheEvict`注解注释的类创建哪种类型的高速缓存代理。 如果`proxy-target-class` 属性设置为`true`,则创建基于类的代理。 如果`proxy-target-class`为`false`或者省略了该属性,则会创建基于标准JDK接口的代理。 (有关不同代理类型的详细检查,请参阅 [代理机制](core.html#aop-proxying) | 364 | | `order` | `order` | Ordered.LOWEST\_PRECEDENCE | 定义应用于使用`@Cacheable`或`@CacheEvict`注解的bean的缓存通知的顺序。 (有关与订购AOP advice相关的规则的更多信息,请参阅[Advice Ordering](core.html#aop-ataspectj-advice-ordering)。)没有指定的排序意味着AOP子系统确定advice的顺序。 | 365 | 366 | ``在 bean 中定义的应用程序上下文中只查找`@Cacheable/@CachePut/@CacheEvict/@Caching`。这意味着,如果你在`WebApplicationContext` 中为`DispatcherServlet`放置``,他只会检查控制器中的bean,而不是你的服务。有关更多信息,请参阅[MVC部分](web.html#mvc-servlet)。 367 | 368 | 方法可见和缓存注解 369 | 370 | 使用代理时,应仅将缓存注解应用于具有公共可见性的方法。如果使用这些注解对受保护的、私有的或包可见的方法进行注解,虽然不会引发任何错误,但注解的方法不会显示已配置的缓存设置。 如果需要在更改字节码本身时对非公共方法进行注解,请考虑使用AspectJ(请参阅本节的其余部分) 371 | 372 | Spring建议您只用`@Cache*`注解来注解具体类(还有具体类的方法),而不是注解接口。当然,您可以将`@Cache*`注解放在接口(或接口方法)上,但这只适用于您在使用基于代理时所期望的效果。Java注解不是从接口继承的这一事实意味着, 如果您使用的是基于类的代理(`proxy-target-class="true"`)或weaving-based aspect(`mode="aspectj"`),则缓存设置无法被代理和编织基础结构,并且对象不会被包装在缓存代理中,这将是绝对糟糕的。 373 | 374 | 在代理模式(默认情况下), 仅截获通过代理进入的外部方法调用。这意味着,实际上,self-invocation在目标对象中调用目标对象的另一种方法时,在运行时不会导致实际的缓存,即使调用的方法标记为`@Cacheable`,考虑使用`aspectj`模式也是这种情况,此外,代理必须完全初始化以提供预期的行为,因此您不应依赖于初始化代码中的此功能,即`@PostConstruct`。 375 | 376 | 377 | 378 | #### [](#cache-annotation-stereotype)8.2.7. 使用自定义的注解 379 | 380 | 自定义的注解和AspectJ 381 | 382 | 此功能仅在基于方法的情况下工作,但可以通过使用AspectJ的额外工作来启用。 383 | 384 | `spring-aspects` 模块定义了标准注解的切面。如果你定义自己的注解,则还需要为这些注解定义一个切面。请检查`AnnotationCacheAspect` 以查看示例: 385 | 386 | 缓存抽象允许您使用自己的注解来识别触发缓存储存或回收的方法。这在模板机制中非常方便,因为它消除了重复缓存注解声明的需要(在指定键或条件时尤其有用),或者在您的代码库中不允许使用外部导入(`org.springframework`)。与[stereotype](core.html#beans-stereotype-annotations)注解的其余部分类似, `@Cacheable`, `@CachePut`、`@CacheEvict`, 和 `@CacheConfig`可以用作[meta-annotations](core.html#beans-meta-annotations)。这是可以注解其他注解的注解(就是元注解)。在下面的示例中,我们使用自己的自定义注释替换常见的`@Cacheable`声明:: 387 | 388 | ```java 389 | @Retention(RetentionPolicy.RUNTIME) 390 | @Target({ElementType.METHOD}) 391 | @Cacheable(cacheNames="books", key="#isbn") 392 | public @interface SlowService { 393 | } 394 | ``` 395 | 396 | 在前面的示例中,我们定义了自己的`SlowService`注释,该注释本身使用`@Cacheable`进行注释。 现在我们可以替换以下代码: 397 | 398 | ```java 399 | @Cacheable(cacheNames="books", key="#isbn") 400 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 401 | ``` 402 | 403 | 以下示例显示了自定义注释,我们可以使用它来替换前面的代码: 404 | 405 | ```java 406 | @SlowService 407 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 408 | ``` 409 | 410 | 尽管`@SlowService`不是Spring注释,但容器会在运行时自动获取其声明并理解其含义。 请注意,[如前所述](#cache-annotation-enable),需要启用注释驱动的行为。 411 | 412 | 413 | 414 | ### [](#cache-jsr-107)8.3. JCache (JSR-107) 注解 415 | 416 | 自Spring框架4.1以来,缓存抽象完全支持JCache标准注释: `@CacheResult`, `@CachePut`, `@CacheRemove`, he `@CacheRemoveAll` 以及 `@CacheDefaults`, `@CacheKey`, 和 `@CacheValue`。这些注解可以正确地使用,而无需将缓存存储迁移到JSR-107。内部实现使用Spring的缓存抽象, 并提供默认的`CacheResolver`和`KeyGenerator`实现,它们符合规范。换言之,如果您已经在使用Spring的缓存抽象,则可以切换到这些标准注解,而无需更改缓存存储(或配置)。 417 | 418 | 419 | 420 | #### [](#cache-jsr-107-summary)8.3.1. 特性总结 421 | 422 | 对于熟悉Spring缓存注解的用户,下表描述了Spring注解和JSR-107对应项之间的主要区别: 423 | 424 | Table 13. Spring vs. JSR-107 缓存注解 425 | 426 | | Spring | JSR-107 | 备注 | 427 | | ---- | ---- | ---- | 428 | | `@Cacheable` |`@CacheResult` | 非常相似。`@CacheResult`可以缓存特定的异常并强制执行该方法,而不管缓存的内容如何。 | 429 | | `@CachePut` | `@CachePut` | 当Spring使用方法调用的结果更新缓存时,JCache要求将其作为使用`@CacheValue`注释的参数传递。 由于这种差异,JCache允许在实际方法调用之前或之后更新缓存。 | 430 | | `@CacheEvict` |`@CacheRemove` | 非常相似。 当方法调用导致异常时,`@CacheRemove`支持条件回收。 | 431 | | `@CacheEvict(allEntries=true)` | `@CacheRemoveAll` | See `@CacheRemove`. | 432 | | `@CacheConfig` | `@CacheDefaults` | 允许您以类似的方式配置相同的概念。 | 433 | 434 | JCache具有与Spring的`CacheResolver`接口相同的`javax.cache.annotation.CacheResolver`,但JCache只支持单个缓存。默认情况下,一个简单的实现是根据注解上声明的名称检索要使用的缓存。 应该注意的是,如果在注解中没有指定缓存名称,则会自动生成默认值,参看`@CacheResult#cacheName()` 。 435 | 436 | `CacheResolver`实例由`CacheResolverFactory`检索。 可以为每个缓存操作自定义工厂,如以下示例所示: 437 | 438 | ```java 439 | @CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) (1) 440 | public Book findBook(ISBN isbn) 441 | ``` 442 | 443 | **1**、为此操作自定义工厂。 444 | 445 | 对于所有引用的类,Spring尝试查找具有给定类型的bean。如果存在多个匹配项,则会创建一个新实例,并且可以使用常规bean生命周期回调(如依赖项注入)。 446 | 447 | 键由`javax.cache.annotation.CacheKeyGenerator`方法生成,其作用与Spring的`KeyGenerator`一样。默认情况下,所有方法参数都将被考虑,除非至少有一个参数是用`@CacheKey`注解。这类似于Spring的[自定义键生成声明](#cache-annotations-cacheable-key)。例如,同样的操作,一个使用Spring的抽象,另一个用JCache:: 448 | 449 | ```java 450 | @Cacheable(cacheNames="books", key="#isbn") 451 | public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) 452 | 453 | @CacheResult(cacheName="books") 454 | public Book findBook(@CacheKey ISBN isbn, boolean checkWarehouse, boolean includeUsed) 455 | ``` 456 | 457 | 您还可以在操作上指定`CacheKeyResolver`,类似于指定`CacheResolverFactory`的方式。 458 | 459 | JCache可以管理由注解的方法引发的异常。这可以防止缓存的更新,但也可以将异常缓存为失败的指示器,而不是再次调用该方法。让我们假设,如果ISBN的结构错误,则抛出`InvalidIsbnNotFoundException`。这是一个永久性的失败,没有book可以用这样的参数检索。下面抛出缓存异常,以便使用相同的无效ISBN进行进一步调用,直接抛出缓存的异常,而不是再次调用该方法。: 460 | 461 | ```java 462 | @CacheResult(cacheName="books", exceptionCacheName="failures" 463 | cachedExceptions = InvalidIsbnNotFoundException.class) 464 | public Book findBook(ISBN isbn) 465 | ``` 466 | 467 | 468 | 469 | #### [](#enabling-jsr-107-support)8.3.2. 启用 JSR-107 支持 470 | 471 | 除了Spring的声明性注解支持之外,不需要做任何具体的工作来启用JSR-107支持。如果`spring-context-support`模块已经在类加载路径中,那么使用`@EnableCaching`或者`cache:annotation-driven`元素都将自动启用JCache支持。 472 | 473 | 根据您的使用情况,选择使用与否由你选择。您甚至可以使用JSR-107 API和其他使用Spring自己的注解来混合使用服务。但是,请注意,如果这些服务影响到相同的缓存,则应使用一致的和相同的键生成实现。 474 | 475 | 476 | 477 | ### [](#cache-declarative-xml)8.4. 声明式基于XML的缓存 478 | 479 | 如果注解不是可选的(不能访问源代码或没有外部码),则可以使用XML进行声明性缓存。因此,您不必对缓存方法进行注解,而是在外部指定目标方法和缓存指令(类似于声明性事务管理[advice](data-access.html#transaction-declarative-first-example))。上一节中的示例可以转换为以下示例: 480 | 481 | ```xml 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | ``` 500 | 501 | 在上面的配置中,`bookService`是可以缓存的。缓存语义被保存在`cache:advice`定义中,指定了方法`findBooks`用于将数据放入缓存,而方法`loadBooks`用于回收数据。这两个定义都可以使用 `books`缓存。 502 | 503 | `aop:config`定义使用AspectJ切点表达式将缓存通知应用于程序中的适当的切点(更多信息参看[Spring面向切面编程](core.html#aop))。在前面的示例中,将考虑`BookService`中的所有方法,并将缓存advice应用于它们。 504 | 505 | 声明性XML缓存支持所有基于注解的模型,因此在两者之间转换应该相当简单。在同一个应用程序中可以进一步使用它们。基于XML的方法不会设计到目标代码,但是编写它非常冗长无聊。在处理具有针对缓存的重载方法的类时,确定正确的方法确实需要额外工作,因为该方法参数不能很好的被辨别。在这些情况下, 在这些情况下,您可以使用AspectJ切入点来挑选目标方法并应用适当的缓存功能。然而,通过XML,更容易应用在包/组/接口范围上的缓存(再次因为AspectJ切点)和创建类似模板的定义(如我们在上面的例子中通过缓存定义目标的`cache:definitions`属性。 506 | 507 | 508 | 509 | ### [](#cache-store-configuration)8.5. 配置缓存的存储 510 | 511 | 缓存抽象集成了多个存储功能,可以开箱即用。为了使用他们,您需要声明一个适当的`CacheManager` (一个控制和管理`Cache`实例的实体,可用于检索这些实例以进行存储)。 512 | 513 | 514 | 515 | #### [](#cache-store-configuration-jdk)8.5.1. 基于JDK`ConcurrentMap` 缓存 516 | 517 | 基于JDK的`Cache`实现位于`org.springframework.cache.concurrent`包下。它允许您使用`ConcurrentHashMap`作为支持`Cache`存储。 以下示例显示如何配置两个缓存: 518 | 519 | ```xml 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | ``` 530 | 531 | 上面的代码段使用`SimpleCacheManager`为名为`default`和`books`的两个嵌套的`ConcurrentMapCache`实例创建`CacheManager`。请注意,这些名称是为每个缓存直接配置的。 532 | 533 | 由于缓存是由应用程序创建的,因此它被绑定到其生命周期,使其适合于基本用例、测试或简单应用程序。高速缓存的规模很大,而且速度非常快,但它不提供任何管理或持久性功能,也没有任何回收的程序。 534 | 535 | 536 | 537 | #### [](#cache-store-configuration-ehcache)8.5.2. 基于Ehcache的缓存 538 | 539 | Ehcache 3.x完全与JSR-107兼容, 不需要专门的支持。 540 | 541 | Ehcache 2.x实现在`org.springframework.cache.ehcache`包中。同样地,要使用它,需要简单地声明适当的 `CacheManager`。以下示例显示了如何执行此操作: 542 | 543 | ```xml 544 | 546 | 547 | 548 | 550 | ``` 551 | 552 | 此设置引导Spring IoC内部的ehcache库(通过`ehcache` bean),然后将其连接到专用的`CacheManager`实现中。 请注意,从`ehcache.xml`读取整个特定于`ehcache`的配置。 553 | 554 | 555 | 556 | #### [](#cache-store-configuration-caffeine)8.5.3. Caffeine Cache 557 | 558 | Caffeine是Java 8重写了Guava的缓存,他的实现在`org.springframework.cache.caffeine`包中,并且提供了访问Caffeine特性的方法。 559 | 560 | 以下示例配置一个按需创建缓存的`CacheManager`: 561 | 562 | ```xml 563 | 565 | ``` 566 | 567 | 还可以提供缓存以显式使用,在这种情况下,只有manager才能提供这些内容。以下示例显示了如何执行此操作: 568 | 569 | ```xml 570 | 571 | 572 | 573 | default 574 | books 575 | 576 | 577 | 578 | ``` 579 | 580 | Caffeine `CacheManager`也支持自定义的`Caffeine` 和 `CacheLoader`. 查阅 [Caffeine 文档](https://github.com/ben-manes/caffeine/wiki)了解更多信息。 581 | 582 | 583 | 584 | #### [](#cache-store-configuration-gemfire)8.5.4. 基于GemFire的缓存 585 | 586 | GemFire是一个面向内存/磁盘存储的全局的备份数据库,它具有可伸缩的、可持续的、可扩展的、具有内置模式订阅通知功能等等特性。全局复制的数据库。并提供全功能的边缘缓存。 有关如何将GemFire用作`CacheManager`(以及更多)的更多信息,请参阅[Spring Data GemFire参考文档](https://docs.spring.io/spring-gemfire/docs/current/reference/html/)。 587 | 588 | 589 | 590 | #### [](#cache-store-configuration-jsr107)8.5.5. JSR-107 缓存 591 | 592 | JSR-107兼容的缓存也可用于Spring的缓存抽象。JCache实现在`org.springframework.cache.jcache` 包中. 593 | 594 | 同样,要使用它,需要简单地声明适当的`CacheManager`。简单示例如下: 595 | 596 | ```xml 597 | 600 | 601 | 602 | 603 | ``` 604 | 605 | 606 | 607 | #### [](#cache-store-configuration-noop)8.5.6.处理没有后端的缓存 608 | 609 | 有时在切换环境或进行测试时, 可能会只声明缓存, 而没有配置实际的后端缓存。由于这是一个无效的配置, 在运行时将引发异常, 因为缓存基础结构无法找到合适的存储。在这样的情况下, 与其删除缓存声明(这可能会很繁琐), 你不如声明一个简单的,不执行缓存的虚拟缓存, 即强制每次执行缓存的方法。以下示例显示了如何执行此操作: 610 | 611 | ```xml 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | ``` 622 | 623 | `CompositeCacheManager`使用了多个`CacheManager`实例。此外,通过`fallbackToNoOpCache` 标志,添加了一个没有op的缓存,为所有的定义没有被配置的缓存管理器处理。 也就是说,在`jdkCache`或`gemfireCache`(上面配置)中找不到的每个缓存定义都将由无op缓存来处理,并且不会存储目标方法的信息而方法每次都会被执行(就是多配置成可执行的无缓存操作)。 624 | 625 | 626 | 627 | ### [](#cache-plug)8.6. 各种各样的后端缓存插件 628 | 629 | 显然,有大量的缓存产品可以用作后端存储。要将它们集成,需要提供`CacheManager`和`Cache`实现,因为不幸的是没有可用的标准,我们可以改用它。这听起来可能比使用它更难,因为在实践中,类往往是简单的[adapters](https://en.wikipedia.org/wiki/Adapter_pattern),它将缓存抽象框架映射到存储API的顶部,就像`ehcache`类可以显示的那样。 大多数`CacheManager`类可以使用`org.springframework.cache.support`包中的类,如`AbstractCacheManager`,它负责处理样板代码,只留下实际的映射即可结束工作。我们希望,提供与Spring集成的库能够及时填补这一小的配置缺口。 630 | 631 | 632 | 633 | ### [](#cache-specific-config)8.7. 我可以如何设置TTL/TTI/Eviction policy/XXX特性? 634 | 635 | 直接通过缓存提供程序。 缓存抽象是抽象,而不是缓存实现。 您正在使用的解决方案可能支持不同的数据策略和不同的拓扑结构,而其他解决方案不会这样做(例如,JDK `ConcurrentHashMap` - 暴露在缓存抽象中将是无用的,因为没有后端支持)。应该通过后端缓存(配置时)或通过其本机API直接控制此类功能。 -------------------------------------------------------------------------------- /pages/integration/cci.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#cci)5\. JCA CCI 4 | ------------------- 5 | 6 | JJava EE提供了一个规范, 用于对企业信息系统(EIS)标准化的访问: JCA(Java EE Connector Architecture:Java EE连接框架)。该规范分为两个不同的部分: 7 | 8 | * 连接器提供程序必须实现的SPI(服务提供程序接口)。这些接口构成了可部署在Java EE应用程序服务器上的资源适配器。在这种情况下, 服务器管理连接池、事务和安全 (托管模式)。应用程序服务器还负责管理在客户端应用程序之外进行的配置。连接器也可以在没有应用服务器的情况下使用。在这种情况下, 应用程序必须直接配置它(非托管模式)。 9 | 10 | * CCI (通用客户端接口) 供应用来使用来连接和通讯和EIS。还提供了用于本地事务划分的API。 11 | 12 | 13 | Spring CCI支持的目的是使用Spring Framework的一般资源和事务管理工具提供以典型Spring方式访问CCI连接器的类。 14 | 15 | 连接器的客户端并不是总要使用CCI。 某些连接器公开自己的API, 只提供JCA资源适配器来使用Java EE Container的系统协定(连接池,全局事务和安全性)。 Spring不为这种特定于连接器的API提供特殊支持。 16 | 17 | 18 | 19 | ### [](#cci-config)5.1. 配置 CCI 20 | 21 | 本节介绍如何配置通用客户端接口(CCI)。 它包括以下主题: 22 | 23 | * [连接器配置](#cci-config-connector) 24 | 25 | * [Spring中的`ConnectionFactory`配置](#cci-config-connectionfactory) 26 | 27 | * [配置 CCI 连接](#cci-config-cci-connections) 28 | 29 | * [使用单独的CCI连接](#cci-config-single-connection) 30 | 31 | 32 | 33 | 34 | #### [](#cci-config-connector)5.1.1. 连接器配置 35 | 36 | 使用JCA CCI的基本资源是`ConnectionFactory` 接口。 您使用的连接器必须提供此接口的实现。 37 | 38 | 若要使用连接器,可以将其部署到应用程序服务器上,并从服务器的JNDI环境(托管模式)中获取`ConnectionFactory`。连接器必须打包为RAR文件(资源适配器存档), 并包含 `ra.xml`文件来描述其部署特性。资源的实际名称在部署时指定。要在Spring中访问它,只需使用Spring的`JndiObjectFactoryBean`/``以其JNDI名称获取工厂。 39 | 40 | 使用连接器的另一种方法是将其嵌入到应用程序(非托管模式)中,而不是使用应用程序服务器部署和配置它。Spring提供了通过提供的`FactoryBean`(`LocalConnectionFactoryBean`)将连接器配置为bean的可能性。以这种方式,您只需要类路径中的连接器库(无RAR文件,且不需要`ra.xml`描述符)。如果需要,必须从连接器的RAR文件中提取该库。 41 | 42 | 一旦你获得了你的`ConnectionFactory`实例, 你就可以将它注入你的组件中。这些组件可以是针对普通的 API, 或者利用Spring的支持类来访问CCI(例如`CciTemplate`)。 43 | 44 | 在非托管模式下使用连接器时, 不能使用全局事务, 因为在当前线程的当前全局事务中从未登记或摘出资源。该资源根本不知道可能正在运行的任何全局Java EE事务。 45 | 46 | 47 | 48 | #### [](#cci-config-connectionfactory)5.1.2. 在Spring中配置`ConnectionFactory` 49 | 50 | 为了与EIS建立连接,如果您处于托管模式,则需要从应用服务器获取`ConnectionFactory`。如果你处于非托管模式,则可以直接从Spring中获取。 51 | 52 | 在托管模式中,你通过JNDI来访问`ConnectionFactory`,它的属性将在应用服务器中配置。以下示例说明了如何执行此操作: 53 | 54 | ``` 55 | 56 | ``` 57 | 58 | 在非托管模式下,您必须将要在Spring配置中使用的`ConnectionFactory`配置为JavaBean。`LocalConnectionFactoryBean`类提供这种设置样式,传递连接器的`ManagedConnectionFactory`实现,公开应用程序级的CCI `ConnectionFactory`。以下示例说明了如何执行此操作: 59 | 60 | ```xml 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | ``` 71 | 72 | 你不能直接实例化特定的`ConnectionFactory`,您需要为连接器的`ManagedConnectionFactory` 接口执行相应的实现。此接口是JCA SPI规范的一部分。 73 | 74 | 75 | 76 | #### [](#cci-config-cci-connections)5.1.3. 配置CCI连接 77 | 78 | JCA CCI 允许开发者使用连接器的`ConnectionSpec`实现来配置到EIS的连接。为了配置其属性,您需要用专用的适配器(`ConnectionSpecConnectionFactoryAdapter`)包装目标连接工厂。因此,,专用的`ConnectionSpec`可以配置为`connectionSpec`属性(作为一个内部bean)。 79 | 80 | 此属性不是必需的,因为CCI `ConnectionFactory`接口定义了两种不同的方法来获取一个与之关联的CCI连接。某些`ConnectionSpec`属性通常可以在应用程序服务器(在托管模式下)或相应的本地`ManagedConnectionFactory`实现中进行配置。以下清单显示了`ConnectionFactory`接口定义的相关部分: 81 | 82 | ```java 83 | public interface ConnectionFactory implements Serializable, Referenceable { 84 | ... 85 | Connection getConnection() throws ResourceException; 86 | Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException; 87 | ... 88 | } 89 | ``` 90 | 91 | Spring提供了一个`ConnectionSpecConnectionFactoryAdapter`,它允许指定一个`ConnectionSpec`实例来用于给定工厂的所有操作。如果指定了适配器的`connectionSpec`属性, 那么适配器将使用带有`ConnectionSpec`参数的`getConnection`变量,否则该变量不带参数。以下示例显示如何配置`ConnectionSpecConnectionFactoryAdapter`: 92 | 93 | ```xml 94 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | ``` 116 | 117 | 118 | 119 | #### [](#cci-config-single-connection)5.1.4. 使用单独的CCI连接 120 | 121 | 如果您想使用单独的CCI连接,Spring提供了一个进一步的`ConnectionFactory`适配器来管理这个。`SingleConnectionFactory`适配器类将会延迟打开一个连接,并在应用程序关闭时关闭此bean。此类将公开相应行为的特殊`Connection`代理,所有共享相同的底层物理连接。以下示例显示如何使用`SingleConnectionFactory`适配器类: 122 | 123 | ```xml 124 | 126 | 127 | 128 | 129 | 130 | 131 | 133 | 134 | 135 | 136 | 138 | 139 | 140 | ``` 141 | 142 | 无法直接使用`ConnectionSpec`配置此`ConnectionFactory`适配器。如果您需要一个特定`ConnectionSpec`的单独连接,请使用`SingleConnectionFactory`对话的中介`ConnectionSpecConnectionFactoryAdapter`。 143 | 144 | 145 | 146 | ### [](#cci-using)5.2. 使用Spring的CCI访问支持 147 | 148 | 本节介绍如何使用Spring对CCI的支持来实现各种目的。 它包括以下主题: 149 | 150 | * [记录转换](#cci-record-creator) 151 | 152 | * [使用 `CciTemplate`](#cci-using-template) 153 | 154 | * [DAO支持](#cci-using-dao) 155 | 156 | * [自动输出记录生成](#automatic-output-generation) 157 | 158 | * [`CciTemplate` `Interaction` 总结](#template-summary) 159 | 160 | * [直接使用CCI连接和交互](#cci-straight) 161 | 162 | * [`CciTemplate`用法示例](#cci-template-example) 163 | 164 | 165 | 166 | 167 | #### [](#cci-record-creator)5.2.1. 记录转换 168 | 169 | JCA CCI支持的目标之一是提供方便的设置来操作CCI记录。开发人员可以指定创建记录和从记录中提取数据的策略,在使用Spring的`CciTemplate`时。如果您不想在应用程序中直接处理记录,以下接口将配置策略以使用输入和输出记录。 170 | 171 | 为了创建输入记录, 开发人员可以使用 `RecordCreator`接口的专用实现。以下清单显示了`RecordCreator` 接口定义: 172 | 173 | ```java 174 | public interface RecordCreator { 175 | 176 | Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException; 177 | 178 | } 179 | ``` 180 | 181 | `createRecord(..)`方法接收一个`RecordFactory`实例作为参数,它对应于`ConnectionFactory`所使用的`RecordFactory` 。此引用可用于创建 `IndexedRecord`或`MappedRecord`实例。下面的示例演示如何使用`RecordCreator`接口和索引/映射记录: 182 | 183 | ```java 184 | public class MyRecordCreator implements RecordCreator { 185 | 186 | public Record createRecord(RecordFactory recordFactory) throws ResourceException { 187 | IndexedRecord input = recordFactory.createIndexedRecord("input"); 188 | input.add(new Integer(id)); 189 | return input; 190 | } 191 | 192 | } 193 | ``` 194 | 195 | 输出记录可用于从EIS接收数据。因此,可以将`RecordExtractor`接口的特定实现传递到Spring的`CciTemplate`, 以便从输出记录中提取数据。以下清单显示了`RecordExtractor`接口定义: 196 | 197 | ```java 198 | public interface RecordExtractor { 199 | 200 | Object extractData(Record record) throws ResourceException, SQLException, DataAccessException; 201 | 202 | } 203 | ``` 204 | 205 | 以下示例显示如何使用:`RecordExtractor`接口: 206 | 207 | ```java 208 | public class MyRecordExtractor implements RecordExtractor { 209 | 210 | public Object extractData(Record record) throws ResourceException { 211 | CommAreaRecord commAreaRecord = (CommAreaRecord) record; 212 | String str = new String(commAreaRecord.toByteArray()); 213 | String field1 = string.substring(0,6); 214 | String field2 = string.substring(6,1); 215 | return new OutputObject(Long.parseLong(field1), field2); 216 | } 217 | 218 | } 219 | ``` 220 | 221 | 222 | 223 | #### [](#cci-using-template)5.2.2. 使用 `CciTemplate` 224 | 225 | `CciTemplate`是核心的CCI支持包(`org.springframework.jca.cci.core`)的中心类。它简化了对管理的使用,因为它处理资源的创建和释放。这有助于避免常见的错误,如忘记总是需要的关闭连接。它关心连接和交互对象的生命周期,让应用程序代码专注于从应用程序数据生成输入记录,并从输出记录中提取应用程序数据。 226 | 227 | JCA的CCI规范定义了两种不同的方法来调用EIS上的操作。CCI的 `Interaction`接口提供了两个execute方法: 228 | 229 | ```java 230 | public interface javax.resource.cci.Interaction { 231 | 232 | ... 233 | 234 | boolean execute(InteractionSpec spec, Record input, Record output) throws ResourceException; 235 | 236 | Record execute(InteractionSpec spec, Record input) throws ResourceException; 237 | 238 | ... 239 | 240 | } 241 | ``` 242 | 243 | 根据调用的模板方法,`CciTemplate`知道在交互时调用哪个执行方法。 无论如何,正确初始化的 `InteractionSpec`实例是必需的。 244 | 245 | 您可以通过两种方式使用`CciTemplate.execute(..)` : 246 | 247 | * 直接记录参数。在这种情况下,您只需要在CCI输入中传递记录, 返回的对象就是相应的CCI输出记录。 248 | 249 | * 与应用程序对象一起使用记录映射。在这种情况下, 您需要提供相应的`RecordCreator` 和 `RecordExtractor`实例。 250 | 251 | 252 | 采用第一种方法,将使以下模板方法,这些方法直接对应于`Interaction`接口上的那些: 253 | 254 | ```java 255 | public class CciTemplate implements CciOperations { 256 | 257 | public Record execute(InteractionSpec spec, Record inputRecord) 258 | throws DataAccessException { ... } 259 | 260 | public void execute(InteractionSpec spec, Record inputRecord, Record outputRecord) 261 | throws DataAccessException { ... } 262 | 263 | } 264 | ``` 265 | 266 | 对于第二种方式,你需要指定记录创建和记录获取策略作为参数。使用的接口是在[上一节中描述的记录转换](#cci-record-creator)。相应的`CciTemplate`方法如下: 267 | 268 | ```java 269 | public class CciTemplate implements CciOperations { 270 | 271 | public Record execute(InteractionSpec spec, 272 | RecordCreator inputCreator) throws DataAccessException { 273 | // ... 274 | } 275 | 276 | public Object execute(InteractionSpec spec, Record inputRecord, 277 | RecordExtractor outputExtractor) throws DataAccessException { 278 | // ... 279 | } 280 | 281 | public Object execute(InteractionSpec spec, RecordCreator creator, 282 | RecordExtractor extractor) throws DataAccessException { 283 | // ... 284 | } 285 | 286 | } 287 | ``` 288 | 289 | 除非在模板上设置了`outputRecordCreator`属性(请参见下一节), 负责每一个方法都将调用带有两个参数的CCI `Interaction`方法`InteractionSpec` 和input `Record`,接收输出记录作为返回值。 290 | 291 | `CciTemplate`还提供了在`RecordCreator`实现之外创建`IndexRecord`和`MappedRecord`的方法,是通过其`createIndexRecord(..)`和`createMappedRecord(..)`方法。这可以在DAO实现中使用,以创建要传递到相应`CciTemplate.execute(..)`方法。以下清单显示了`CciTemplate`接口定义: 292 | 293 | ```java 294 | public class CciTemplate implements CciOperations { 295 | 296 | public IndexedRecord createIndexedRecord(String name) throws DataAccessException { ... } 297 | 298 | public MappedRecord createMappedRecord(String name) throws DataAccessException { ... } 299 | 300 | } 301 | ``` 302 | 303 | 304 | 305 | #### [](#cci-using-dao)5.2.3. DAO支持 306 | 307 | Spring的CCI支持为DAO提供了一个抽象类,支持支持`ConnectionFactory`或`CciTemplate`实例的注入。该类的名称为`CciDaoSupport`:它提供了简单的`setConnectionFactory`和`setCciTemplate`方法。在内部, 此类将为传入的`ConnectionFactory`创建一个`CciTemplate`实例,并将其公开给子类中的具体数据访问实现。以下示例显示如何使用`CciDaoSupport`: 308 | 309 | ```java 310 | public abstract class CciDaoSupport { 311 | 312 | public void setConnectionFactory(ConnectionFactory connectionFactory) { 313 | // ... 314 | } 315 | 316 | public ConnectionFactory getConnectionFactory() { 317 | // ... 318 | } 319 | 320 | public void setCciTemplate(CciTemplate cciTemplate) { 321 | // ... 322 | } 323 | 324 | public CciTemplate getCciTemplate() { 325 | // ... 326 | } 327 | 328 | } 329 | ``` 330 | 331 | 332 | 333 | #### [](#automatic-output-generation)5.2.4. 自动输出记录生成 334 | 335 | 如果连接器只支持使用输入和输出记录的`Interaction.execute(..)`方法作为参数(即它需要传递所需的输出记录,而不是返回适当的输出记录),您可以设置`CciTemplate` 的`outputRecordCreator`属性,以便在收到响应时自动生成由JCA连接器填充的输出记录。此记录将被返回到模板的调用方。 336 | 337 | 此属性仅包含用于此目的的[`RecordCreator`](#cci-record-creator)接口的实现, 您必须直接在`CciTemplate`上指定`outputRecordCreator`属性。 以下示例显示了如何执行此操作: 338 | 339 | ``` 340 | cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator()); 341 | ``` 342 | 343 | 或者(我们建议使用此方法),在Spring配置中,如果将`CciTemplate` 配置为专用bean实例,则可以按以下方式定义bean: 344 | 345 | ```xml 346 | 347 | 348 | 349 | 350 | 351 | 352 | ``` 353 | 354 | 由于`CciTemplate`类是线程安全的,因此通常将其配置为共享实例。 355 | 356 | 357 | 358 | #### [](#template-summary)5.2.5. `CciTemplate` `Interaction` 总结 359 | 360 | 下表总结了`CciTemplate` 类的机制以及在CCI `Interaction`接口上调用的相应方法: 361 | 362 | Table 9. 操作execute方法的使用 363 | 364 | | `CciTemplate` 调用方法签名 | `CciTemplate` `outputRecordCreator` 属性 | `execute` 方法调用对CCI的操作 | 365 | | ---- | ---- | ---- | 366 | | `Record execute(InteractionSpec, Record)` | Not set | `Record execute(InteractionSpec, Record)` | 367 | | `Record execute(InteractionSpec, Record)` | Set | `boolean execute(InteractionSpec, Record, Record)` | 368 | | void execute(InteractionSpec, Record, Record) | Not set | void execute(InteractionSpec, Record, Record) | 369 | | `void execute(InteractionSpec, Record, Record)` | Set | `void execute(InteractionSpec, Record, Record)` | 370 | | `Record execute(InteractionSpec, RecordCreator)` | Not set | `Record execute(InteractionSpec, Record)` | 371 | | `Record execute(InteractionSpec, RecordCreator)` | Set | `void execute(InteractionSpec, Record, Record)` | 372 | | `Record execute(InteractionSpec, Record, RecordExtractor)` | Not set | `Record execute(InteractionSpec, Record)` | 373 | | `Record execute(InteractionSpec, Record, RecordExtractor)` | Set | `void execute(InteractionSpec, Record, Record)` | 374 | | `Record execute(InteractionSpec, RecordCreator, RecordExtractor)` | Not set | `Record execute(InteractionSpec, Record)` | 375 | | `Record execute(InteractionSpec, RecordCreator, RecordExtractor)` | Set | `void execute(InteractionSpec, Record, Record)` | 376 | 377 | 378 | 379 | 380 | #### [](#cci-straight)5.2.6. 直接使用CCI连接和交互 381 | 382 | `CciTemplate` 也提供了与`JdbcTemplate` 和 `JmsTemplate`相同的方式直接使用CCI连接和交互。 例如,当您想要在CCI连接或交互上执行多个操作时,这非常有用。 383 | 384 | `ConnectionCallback`接口提供了CCI `Connection`作为参数,用于执行自定义的操作,加到CCI `ConnectionFactory`的`Connection`创建。。 后者可用于获取关联的`RecordFactory`实例并创建索引/映射记录,以下清单显示了`ConnectionCallback`接口定义: 385 | 386 | ```java 387 | public interface ConnectionCallback { 388 | 389 | Object doInConnection(Connection connection, ConnectionFactory connectionFactory) 390 | throws ResourceException, SQLException, DataAccessException; 391 | 392 | } 393 | ``` 394 | 395 | 接口`InteractionCallback`提供了CCI `Interaction`,是为了对其执行自定义操作,添加到相应的`ConnectionFactory`。以下清单显示了`InteractionCallback`接口定义:: 396 | 397 | ```java 398 | public interface InteractionCallback { 399 | 400 | Object doInInteraction(Interaction interaction, ConnectionFactory connectionFactory) 401 | throws ResourceException, SQLException, DataAccessException; 402 | 403 | } 404 | ``` 405 | 406 | `InteractionSpec`对象可以在多个模板调用之间共享,或者在每个回调方法中新创建。这完全由DAO实现来完成。 407 | 408 | 409 | 410 | #### [](#cci-template-example)5.2.7. `CciTemplate` 用法示例 411 | 412 | 在本节中,`CciTemplate`的使用将被显示为访问到具有ECI模式的CICS,使用IBM CICS ECI连接器。 413 | 414 | 首先, 必须对CCI `InteractionSpec`进行一些初始化,以指定要访问的CICS程序以及如何与之交互: 415 | 416 | ``` 417 | ECIInteractionSpec interactionSpec = new ECIInteractionSpec(); 418 | interactionSpec.setFunctionName("MYPROG"); 419 | interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE); 420 | ``` 421 | 422 | 然后,程序可以通过Spring模板使用CCI, 指定自定义对象和记录之间的映射。如下: 423 | 424 | ```java 425 | public class MyDaoImpl extends CciDaoSupport implements MyDao { 426 | 427 | public OutputObject getData(InputObject input) { 428 | ECIInteractionSpec interactionSpec = ...; 429 | 430 | OutputObject output = (ObjectOutput) getCciTemplate().execute(interactionSpec, 431 | new RecordCreator() { 432 | public Record createRecord(RecordFactory recordFactory) throws ResourceException { 433 | return new CommAreaRecord(input.toString().getBytes()); 434 | } 435 | }, 436 | new RecordExtractor() { 437 | public Object extractData(Record record) throws ResourceException { 438 | CommAreaRecord commAreaRecord = (CommAreaRecord)record; 439 | String str = new String(commAreaRecord.toByteArray()); 440 | String field1 = string.substring(0,6); 441 | String field2 = string.substring(6,1); 442 | return new OutputObject(Long.parseLong(field1), field2); 443 | } 444 | }); 445 | 446 | return output; 447 | } 448 | } 449 | ``` 450 | 451 | 如前所述,您可以使用回调直接处理CCI连接或交互。 以下示例显示了如何执行此操作: 452 | 453 | ```java 454 | public class MyDaoImpl extends CciDaoSupport implements MyDao { 455 | 456 | public OutputObject getData(InputObject input) { 457 | ObjectOutput output = (ObjectOutput) getCciTemplate().execute( 458 | new ConnectionCallback() { 459 | public Object doInConnection(Connection connection, 460 | ConnectionFactory factory) throws ResourceException { 461 | 462 | // do something... 463 | 464 | } 465 | }); 466 | } 467 | return output; 468 | } 469 | 470 | } 471 | ``` 472 | 473 | 使用`ConnectionCallback`时,所使用的`Connection`由`CciTemplate`管理和关闭,但回调实现必须管理在连接上创建的任何交互。 474 | 475 | 对于更具体的回调,您可以实现`InteractionCallback`。 如果这样做,传入的`Interaction` 将由`CciTemplate`管理和关闭。 以下示例显示了如何执行此操作: 476 | 477 | ```java 478 | public class MyDaoImpl extends CciDaoSupport implements MyDao { 479 | 480 | public String getData(String input) { 481 | ECIInteractionSpec interactionSpec = ...; 482 | String output = (String) getCciTemplate().execute(interactionSpec, 483 | new InteractionCallback() { 484 | public Object doInInteraction(Interaction interaction, 485 | ConnectionFactory factory) throws ResourceException { 486 | Record input = new CommAreaRecord(inputString.getBytes()); 487 | Record output = new CommAreaRecord(); 488 | interaction.execute(holder.getInteractionSpec(), input, output); 489 | return new String(output.toByteArray()); 490 | } 491 | }); 492 | return output; 493 | } 494 | 495 | } 496 | ``` 497 | 498 | 对于前面的示例,所涉及的Spring bean的相应配置可能类似于非托管模式中的以下示例:: 499 | 500 | ```xml 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | ``` 516 | 517 | 在托管模式下(即在Java EE环境中),配置可能类似于以下示例: 518 | 519 | ```xml 520 | 521 | 522 | 523 | 524 | 525 | ``` 526 | 527 | 528 | 529 | ### [](#cci-object)5.3. 将CCI访问建模为操作对象 530 | 531 | `org.springframework.jca.cci.object`包中包含的支持类允许你以另一种风格访问EIS:通过可重用的操作对象,类似于Spring的JDBC操作对象(参见[数据访问章节中的JDBC](data-access.html#jdbc))。它通常都封装了CCI的API,将应用级的输入对象传入到操作对象,从而它能创建输入record然后转换接收到的record数据到一个应用级输出对象并返回它。 532 | 533 | 这种方法内在地基于`CciTemplate`类和`RecordCreator`/`RecordExtractor`接口,重用了Spring核心CCI支持的机制。 534 | 535 | 536 | 537 | #### [](#cci-object-mapping-record)5.3.1. 使用 `MappingRecordOperation` 538 | 539 | `MappingRecordOperation`本质上与`CciTemplate`做的事情是一样的,但是它表达了一个明确的、预配置(pre-configured)的操作作为对象。它提供了两个模板方法来指明如何将一个输入对象转换为输入记录,以及如何将一个输出记录转换为输出对象(记录映射)。: 540 | 541 | * `createInputRecord(..)`: 指定了如何将一个输入对象转换为输入`Record`。 542 | 543 | * `extractOutputData(..)`:指定了如何从输出`Record`中提取输出对象。 544 | 545 | 546 | 以下清单显示了这些方法的签名: 547 | 548 | ```java 549 | public abstract class MappingRecordOperation extends EisOperation { 550 | 551 | ... 552 | 553 | protected abstract Record createInputRecord(RecordFactory recordFactory, 554 | Object inputObject) throws ResourceException, DataAccessException { 555 | // ... 556 | } 557 | 558 | protected abstract Object extractOutputData(Record outputRecord) 559 | throws ResourceException, SQLException, DataAccessException { 560 | // ... 561 | } 562 | 563 | ... 564 | 565 | } 566 | ``` 567 | 568 | 此后,为了执行一个EIS操作,你需要使用一个单独的`execute`方法,传递一个应用级(application-level)输入对象,并接收一个应用级输出对象作为结果。如下所示:: 569 | 570 | ```java 571 | public abstract class MappingRecordOperation extends EisOperation { 572 | 573 | ... 574 | 575 | public Object execute(Object inputObject) throws DataAccessException { 576 | } 577 | 578 | ... 579 | } 580 | ``` 581 | 582 | 与`CciTemplate` 类相反,此`execute(..)`方法没有`InteractionSpec`作为参数。 相反,`InteractionSpec` 对于操作是全局的。 必须使用以下构造函数来实例化具有特定`InteractionSpec`的操作对象。 以下示例显示了如何执行此操作: 583 | 584 | ```java 585 | InteractionSpec spec = ...; 586 | MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec); 587 | ... 588 | ``` 589 | 590 | 591 | 592 | #### [](#cci-object-mapping-comm-area)5.3.2. 使用 `MappingCommAreaOperation` 593 | 594 | 一些连接器使用了基于COMMAREA的记录,该记录包含了发送给EIS的参数和返回数据的字节数组。Spring提供了一个专门的操作类用于直接操作COMMAREA而不是操作记录。 `MappingCommAreaOperation`类扩展了`MappingRecordOperation`类以提供这种专门的COMMAREA支持。它隐含地使用了`CommAreaRecord`类作为输入和输出record类型,并提供了两个新的方法来转换输入对象到输入COMMAREA,以及转换输出COMMAREA到输出对象。以下清单显示了相关的方法签名:: 595 | 596 | ```java 597 | public abstract class MappingCommAreaOperation extends MappingRecordOperation { 598 | 599 | ... 600 | 601 | protected abstract byte[] objectToBytes(Object inObject) 602 | throws IOException, DataAccessException; 603 | 604 | protected abstract Object bytesToObject(byte[] bytes) 605 | throws IOException, DataAccessException; 606 | 607 | ... 608 | 609 | } 610 | ``` 611 | 612 | 613 | 614 | #### [](#cci-automatic-record-gen)5.3.3. 自动的输出记录生成 615 | 616 | 由于每个`MappingRecordOperation`子类的内部都是基于`CciTemplate`的,所以用CciTemplate以相同的方式自动生成输出record都是有效的。每个操作对象提供一个相应的`setOutputRecordCreator(..)`方法,有关详细信息,请参阅[自动输出记录生成](#automatic-output-generation)。 617 | 618 | 619 | 620 | #### [](#cci-object-summary)5.3.4. 总结 621 | 622 | 操作对象方法使用了跟`CciTemplate`相同的方式来使用记录。 623 | 624 | Table 10. Interaction的execute方法的使用 625 | 626 | | `MappingRecordOperation` 方法签名 | `MappingRecordOperation` `outputRecordCreator` 属性 | 在CCI Interaction上调用的 `execute` 方法 | 627 | | ---- | ---- | ---- | 628 | | `Object execute(Object)` | Not set | `Record execute(InteractionSpec, Record)` | 629 | | `Object execute(Object)` | Set | `boolean execute(InteractionSpec, Record, Record)` | 630 | 631 | 632 | 633 | 634 | #### [](#cci-objects-mappring-record-example)5.3.5. `MappingRecordOperation` 使用例子 635 | 636 | 在本节中,我们将展示如何使用`MappingRecordOperation` 访问具有Blackbox CCI连接器的数据库。 637 | 638 | 此连接器的原始版本由Java EE SDK(版本1.3)提供,可从Oracle获得。 639 | 640 | 首先,必须对CCI `InteractionSpec`进行一些初始化,在下面的示例中,我们直接定义将请求的参数转换为CCI记录的方式以及将CCI结果记录转换为`Person`类的实例的方法:: 641 | 642 | ```java 643 | public class PersonMappingOperation extends MappingRecordOperation { 644 | 645 | public PersonMappingOperation(ConnectionFactory connectionFactory) { 646 | setConnectionFactory(connectionFactory); 647 | CciInteractionSpec interactionSpec = new CciConnectionSpec(); 648 | interactionSpec.setSql("select * from person where person_id=?"); 649 | setInteractionSpec(interactionSpec); 650 | } 651 | 652 | protected Record createInputRecord(RecordFactory recordFactory, 653 | Object inputObject) throws ResourceException { 654 | Integer id = (Integer) inputObject; 655 | IndexedRecord input = recordFactory.createIndexedRecord("input"); 656 | input.add(new Integer(id)); 657 | return input; 658 | } 659 | 660 | protected Object extractOutputData(Record outputRecord) 661 | throws ResourceException, SQLException { 662 | ResultSet rs = (ResultSet) outputRecord; 663 | Person person = null; 664 | if (rs.next()) { 665 | Person person = new Person(); 666 | person.setId(rs.getInt("person_id")); 667 | person.setLastName(rs.getString("person_last_name")); 668 | person.setFirstName(rs.getString("person_first_name")); 669 | } 670 | return person; 671 | } 672 | } 673 | ``` 674 | 675 | 然后应用程序会以person标识符作为参数来得到操作对象。注意,操作对象可以被设为共享实例,因为它是线程安全的。 以下以person标识符作为参数执行操作对象: 676 | 677 | ```java 678 | public class MyDaoImpl extends CciDaoSupport implements MyDao { 679 | 680 | public Person getPerson(int id) { 681 | PersonMappingOperation query = new PersonMappingOperation(getConnectionFactory()); 682 | Person person = (Person) query.execute(new Integer(id)); 683 | return person; 684 | } 685 | } 686 | ``` 687 | 688 | 在非托管模式下,Spring bean的相应配置如下: 689 | 690 | ```xml 691 | 693 | 694 | 695 | 696 | 697 | 699 | 700 | 701 | 702 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | ``` 717 | 718 | 在托管模式下(即在Java EE环境中),配置可能如下所示: 719 | 720 | ```xml 721 | 722 | 723 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | ``` 738 | 739 | 740 | 741 | #### [](#cci-objects-mapping-comm-area-example)5.3.6. `MappingCommAreaOperation` 的使用例子 742 | 743 | 在本节中,我们将展示如何使用`MappingCommAreaOperation`来使用IBM CICS ECI连接器访问具有ECI模式的CICS。 744 | 745 | 首先,我们需要初始化CCI `InteractionSpec`以指定要访问的CICS程序以及如何与之交互,如以下示例所示:: 746 | 747 | ```java 748 | public abstract class EciMappingOperation extends MappingCommAreaOperation { 749 | 750 | public EciMappingOperation(ConnectionFactory connectionFactory, String programName) { 751 | setConnectionFactory(connectionFactory); 752 | ECIInteractionSpec interactionSpec = new ECIInteractionSpec(), 753 | interactionSpec.setFunctionName(programName); 754 | interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE); 755 | interactionSpec.setCommareaLength(30); 756 | setInteractionSpec(interactionSpec); 757 | setOutputRecordCreator(new EciOutputRecordCreator()); 758 | } 759 | 760 | private static class EciOutputRecordCreator implements RecordCreator { 761 | public Record createRecord(RecordFactory recordFactory) throws ResourceException { 762 | return new CommAreaRecord(); 763 | } 764 | } 765 | 766 | } 767 | ``` 768 | 769 | 然后我们可以将抽象的 `EciMappingOperation`类子类化为指定自定义对象和Records之间的映射,如下例所示: 770 | 771 | ```java 772 | public class MyDaoImpl extends CciDaoSupport implements MyDao { 773 | 774 | public OutputObject getData(Integer id) { 775 | EciMappingOperation query = new EciMappingOperation(getConnectionFactory(), "MYPROG") { 776 | 777 | protected abstract byte[] objectToBytes(Object inObject) throws IOException { 778 | Integer id = (Integer) inObject; 779 | return String.valueOf(id); 780 | } 781 | 782 | protected abstract Object bytesToObject(byte[] bytes) throws IOException; 783 | String str = new String(bytes); 784 | String field1 = str.substring(0,6); 785 | String field2 = str.substring(6,1); 786 | String field3 = str.substring(7,1); 787 | return new OutputObject(field1, field2, field3); 788 | } 789 | }); 790 | 791 | return (OutputObject) query.execute(new Integer(id)); 792 | } 793 | 794 | } 795 | ``` 796 | 797 | 在非托管模式下,Spring bean的相应配置如下: 798 | 799 | ```xml 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | ``` 815 | 816 | 在托管模式下(即在Java EE环境中),配置可能如下所示: 817 | 818 | ```xml 819 | 820 | 821 | 822 | 823 | 824 | ``` 825 | 826 | 827 | 828 | ### [](#cci-tx)5.4. 事务 829 | 830 | JCA为资源适配器(resource adapters)指定了几个级别的事务支持。你可以在`ra.xml`文件中指定你的资源适配器支持的事务类型。它本质上有三个选项:none(例如,使用CICS EPI连接器),本地事务(例如,使用CICS ECI连接器)和全局事务(例如,使用IMS连接器)。 以下示例配置全局选项:: 831 | 832 | ```xml 833 | 834 | 835 | 836 | 837 | XATransaction 838 | 839 | 840 | ``` 841 | 842 | 对于全局事务,您可以使用Spring的通用事务基础结构来划分事务,使用`JtaTransactionManager`作为后端(委托给下面的Java EE服务器的分布式事务协调器)。 843 | 844 | 对于单个CCI `ConnectionFactory`上的本地事务,Spring为CCI提供了特定的事务管理策略,类似于JDBC的`DataSourceTransactionManager`。CCI API定义了本地事务对象和相应的本地事务划分方法。 Spring的`CciLocalTransactionManager`执行这样的本地CCI事务,完全依照Spring中常见的`PlatformTransactionManager`抽象。以下示例配置 `CciLocalTransactionManager`: 845 | 846 | ```xml 847 | 848 | 849 | 851 | 852 | 853 | ``` 854 | 855 | 您可以将这两种事务策略与Spring的任何事务划分工具一起使用,无论是声明性的还是编程式的。这是Spring通用的`PlatformTransactionManager`抽象的结果,它解耦了实际运行策略中的事务划分。你可以保持现在的事务划分,只需要在`JtaTransactionManager`和`CciLocalTransactionManager`之间转换即可。 856 | 857 | 有关Spring的事务处理机制的更多信息,请参阅[事务管理](data-access.html#transaction)。 -------------------------------------------------------------------------------- /pages/integration/remoting.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [](#remoting)1\. Spring的远程处理和Web服务 4 | ---------------------------------- 5 | 6 | Spring为使用各种技术的远程支持的集成类提供了功能,远程处理支持可轻松使用常用(Spring)pojo实现的、由您提供的服务开发。目前,Spring支持以下远程技术: 7 | 8 | * **远程方法调用(RMI)**:通过使用`RmiProxyFactoryBean`和`RmiServiceExporter`。Spring支持传统的RMI(使用 `java.rmi.Remote`接口和`java.rmi.RemoteException`)以及通过RMI调用程序(使用任何Java接口)进行透明的远程处理。 9 | 10 | * **Spring的 HTTP 调用**: Spring提供了一种特殊的远程处理策略,允许通过HTTP进行Java序列化,支持任何Java接口(如RMI调用者所做的那样)。 相应的支持类是`HttpInvokerProxyFactoryBean`和`HttpInvokerServiceExporter`. 11 | 12 | * **Hessian**:通过使用Spring的`HessianProxyFactoryBean` 和`HessianServiceExporter`,您可以使用Caucho提供的轻量级二进制http协议透明地公开您的服务。 13 | 14 | * **JAX-WS**: Spring通过JAX-WS 为Web服务提供了远程处理支持(如Java EE 5和Java 6所介绍的那样, 它继承了JAX-RPC). 15 | 16 | * **JMS**: 通过`JmsInvokerServiceExporter` 和 `JmsInvokerProxyFactoryBean`类支持使用JMS作为底层协议进行远程处理。 17 | 18 | * **AMQP**:Spring AMQP项目支持使用AMQP作为底层协议进行远程处理。 19 | 20 | 在讨论Spring的远程处理功能时,我们使用以下域模型和相应的服务: 21 | 22 | ```java 23 | public class Account implements Serializable{ 24 | 25 | private String name; 26 | 27 | public String getName(){ 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | } 36 | 37 | public interface AccountService { 38 | 39 | public void insertAccount(Account account); 40 | 41 | public List getAccounts(String name); 42 | 43 | } 44 | 45 | // the implementation doing nothing at the moment 46 | public class AccountServiceImpl implements AccountService { 47 | 48 | public void insertAccount(Account acc) { 49 | // do something... 50 | } 51 | 52 | public List getAccounts(String name) { 53 | // do something... 54 | } 55 | 56 | } 57 | ``` 58 | 59 | ```java 60 | 61 | ``` 62 | 63 | 本节首先使用RMI将服务公开给远程客户端,然后再谈谈使用RMI的缺点。 然后继续使用Hessian作为协议的示例。 64 | 65 | 66 | 67 | ### [](#remoting-rmi)1.1. 使用RMI公开服务 68 | 69 | 使用Spring对RMI的支持, 您可以通过RMI架构透明地公开服务。在用此设置之后, 您基本上拥有一个类似于远程EJB的配置, 但不存在安全上下文传播或远程事务传播的标准支持这一事实。Spring在使用RMI调用器时提供了这样的附加调用上下文的钩子, 因此您可以在此处插入安全框架或自定义安全凭据。 70 | 71 | 72 | 73 | #### [](#remoting-rmi-server)1.1.1. 使用`RmiServiceExporter`暴露服务 74 | 75 | 使用`RmiServiceExporter`,我们可以将AccountService对象的接口公开为RMI对象。可以使用`RmiProxyFactoryBean`访问该接口,或者在传统RMI服务的情况下通过普通RMI访问该接口。 `RmiServiceExporter`明确支持通过RMI调用程序公开任何非RMI服务。 76 | 77 | 我们首先必须在Spring容器中设置我们的服务。 以下示例显示了如何执行此操作: 78 | 79 | ```xml 80 | 81 | 82 | 83 | ``` 84 | 85 | 接下来,我们必须使用`RmiServiceExporter`暴露我们的服务。 以下示例显示了如何执行此操作: 86 | 87 | ```xml 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | ``` 97 | 98 | 在前面的示例中,我们覆盖RMI注册表的端口。通常情况下, 您的应用服务器还维护一个RMI注册表, 因此最好不要干预它。此外, 服务名称用于绑定服务。因此,在前面的示例中,服务绑定在 `'rmi://HOST:1199/AccountService'`。 我们稍后使用此URL链接客户端的服务。 . 99 | 100 | `servicePort` 属性已被省略(默认为0)。这意味着将使用匿名端口与服务进行通信。 101 | 102 | 103 | 104 | #### [](#remoting-rmi-client)1.1.2. 连接客户端和服务 105 | 106 | 我们的客户端是一个使用 `AccountService`管理帐户的简单对象,如以下示例所示: 107 | 108 | ```java 109 | public class SimpleObject { 110 | 111 | private AccountService accountService; 112 | 113 | public void setAccountService(AccountService accountService) { 114 | this.accountService = accountService; 115 | } 116 | 117 | // additional methods using the accountService 118 | 119 | } 120 | ``` 121 | 122 | 要在客户端上链接服务, 我们将创建一个单独的Spring容器, 其中包含简单对象和连接配置位的服务。 123 | 124 | ```xml 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | ``` 134 | 135 | 这就是支持客户端连接远程帐户服务所需做的全部工作,Spring将透明地创建一个调用, 并通过 `RmiServiceExporter`远程启用帐户服务。在客户端, 我们在其链接中使用 `RmiProxyFactoryBean`。 136 | 137 | 138 | 139 | ### [](#remoting-caucho-protocols)1.2. 使用Hessian通过HTTP远程调用服务 140 | 141 | Hessian提供基于HTTP的二进制远程协议。 它由Caucho开发,您可以在 [http://www.caucho.com](http://www.caucho.com)找到有关Hessian本身的更多信息。 142 | 143 | 144 | 145 | #### [](#remoting-caucho-protocols-hessian)1.2.1.使用`DispatcherServlet` 连接Hessian 146 | 147 | Hessian通过HTTP进行通信,并使用自定义servlet进行通信。通过使用Spring的`DispatcherServlet` 原则(参见[\[webmvc#mvc-servlet\]](webmvc.html#mvc-servlet)),我们可以连接这样的servlet以公开您的服务。 首先,我们必须在我们的应用程序中创建一个新的servlet,如以下摘自`web.xml`所示: 148 | 149 | ```xml 150 | 151 | remoting 152 | org.springframework.web.servlet.DispatcherServlet 153 | 1 154 | 155 | 156 | 157 | remoting 158 | /remoting/* 159 | 160 | ``` 161 | 162 | 如果您熟悉Spring的`DispatcherServlet`原则,您可能知道现在必须在 `WEB-INF`目录中创建一个名为`remoting-servlet.xml`的Spring容器配置资源(在您的servlet名称之后)。 应用程序上下文将在下一节中使用。 163 | 164 | 或者, 考虑使用Spring更简单的`HttpRequestHandlerServlet`。这使您可以在根应用程序上下文中嵌入远程公开定义(默认情况下为`WEB-INF/applicationContext.xml`), 并将单个Servlet定义指向特定的公开bean。在这种情况下, 每个Servlet名称都需要匹配其目标公开的bean名称。 165 | 166 | 167 | 168 | #### [](#remoting-caucho-protocols-hessian-server)1.2.2. 使用`HessianServiceExporter`暴露您的Bean 169 | 170 | 在新创建的名为 `remoting-servlet.xml`的应用程序上下文中,我们创建了一个`HessianServiceExporter` 来暴露我们的服务,如下例所示: 171 | 172 | ```xml 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | ``` 182 | 183 | 现在, 我们已经准备好在客户端上链接服务了.未指定显式处理程序映射, 将请求URL映射到服务上, 因此将使用`BeanNameUrlHandlerMapping`。 因此, 该服务将在包含 `DispatcherServlet`映射(如上所述)的URL中通过其bean名称指示的网址公开:`[http://HOST:8080/remoting/AccountService](http://HOST:8080/remoting/AccountService)` 184 | 185 | 或者, 在您的根应用程序上下文中创建一个`HessianServiceExporter`(例如在 `WEB-INF/applicationContext.xml`)。如下所示: 186 | 187 | ```xml 188 | 189 | 190 | 191 | 192 | ``` 193 | 194 | 在后一种情况下, 在`web.xml`中为该公开定义一个相应的Servlet, 并使用相同的最终结果: 公开程序被映射到请求路径`/remoting/AccountService`。请注意, Servlet名称需要与目标公开方的bean名称匹配。 195 | 196 | ```xml 197 | 198 | accountExporter 199 | org.springframework.web.context.support.HttpRequestHandlerServlet 200 | 201 | 202 | 203 | accountExporter 204 | /remoting/AccountService 205 | 206 | ``` 207 | 208 | 209 | 210 | #### [](#remoting-caucho-protocols-hessian-client)1.2.3. 在客户端的服务中链接 211 | 212 | 通过使用`HessianProxyFactoryBean`,我们可以链接客户端的服务。同样的原则适用于RMI示例。我们创建一个单独的bean工厂或应用程序上下文,并提到以下bean,其中`SimpleObject`是通过使用`AccountService`来管理帐户,如以下示例所示:: 213 | 214 | ```xml 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | ``` 224 | 225 | 226 | 227 | #### [](#remoting-caucho-protocols-security)1.2.4. 通过Hessian公开的服务来应用HTTP基本的验证 228 | 229 | Hessian的一个优点是我们可以轻松应用HTTP基本身份验证,因为这两种协议都是基于HTTP的。例如,可以通过使用 `web.xml`安全功能来应用常规HTTP服务器安全性机制。 通常,您无需在此处使用每用户安全凭据。 相反,您可以使用在`HessianProxyFactoryBean`级别定义的共享凭证(类似于JDBC `DataSource`),如以下示例所示: 230 | 231 | ```xml 232 | 233 | 234 | 235 | 236 | 238 | 239 | 240 | ``` 241 | 242 | 在前面的示例中,我们明确提到了 `BeanNameUrlHandlerMapping`并设置了一个拦截器,只允许管理员和操作员调用此应用程序上下文中提到的bean。 243 | 244 | 前面的示例并没有显示一种灵活的安全基础结构。有关安全性的更多选项,请查看[http://projects.spring.io/spring-security/](https://projects.spring.io/spring-security/)上的Spring Security项目。 245 | 246 | 247 | 248 | ### [](#remoting-httpinvoker)1.3. 使用HTTP调用公开服务 249 | 250 | 与Hessian相反,Spring HTTP调用者都是轻量级协议,Hessian使用自身序列化机制,而Spring HTTP调用者使用标准的Java序列化机制通过HTTP公开服务。如果您的参数和返回类型是使用Hessian使用的序列化机制无法序列化的复杂类型,那么这具有巨大的优势(当您选择远程处理技术时,请参阅下一节以了解更多注意事项)。 251 | 252 | 在底层,Spring使用JDK或Apache `HttpComponents`提供的标准工具来执行HTTP调用。 如果您需要更高级且更易于使用的功能,请使用后者。 有关更多信息,请参阅[hc.apache.org/httpcomponents-client-ga/](https://hc.apache.org/httpcomponents-client-ga/)。 253 | 254 | 由于不安全的Java反序列化而造成的漏洞:操作的输入流可能导致在反序列化步骤中在服务器上执行不需要的代码。因此, 不要将HTTP调用方终结点公开给不受信任的客户端, 而只应该在您自己的服务之间。通常, 我们强烈推荐任何其他消息格式(例如JSON)。 255 | 256 | 如果您担心由于Java序列化引起的安全漏洞, 请考虑核心JVM级别的generalpurpose序列化筛选器机制, 它最初是为JDK 9开发的。但同时又向后移植到JDK 8,7和6。 请参阅[https://blogs.oracle.com/java-platform-group/entry/incoming\_filter\_serialization\_data\_a](https://blogs.oracle.com/java-platform-group/entry/incoming_filter_serialization_data_a) 和 [http://openjdk.java.net/jeps/290](http://openjdk.java.net/jeps/290). 257 | 258 | 259 | 260 | #### [](#remoting-httpinvoker-server)1.3.1. 公开服务对象 261 | 262 | 为服务对象设置HTTP调用的架构类似于使用Hession,方式也是相同的。正如Hession支持提供 是`HessianServiceExporter`。 Spring的HttpInvoker支持提供了`org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter`。 263 | 264 | 要在Spring Web MVC `DispatcherServlet`中暴露`AccountService`(前面提到过),需要在调度程序的应用程序上下文中使用以下配置,如以下示例所示:: 265 | 266 | ```xml 267 | 268 | 269 | 270 | 271 | ``` 272 | 273 | 这样一个公开的定义将通过 `DispatcherServlet`的标准映射设备, 如在关于[Hession的章节](#remoting-caucho-protocols)中解释。 274 | 275 | 或者,您可以在根应用程序上下文中创建`HttpInvokerServiceExporter`(例如,在`'WEB-INF/applicationContext.xml'`中),如以下示例所示: 276 | 277 | ```xml 278 | 279 | 280 | 281 | 282 | ``` 283 | 284 | 此外,在`web.xml`中为该公开定义一个对应的Servlet, 其Servlet名称与目标公开的bean名称相匹配。如下所示: 285 | 286 | ```xml 287 | 288 | accountExporter 289 | org.springframework.web.context.support.HttpRequestHandlerServlet 290 | 291 | 292 | 293 | accountExporter 294 | /remoting/AccountService 295 | 296 | ``` 297 | 298 | 299 | 300 | #### [](#remoting-httpinvoker-client)1.3.2. 在客户端链接服务 301 | 302 | 同样,从客户端链接服务非常类似于使用Hessian时的方式。通过使用代理,Spring可以将对HTTP POST请求的调用转换为指向公开服务的URL。以下示例显示如何配置: 303 | 304 | ```xml 305 | 306 | 307 | 308 | 309 | ``` 310 | 311 | 如前所述,您可以选择要使用的HTTP客户端。默认情况下,`HttpInvokerProxy`使用JDK的HTTP功能,但您也可以通过设置 `httpInvokerRequestExecutor`属性来使用Apache `HttpComponents`客户端。以下示例显示了如何执行此操作: 312 | 313 | ```xml 314 | 315 | 316 | 317 | ``` 318 | 319 | 320 | 321 | ### [](#remoting-web-services)1.4. Web Services 322 | 323 | Spring提供对标准Java Web服务API的完全支持: 324 | 325 | * 使用JAX-WS暴露服务 326 | 327 | * 使用JAX-WS访问Web服务 328 | 329 | 330 | 除了在Spring的核心中支持JAX-WS外,Spring的portfolio也具有Spring Web服务的特点。这是一种以文档驱动的[Spring Web服务](http://www.springframework.org/spring-ws)为基础的解决方案, 它极力推荐用于构建现代的、经得起未来考验的Web服务。 331 | 332 | 333 | 334 | #### [](#remoting-web-services-jaxws-export-servlet)1.4.1. 使用JAX-WS公开基于Servlet的Web服务 335 | 336 | Spring为JAX-WS Servlet端实现提供了一个方便的基类- `SpringBeanAutowiringSupport`。为了公开我们的`AccountService`, 我们继承了Spring的`SpringBeanAutowiringSupport`类并在这里实现我们的业务逻辑, 通常是将调用委托给业务层。我们将简单地使用spring的`@Autowired`注解来表达对Spring管理bean的这种依赖性。以下示例显示了扩展`SpringBeanAutowiringSupport`的类: 337 | 338 | ```java 339 | /** 340 | * JAX-WS compliant AccountService implementation that simply delegates 341 | * to the AccountService implementation in the root web application context. 342 | * 343 | * This wrapper class is necessary because JAX-WS requires working with dedicated 344 | * endpoint classes. If an existing service needs to be exported, a wrapper that 345 | * extends SpringBeanAutowiringSupport for simple Spring bean autowiring (through 346 | * the @Autowired annotation) is the simplest JAX-WS compliant way. 347 | * 348 | * This is the class registered with the server-side JAX-WS implementation. 349 | * In the case of a Java EE 5 server, this would simply be defined as a servlet 350 | * in web.xml, with the server detecting that this is a JAX-WS endpoint and reacting 351 | * accordingly. The servlet name usually needs to match the specified WS service name. 352 | * 353 | * The web service engine manages the lifecycle of instances of this class. 354 | * Spring bean references will just be wired in here. 355 | */ 356 | import org.springframework.web.context.support.SpringBeanAutowiringSupport; 357 | 358 | @WebService(serviceName="AccountService") 359 | public class AccountServiceEndpoint extends SpringBeanAutowiringSupport { 360 | 361 | @Autowired 362 | private AccountService biz; 363 | 364 | @WebMethod 365 | public void insertAccount(Account acc) { 366 | biz.insertAccount(acc); 367 | } 368 | 369 | @WebMethod 370 | public Account[] getAccounts(String name) { 371 | return biz.getAccounts(name); 372 | } 373 | 374 | } 375 | ``` 376 | 377 | 我们的`AccountServiceEndpoint`需要在与Spring上下文相同的Web应用程序中运行,以允许访问Spring的工具。 默认情况下,在Java EE 5环境中使用JAX-WS servlet端点部署的标准协定就是这种情况。 有关详细信息,请参阅各种Java EE 5 Web服务教程。 378 | 379 | 380 | 381 | #### [](#remoting-web-services-jaxws-export-standalone)1.4.2. 使用JAX-WS公开独立Web服务 382 | 383 | Oracle JDK附带的内置JAX-WS提供程序通过使用JDK中包含的内置HTTP服务器支持Web服务的公开。Spring的`SimpleJaxWsServiceExporter`在Spring应用程序上下文中检测所有`@WebService`注解的bean,并通过默认的JAX-WS服务器(JDK HTTP服务器)公开它们。 384 | 385 | 在这种情况下, 端实例被定义并作为Spring bean来管理。它们将在JAX-WS引擎中注册, 但它们的生命周期将由Spring应用程序上下文来实现。这意味着像显式依赖注入这样的Spring功能可以应用到端点实例。 当然, 通过`@Autowired`的注解驱动的注入也会起作用。以下示例显示了如何定义这些bean: 386 | 387 | ```xml 388 | 389 | 390 | 391 | 392 | 393 | ... 394 | 395 | 396 | ... 397 | ``` 398 | 399 | `AccountServiceEndpoint`可以不用继承Spring的`SpringBeanAutowiringSupport`,因为此示例中的端点是完全由Spring管理的bean。 这意味着端点实现可以如下(没有声明任何超类 - 而且Spring的`@Autowired`配置注解仍然被可用): 400 | 401 | ```java 402 | @WebService(serviceName="AccountService") 403 | public class AccountServiceEndpoint { 404 | 405 | @Autowired 406 | private AccountService biz; 407 | 408 | @WebMethod 409 | public void insertAccount(Account acc) { 410 | biz.insertAccount(acc); 411 | } 412 | 413 | @WebMethod 414 | public List getAccounts(String name) { 415 | return biz.getAccounts(name); 416 | } 417 | 418 | } 419 | ``` 420 | 421 | 422 | 423 | #### [](#remoting-web-services-jaxws-export-ri)1.4.3. 使用JAX-WS RI的Spring支持公开Web服务 424 | 425 | Oracle的JAX-WS RI, 作为GlassFish项目的一部分,通过Spring的支持也作为JAX-WS Commons项目的一部分。这允许将 JAX-WS端点定义为Spring管理bean, 类似于[上一节](#remoting-web-services-jaxws-export-standalone)中讨论的独立模式。 但这一次是在Servlet环境中进行的 426 | 427 | 这在Java EE 5环境中不可移植。 它主要用于非EE环境,例如Tomcat,它将JAX-WS RI作为Web应用程序的一部分嵌入。 428 | 429 | 与Servlet端的标准样式的不同之处在于,端实例本身的生命周期将由Spring来管理,并且在`web.xml`中将只定义一个JAX-WS Servlet。使用标准的Java EE 5样式 (如上所述), 每个服务端都有一个Servlet定义, 每个端通常委派给Spring bean(通过使用`@Autowired`,如上所示)。 430 | 431 | 有关设置和使用方式的详细信息,请参阅[https://jax-ws-commons.java.net/spring/](https://jax-ws-commons.java.net/spring/)。 432 | 433 | 434 | 435 | #### [](#remoting-web-services-jaxws-access)1.4.4. 使用JAX-WS访问Web服务 436 | 437 | Spring提供了两个工厂bean来创建JAX-WS Web服务代理,即`LocalJaxWsServiceFactoryBean` 和 `JaxWsPortProxyFactoryBean`。前者只能返回一个JAX-WS服务类供我们使用。后者是完整版本,可以返回实现我们的业务服务接口的代理。在以下示例中,我们使用`JaxWsPortProxyFactoryBean`为`AccountService`端点创建代理(再次):: 438 | 439 | ```xml 440 | 441 | (1) 442 | 443 | 444 | 445 | 446 | 447 | ``` 448 | 449 | **1**、其中`serviceInterface` 是客户端使用的业务接口。 450 | 451 | `wsdlDocumentUrl`是WSDL文件的URL。Spring需要这样的启动时间来创建 JAX-WS服务。`namespaceUri`对应于.wsdl文件中的`targetNamespace` 。`serviceName`对应于.wsdl文件中的服务名称。 `portName`对应于.wsdl文件中的端口名称。 452 | 453 | 访问Web服务现在非常容易, 因为我们有一个bean工厂, 它将公开它作为`AccountService`接口。我们可以在Spring中配置: 454 | 455 | ```xml 456 | 457 | ... 458 | 459 | 460 | ``` 461 | 462 | 从客户端代码中, 我们可以访问Web服务,就好像它是一个普通类一样: 463 | 464 | ```java 465 | public class AccountClientImpl { 466 | 467 | private AccountService service; 468 | 469 | public void setService(AccountService service) { 470 | this.service = service; 471 | } 472 | 473 | public void foo() { 474 | service.insertAccount(...); 475 | } 476 | } 477 | ``` 478 | 479 | 上述情况略有简化, 因为JAX-WS需要端点接口和实现类来注解`@WebService`、`@SOAPBinding`等注解。这意味着您不能(很容易)使用普通的Java接口和实现类作为JAX-WS端点工件;首先您需要相应地对它们进行注解。 有关这些要求的详细信息 请参阅JAX-WS文档。 480 | 481 | 482 | 483 | ### [](#remoting-jms)1.5. 通过JMS公开服务 484 | 485 | 您还可以使用JMS作为底层通信协议透明地公开服务。 JMS的远程支持在Spring Framework中是非常基础。它在同一个线程和同一个非事务性会话中发送和接收。 并且这样的吞吐量将非常依赖于实现, 请注意, 这些单线程和非事务性约束仅适用于Spring的JMS远程处理支持。有关Spring对JMS消息传递的丰富支持的信息, 请参见[JMS (Java Message Service)](#jms) 486 | 487 | 以下接口用于服务器端和客户端: 488 | 489 | ```java 490 | package com.foo; 491 | 492 | public interface CheckingAccountService { 493 | 494 | public void cancelAccount(Long accountId); 495 | 496 | } 497 | ``` 498 | 499 | 在服务器端使用上述接口的以下简单实现: 500 | 501 | ```java 502 | package com.foo; 503 | 504 | public class SimpleCheckingAccountService implements CheckingAccountService { 505 | 506 | public void cancelAccount(Long accountId) { 507 | System.out.println("Cancelling account [" + accountId + "]"); 508 | } 509 | 510 | } 511 | ``` 512 | 513 | 此配置文件包含在客户端和服务器上共享的JMS基础结构bean: 514 | 515 | ```xml 516 | 517 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | ``` 532 | 533 | 534 | 535 | #### [](#remoting-jms-server)1.5.1. 服务端的配置 536 | 537 | 在服务器上, 您只需使用`JmsInvokerServiceExporter`公开服务对象: 538 | ```xml 539 | 540 | 544 | 545 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | ``` 562 | ```java 563 | package com.foo; 564 | 565 | import org.springframework.context.support.ClassPathXmlApplicationContext; 566 | 567 | public class Server { 568 | 569 | public static void main(String[] args) throws Exception { 570 | new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"}); 571 | } 572 | 573 | } 574 | ``` 575 | 576 | 577 | 578 | #### [](#remoting-jms-client)1.5.2. 客户端方面的配置 579 | 580 | 客户端只需要创建一个实现约定接口(`CheckingAccountService`)的客户端代理。 581 | 582 | 以下示例定义了可以注入其他客户端对象的bean(代理负责通过JMS将调用转发到服务器端对象): 583 | ```xml 584 | 585 | 589 | 590 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | ``` 599 | ```java 600 | package com.foo; 601 | 602 | import org.springframework.context.ApplicationContext; 603 | import org.springframework.context.support.ClassPathXmlApplicationContext; 604 | 605 | public class Client { 606 | 607 | public static void main(String[] args) throws Exception { 608 | ApplicationContext ctx = new ClassPathXmlApplicationContext( 609 | new String[] {"com/foo/client.xml", "com/foo/jms.xml"}); 610 | CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService"); 611 | service.cancelAccount(new Long(10)); 612 | } 613 | 614 | } 615 | ``` 616 | 617 | 618 | 619 | ### [](#remoting-amqp)1.6. AMQP 620 | 621 | 有关详细信息,请参阅[Spring AMQP参考指南的“使用AMQP进行Spring Remoting”部分](https://docs.spring.io/spring-amqp/docs/current/reference/html/_reference.html#remoting) 。 622 | 623 | 远程接口未实现自动检测 624 | 625 | 远程接口不发生自动检测实现的接口的主要原因是避免向远程调用方打开太多的门,目标对象可能实现内部回调接口,例如`InitializingBean`或`DisposableBean`,它们不希望向调用者公开。 626 | 627 | 在本地情况下, 向代理提供由目标实现的所有接口通常并不重要。但是, 当公开远程服务时, 应公开特定的服务接口, 并使用特定的操作来进行远程使用。 除内部回调接口外, 目标可能实现多个业务接口, 其中仅有一个用于远程公开。由于这些原因,, 我们需要指定这样的服务接口。 628 | 629 | 这是配置方便和意外暴露内部方法风险之间的权衡,总是指定一个服务接口并不费劲, 并把你放在安全方面的控制公开的特定方法。 630 | 631 | 632 | 633 | ### [](#remoting-considerations)1.7. 选择技术时的注意事项 634 | 635 | 这里介绍的每项技术都有其缺点。 在选择技术时,您应该仔细考虑您的需求,您公开的服务以及通过网络发送的对象。 636 | 637 | 当使用RMI,不可能通过HTTP协议来访问到这些对象,除非你使用了RMI的隧道。RMI是一个重量级的协议,支持全功能的序列化策略,这在使用需要在线上进行序列化的复杂数据模型时非常重要。 但是, RMI-JRMP与Java客户端绑定在一起,它是一种Java-to-Java远程处理解决方案。 638 | 639 | 如果您需要基于HTTP的远程处理,而且还依赖于Java序列化,那么Spring的HTTP调用程序是一个不错的选择。它与RMI调用共享基本的基础结构, 只是使用HTTP作为传输。 请注意,HTTP调用程序不仅限于Java-to-Java远程处理,还包括客户端和服务器端的Spring。(后者也适用于Spring的非RMI接口的RMI调用程序。) 640 | 641 | 在跨平台中使用时,Hessian是很好的选择,因为它们明确允许非Java客户端。但是,非Java支持仍然有限。已知问题包括Hibernate对象的序列化以及延迟初始化的集合。如果您有这样的数据模型,请考虑使用RMI或HTTP调用程序而不是Hessian。 642 | 643 | JMS可用于提供服务群集, 并允许JMS代理处理负载平衡、发现和自动故障转移。 默认情况下, 在使用JMS远程处理时使用Java序列化, 但JMS提供程序可以对格式使用不同的机制, 例如XStream以允许在其他技术中实现服务器。 644 | 645 | 最后但并非最不重要的是,EJB具有优于RMI的优势,因为它支持标准的基于角色的身份验证和授权以及远程事务传播。有可能让RMI调用者或HTTP调用者也支持安全上下文传播,尽管核心Spring没有提供。 Spring仅提供适当的挂钩,用于插入第三方或自定义解决方案。 646 | 647 | 648 | 649 | ### [](#rest-client-access)1.8. REST 端点 650 | 651 | Spring Framework为调用REST端点提供了两种选择: 652 | 653 | * [使用 `RestTemplate`](#rest-resttemplate): 原始的Spring REST客户端与在Spring中API相似于其他模板类,例如JdbcTemplate 654 | 655 | * [WebClient](web-reactive.html#webflux-client): 非阻塞,响应式替代方案,支持同步和异步以及流方案。 656 | 657 | 658 | 从5.0开始,非阻塞,反应式 `WebClient`提供了`RestTemplate`的现代替代方案,同时有效支持同步和异步以及流方案。 `RestTemplate`将在未来版本中弃用,并且不会在未来添加主要的新功能。 659 | 660 | 661 | 662 | #### [](#rest-resttemplate)1.8.1. 使用 `RestTemplate` 663 | 664 | `RestTemplate`在HTTP客户端库上提供了更高级别的API,它使得在一行中调用REST端点变得容易。 它公开了以下重载方法组: 665 | 666 | Table 1. RestTemplate 方法 667 | 668 | | Method group | Description | 669 | | ---- | ---- | 670 | | `getForObject` | 通过GET获取响应。 | 671 | | `getForEntity` | 使用GET获取`ResponseEntity` (即status, headers和body) | 672 | | `headForHeaders` | Retrieves all headers for a resource by using HEAD. | 673 | | `postForLocation` | 使用POST创建新资源,并从响应中返回`Location`标头。 | 674 | | `postForObject` | 使用POST创建新资源并从响应中返回表示。 | 675 | | `postForEntity` | 使用POST创建新资源并从响应中返回表示。 | 676 | | `put` | 使用PUT创建或更新资源。 | 677 | | `patchForObject` | 使用PATCH更新资源并从响应中返回表示。 请注意,JDK `HttpURLConnection`不支持`PATCH`,但Apache HttpComponents和其他一样。 | 678 | | `delete` | 使用DELETE删除指定URI处的资源。| 679 | | `optionsForAllow` | 使用ALLOW检索资源的允许HTTP方法。 | 680 | | `exchange` | 上述方法的更通用(且不太固定)的版本,在需要时提供额外的灵活性。 它接受`RequestEntity`(包括HTTP方法,URL,headers和正文作为输入)并返回`ResponseEntity`。这些方法允许使用`ParameterizedTypeReference`而不是`Class`来指定具有泛型的响应类型。 | 681 | | `execute` | 执行请求的最通用方式,通过回调接口完全控制请求准备和响应提取。 | 682 | 683 | 684 | 685 | 686 | ##### [](#rest-resttemplate-create)初始化 687 | 688 | 默认构造函数使用 `java.net.HttpURLConnection`来执行请求。 您可以使用 `ClientHttpRequestFactory`的实现切换到不同的HTTP库。 内置支持以下内容: 689 | 690 | * Apache HttpComponents 691 | 692 | * Netty 693 | 694 | * OkHttp 695 | 696 | 697 | 例如,要切换到Apache HttpComponents,您可以使用以下命令: 698 | 699 | ```java 700 | RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); 701 | ``` 702 | 703 | 每个`ClientHttpRequestFactory`都公开特定于底层HTTP客户端库的配置选项 - 例如,用于凭据,连接池和其他详细信息。 704 | 705 | 请注意,HTTP请求的`java.net`实现在访问表示错误的响应的状态(例如401)时可能引发异常。 如果这是一个问题,请切换到另一个HTTP客户端库。 706 | 707 | 708 | 709 | ##### [](#rest-resttemplate-uri)URIs 710 | 711 | 许多`RestTemplate`方法接受URI模板和URI模板变量,可以是`String`变量参数,也可以是`Map`。 712 | 713 | 以下示例使用`String`变量参数: 714 | 715 | ```java 716 | String result = restTemplate.getForObject( 717 | "http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21"); 718 | ``` 719 | 720 | 以下示例使用`Map` : 721 | 722 | ```java 723 | Map vars = Collections.singletonMap("hotel", "42"); 724 | 725 | String result = restTemplate.getForObject( 726 | "http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); 727 | ``` 728 | 729 | 请记住,URI模板是自动编码的,如以下示例所示: 730 | 731 | ```java 732 | restTemplate.getForObject("http://example.com/hotel list", String.class); 733 | 734 | // Results in request to "http://example.com/hotel%20list" 735 | ``` 736 | 737 | 您可以使用`RestTemplate`的`uriTemplateHandler`属性来自定义URI的编码方式。 或者,您可以准备`java.net.URI`并将其传递给接受`URI`的`RestTemplate`方法之一。 738 | 739 | 有关使用和编码URI的更多详细信息,请参阅[URI Links](web.html#mvc-uri-building)。 740 | 741 | 742 | 743 | ##### [](#rest-template-headers)Headers 744 | 745 | 您可以使用`exchange()`方法指定请求头,如以下示例所示: 746 | 747 | ```java 748 | String uriTemplate = "http://example.com/hotels/{hotel}"; 749 | URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42); 750 | 751 | RequestEntity requestEntity = RequestEntity.get(uri) 752 | .header(("MyRequestHeader", "MyValue") 753 | .build(); 754 | 755 | ResponseEntity response = template.exchange(requestEntity, String.class); 756 | 757 | String responseHeader = response.getHeaders().getFirst("MyResponseHeader"); 758 | String body = response.getBody(); 759 | ``` 760 | 761 | 您可以通过返回 `ResponseEntity`的许多`RestTemplate`方法变体获取响应头。 762 | 763 | 764 | 765 | ##### [](#rest-template-body)Body 766 | 767 | 传递到`RestTemplate`方法并从RestTemplate方法返回的对象在 `HttpMessageConverter`的帮助下转换为原始内容和从原始内容转换。 768 | 769 | 在POST上,输入对象被序列化到请求主体,如以下示例所示: 770 | 771 | URI location = template.postForLocation("http://example.com/people", person); 772 | 773 | 您无需显式设置请求的`Content-Type`头。在大多数情况下,您可以找到基于源对象类型的兼容消息转换器,并且所选消息转换器会相应地设置内容类型。 如有必要,您可以使用`exchange`方法显式提供`Content-Type`请求头,这反过来会影响选择的消息转换器。 774 | 775 | 在GET上,响应的主体被反序列化为输出`Object`,如以下示例所示: 776 | 777 | Person person = restTemplate.getForObject("http://example.com/people/{id}", Person.class, 42); 778 | 779 | 不需要显式设置请求的`Accept`头。在大多数情况下,可以根据预期的响应类型找到兼容的消息转换器,然后有助于填充`Accept`头。如有必要,可以使用 `exchange`方法显式提供Accept头。 780 | 781 | 默认情况下, `RestTemplate`会注册所有[消息转换器](#rest-message-conversion),具体取决于有助于确定存在哪些可选转换库的类路径检查。 您还可以将消息转换器设置为显式使用。 782 | 783 | 784 | 785 | ##### [](#rest-message-conversion)消息转换器 786 | 787 | [Same as in Spring WebFlux](web-reactive.html#webflux-codecs) 788 | 789 | `spring-web` 模块包含 `HttpMessageConverter`,用于通过`InputStream`和`OutputStream`读取和写入HTTP请求和响应的主体。 `HttpMessageConverter`实例用于客户端(例如,在`RestTemplate`中)和服务器端(例如,在Spring MVC REST控制器中)。 790 | 791 | 框架中提供了主要媒体(MIME)类型的具体实现,默认情况下,它们在客户端注册`RestTemplate` ,在服务器端注册`RequestMethodHandlerAdapter`(请参阅[配置消息转换器](web.html#mvc-config-message-converters))。 792 | 793 | `HttpMessageConverter`的实现将在以下部分中介绍。 对于所有转换器,使用默认媒体类型,但您可以通过设置`supportedMediaTypes` bean属性来覆盖它。 下表描述了每种实现: 794 | 795 | Table 2. HttpMessageConverter 实现 796 | 797 | | 消息转换器 | 描述 | 798 | | ---- | ---- | 799 | | `StringHttpMessageConverter` | 一个`HttpMessageConverter`实现,可以从HTTP请求和响应中读取和写入`String`实例。 默认情况下,此转换器支持所有文本媒体类型(`text/*`),并使用`Content-Type` 为`text/plain`进行写入。 | 800 | | `FormHttpMessageConverter` | 一个`HttpMessageConverter`实现,可以从HTTP请求和响应中读取和写入表单数据。 默认情况下,此转换器读取和写入 `application/x-www-form-urlencoded`媒体类型。表单数据从`MultiValueMap`读取并写入。 | 801 | | `ByteArrayHttpMessageConverter` | 一个`HttpMessageConverter`实现,可以从HTTP请求和响应中读取和写入字节数组。 默认情况下,此转换器支持所有媒体类型(`*/*`),并使用`Content-Type` 为`application/octet-stream`进行写入。 您可以通过设置`supportedMediaTypes`属性并覆盖`getContentType(byte[])`来覆盖它。 | 802 | | `MarshallingHttpMessageConverter` | 一个`HttpMessageConverter`实现,可以使用`org.springframework.oxm`包中的Spring的`Marshaller`和`Unmarshaller`抽象来读写XML。 该转换器需要`Marshaller`和`Unmarshaller`才能使用。 您可以通过构造函数或bean属性注入这些。 默认情况下,此转换器支持`text/xml`和 `application/xml`。 | 803 | | `MappingJackson2HttpMessageConverter` | 一个`HttpMessageConverter`实现,可以使用Jackson的`ObjectMapper`读写JSON。您可以根据需要通过使用Jackson提供的注解来自定义JSON映射。 当您需要进一步控制时(对于需要为特定类型提供自定义JSON序列化器/反序列化器的情况),您可以通过`ObjectMapper`属性注入自定义`ObjectMapper`。 默认情况下,此转换器支持`application/json`.。 | 804 | | `MappingJackson2XmlHttpMessageConverter` | 一个`HttpMessageConverter`实现,可以使用 [Jackson XML](https://github.com/FasterXML/jackson-dataformat-xml)扩展的 `XmlMapper`读写XML。 您可以根据需要通过使用JAXB或Jackson提供的注解来自定义XML映射。 当您需要进一步控制时(对于需要为特定类型提供自定义XML序列化器/反序列化器的情况),您可以通过`ObjectMapper` 属性注入自定义`XmlMapper`。 默认情况下,此转换器支持`application/xml`。 | 805 | | `SourceHttpMessageConverter` | 一个`HttpMessageConverter`实现,可以从HTTP请求和响应中读取和写入`javax.xml.transform.Source`。 仅支持`DOMSource`,`SAXSource`和`StreamSource`。 默认情况下,此转换器支持`text/xml` 和 `application/xml`。 | 806 | | `BufferedImageHttpMessageConverter` | 一个`HttpMessageConverter`实现,可以从HTTP请求和响应中读取和写入 `java.awt.image.BufferedImage`。 此转换器读取和写入Java I/O API支持的媒体类型。| 807 | 808 | 809 | 810 | ##### [](#rest-template-jsonview)Jackson JSON 视图 811 | 812 | 您可以指定[Jackson JSON视图](http://wiki.fasterxml.com/JacksonJsonViews)以仅序列化对象属性的子集,如以下示例所示: 813 | 814 | ```java 815 | MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23")); 816 | value.setSerializationView(User.WithoutPasswordView.class); 817 | 818 | RequestEntity requestEntity = 819 | RequestEntity.post(new URI("http://example.com/user")).body(value); 820 | 821 | ResponseEntity response = template.exchange(requestEntity, String.class); 822 | ``` 823 | 824 | 825 | 826 | ##### [](#rest-template-multipart)Multipart 827 | 828 | 要发送多部分数据,您需要提供`MultiValueMap` ,其值是表示部件内容的`Object`实例或表示部件的内容和标头的`HttpEntity` 实例。 `MultipartBodyBuilder`提供了一个方便的API来准备多部分请求,如下例所示: 829 | 830 | ```java 831 | MultipartBodyBuilder builder = new MultipartBodyBuilder(); 832 | builder.part("fieldPart", "fieldValue"); 833 | builder.part("filePart", new FileSystemResource("...logo.png")); 834 | builder.part("jsonPart", new Person("Jason")); 835 | 836 | MultiValueMap> parts = builder.build(); 837 | ``` 838 | 839 | 在大多数情况下,您不必为每个部件指定`Content-Type` 。内容类型是根据选择用于序列化它的 `HttpMessageConverter`自动确定的,或者在有资源的情况下,基于文件扩展名自动确定。 如有必要,您可以通过其中一个重载的构建器`part` 方法显式提供要用于每个部件的 `MediaType`。 840 | 841 | 一旦`MultiValueMap`准备就绪,您可以将其传递给`RestTemplate`,如以下示例所示: 842 | 843 | ```java 844 | MultipartBodyBuilder builder = ...; 845 | template.postForObject("http://example.com/upload", builder.build(), Void.class); 846 | ``` 847 | 848 | 如果`MultiValueMap`包含至少一个非`String`值,该值也可以表示常规表单数据(即`application/x-www-form-urlencoded`),则无需将 `Content-Type`设置为`multipart/form-data`。 当您使用确保`HttpEntity` 包装器的 `MultipartBodyBuilder`时,情况总是如此。 849 | 850 | 851 | 852 | #### [](#rest-async-resttemplate)1.8.2. 使用 `AsyncRestTemplate` (已废弃) 853 | 854 | 不推荐使用`AsyncRestTemplate`。 对于您可能考虑使用`AsyncRestTemplate`的所有用例,请改用[WebClient](web-reactive.html#webflux-client) 。 --------------------------------------------------------------------------------