diff --git a/README.md b/README.md index fa4d537d7b56fb15e0086d005f416672c895bf90..9360d0f6a50eb6c8d8676bdd0be5751372ba039b 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ ##基本说明 -Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大量的adoc子文件,该文件在`src/asciidoc/chapter`路径下。 +Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大量的adoc子文件,该文件在`src/asciidoc/chapter`路径下。 -每一个adco文件内容在20行~200行左右,行数少的文件纯翻译内容较多,行数多的文件包含大量不需要进行翻译的代码。 +每一个adco文件内容在20行~200行左右,行数少的文件纯翻译内容较多,行数多的文件包含大量不需要进行翻译的代码。 ##参与翻译 @@ -24,17 +24,17 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 ##I. 全部章节(加粗内容为已分配的章节) -- 1.introduction.adoc - +- 1.introduction.adoc - - **【完成】2.spring-whats-new.adoc - [isea533](http://blog.csdn.net/isea533)** -- 2.1.spring-whats-new.adoc - +- ** 2.1.spring-whats-new.adoc -[厉害吧24中](http://blog.csdn.net/liang101x)** - **【完成】3.beans.adoc - [isea533](http://blog.csdn.net/isea533)** - **【完成】3.1.beans.adoc(5.1. Introduction&5.2. Container overview) - [isea533](http://blog.csdn.net/isea533)** - **【完成】3.2.beans.adoc(5.3. Bean overview) - [isea533](http://blog.csdn.net/isea533)** - **【完成】3.3.beans.adoc(5.4. Dependencies) - [reeco](http://my.oschina.net/reeco)** -- 3.4.beans.adoc(5.5. Bean scopes) - -- 3.4.1.beans.adoc(5.5. Bean scopes) - -- 3.4.2.beans.adoc(5.5. Bean scopes) - -- 3.4.3.beans.adoc(5.5. Bean scopes) - +- **【完成】3.4.beans.adoc(5.5. Bean scopes) - [bliver](http://my.oschina.net/bliver)** 第二段可能翻译的不太贴切,所以保留了原文 +- **【完成】3.4.1.beans.adoc(5.5. Bean scopes) - [请输入你的昵称](http://git.oschina.net/wangchengsi) +- **【完成】3.4.2.beans.adoc(5.5. Bean scopes) - [请输入你的昵称](http://git.oschina.net/wangchengsi) +- **【完成】3.4.3.beans.adoc(5.5. Bean scopes) - [请输入你的昵称](http://git.oschina.net/wangchengsi) - 3.4.4.beans.adoc(5.5. Bean scopes) - - **【完成】3.5.beans.adoc(5.6. Customizing the nature of a bean) - [avvei](http://my.oschina.net/avvei)** - **【完成】3.6.beans.adoc(5.7. Bean definition inheritance) - [令狐流水](http://my.oschina.net/linghuliushui)** @@ -43,11 +43,11 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 3.7.2.beans.adoc(5.8. Container Extension Points) - - 3.7.3.beans.adoc(5.8. Container Extension Points) - - 3.7.4.beans.adoc(5.8. Container Extension Points) - -- 3.8.beans.adoc(5.9. Annotation-based container configuration) -- 3.8.1.beans.adoc(5.9. Annotation-based container configuration) -- 3.8.2.beans.adoc(5.9. Annotation-based container configuration) -- 3.8.3.beans.adoc(5.9. Annotation-based container configuration) -- 3.8.4.beans.adoc(5.9. Annotation-based container configuration) +- 3.8.beans.adoc(5.9. Annotation-based container configuration)-[cloudeye]翻译中 +- 3.8.1.beans.adoc(5.9. Annotation-based container configuration)-[cloudeye]翻译中 +- 3.8.2.beans.adoc(5.9. Annotation-based container configuration)-[cloudeye]翻译中 +- 3.8.3.beans.adoc(5.9. Annotation-based container configuration)-[cloudeye]翻译中 +- 3.8.4.beans.adoc(5.9. Annotation-based container configuration)-[cloudeye]翻译中 - 3.9.beans.adoc(5.10. Classpath scanning and managed components) - - 3.9.1.beans.adoc(5.10. Classpath scanning and managed components) - - 3.9.2.beans.adoc(5.10. Classpath scanning and managed components) - @@ -67,9 +67,9 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - **【完成】3.12.beans.adoc(5.13. Environment abstraction)- [isea533](http://blog.csdn.net/isea533)** - **3.12.1.beans.adoc(5.13. Environment abstraction)- [isea533](http://blog.csdn.net/isea533)** - **【完成】3.13.beans.adoc(5.14. Registering a LoadTimeWeaver)- [isea533](http://blog.csdn.net/isea533)** -- 3.14.beans.adoc(5.15. Additional Capabilities of the ApplicationContext) - +- **3.14.beans.adoc(5.15. Additional Capabilities of the ApplicationContext) - [tequlia2pop](http://git.oschina.net/tequlia2pop)** - 3.14.1.beans.adoc(5.15. Additional Capabilities of the ApplicationContext) - -- 3.14.2.beans.adoc(5.15. Additional Capabilities of the ApplicationContext) - +- **3.14.2.beans.adoc(5.15. Additional Capabilities of the ApplicationContext) - [tequlia2pop](http://git.oschina.net/tequlia2pop)** - 3.15.beans.adoc(5.16. The BeanFactory) - - **【完成】4.resources.adoc - [tianya](tianyatianya-PC)** - **【完成】5.validation.adoc - [jassen](http://git.oschina.net/thinapple)** @@ -86,13 +86,13 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 5.11.validation.adoc - - 5.12.validation.adoc - - **【完成】7.expressions.adoc - [Ji.K'](https://jik92.com/)** -- 8.aop.adoc - -- 8.1.aop.adoc - -- 8.2.aop.adoc - -- 8.2.1.aop.adoc - -- 8.2.2.aop.adoc - -- 8.2.3.aop.adoc - -- 8.3.aop.adoc - +- **【完成】8.aop.adoc - [ultrang](http://ultrang.iteye.com)** +- **8.1.aop.adoc -[ultrang](http://ultrang.iteye.com)** +- **8.2.aop.adoc -[ultrang](http://ultrang.iteye.com)** +- **8.2.1.aop.adoc -[ultrang](http://ultrang.iteye.com)** +- **8.2.2.aop.adoc -[ultrang](http://ultrang.iteye.com)** +- **8.2.3.aop.adoc -[ultrang](http://ultrang.iteye.com)** +- 8.3.aop.adoc -[菜鸟-程序猿]翻译中 - 8.3.1.aop.adoc - - 8.3.2.aop.adoc - - 8.3.3.aop.adoc - @@ -115,7 +115,7 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 8.14.aop.adoc - - 8.15.aop.adoc - - 8.15.1.aop.adoc - -- 9.aop-api.adoc(10.1. Introduction) - +- 9.aop-api.adoc(10.1. Introduction) - - 9.1.aop-api.adoc(10.2. Pointcut API in Spring) - - 9.1.1.aop-api.adoc(10.2. Pointcut API in Spring) - - **【翻译不完整,并且加入了无关的时间】9.2.aop-api.adoc(10.3. Advice API in Spring) - [陈鹏烨](http://my.oschina.net/u/1450455)** @@ -123,9 +123,9 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 9.3.1.aop-api.adoc(10.4. Advisor API in Spring) - - 9.3.2.aop-api.adoc(10.4. Advisor API in Spring) - - 9.3.3.aop-api.adoc(10.4. Advisor API in Spring) - -- **【完成】9.4.aop-api.adoc(10.6. Concise proxy definitions) - [阿信](http://my.oschina.net/songxinqiang)** -- **【完成】9.5.aop-api.adoc(10.7. Creating AOP proxies) - [阿信](http://my.oschina.net/songxinqiang)** -- **【完成】9.6.aop-api.adoc(10.8. Manipulating advised objects) - [阿信](http://my.oschina.net/songxinqiang)** +- **【完成】9.4.aop-api.adoc(10.6. Concise proxy definitions) - [阿信sxq](http://my.oschina.net/songxinqiang)** +- **【完成】9.5.aop-api.adoc(10.7. Creating AOP proxies) - [阿信sxq](http://my.oschina.net/songxinqiang)** +- **【完成】9.6.aop-api.adoc(10.8. Manipulating advised objects) - [阿信sxq](http://my.oschina.net/songxinqiang)** - 9.7.aop-api.adoc(10.9. Using the "auto-proxy" facility) - - 9.7.1.aop-api.adoc(10.9. Using the "auto-proxy" facility) - - 9.7.2.aop-api.adoc(10.9. Using the "auto-proxy" facility) - @@ -152,18 +152,18 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 10.4.2.6.testing.adoc(11.3.5.4. DI) - - 10.4.2.7.testing.adoc(11.3.5.4. DI) - - 10.4.2.8.testing.adoc(11.3.5.4. DI) - -- 10.5.testing.adoc(11.3.6. Spring MVC Test Framework) - -- 10.5.1.testing.adoc(11.3.6. Spring MVC Test Framework) - -- 10.5.2.testing.adoc(11.3.6. Spring MVC Test Framework) - -- 10.5.3.testing.adoc(11.3.6. Spring MVC Test Framework) - +- **【完成】10.5.testing.adoc(11.3.6. Spring MVC Test Framework) - [请输入你的昵称](http://git.oschina.net/wangchengsi) +- **【完成】10.5.1.testing.adoc(11.3.6. Spring MVC Test Framework) - [请输入你的昵称](http://git.oschina.net/wangchengsi) +- **【完成】10.5.2.testing.adoc(11.3.6. Spring MVC Test Framework) - [请输入你的昵称](http://git.oschina.net/wangchengsi) +- **【完成】10.5.3.testing.adoc(11.3.6. Spring MVC Test Framework) - [请输入你的昵称](http://git.oschina.net/wangchengsi) - 10.5.4.testing.adoc(11.3.6. Spring MVC Test Framework) - - 10.6.testing.adoc(11.3.7. PetClinic Example) - -- **【完成】11.spring-data-tier.adoc - [阿信](http://my.oschina.net/songxinqiang)** -- 12.dao.adoc - +- **【完成】11.spring-data-tier.adoc - [阿信sxq](http://my.oschina.net/songxinqiang)** +- **【完成】12.dao.adoc - [ultrang](http://ultrang.iteye.com)** - **【完成】13.jdbc.adoc - [一宁](http://my.oschina.net/u/243914)** -- 13.1.jdbc.adoc - -- 13.2.jdbc.adoc - -- 13.3.jdbc.adoc - +- **13.1.jdbc.adoc - [一宁](http://my.oschina.net/u/243914)** +- **【完成】13.2.jdbc.adoc - [路边乞丐](https://gitee.com/runME)** +- **13.3.jdbc.adoc - [一宁](http://my.oschina.net/u/243914)** - 13.4.jdbc.adoc - - 13.5.jdbc.adoc - - 13.6.jdbc.adoc - @@ -200,25 +200,25 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 16.3.spring-web.adoc - - 16.4.spring-web.adoc - - 16.5.spring-web.adoc - -- 16.6.spring-web.adoc - +- **【完成】16.6.spring-web.adoc - [tequlia2pop](http://git.oschina.net/tequlia2pop)** - 16.7.spring-web.adoc - - 16.8.spring-web.adoc - -- 16.9.spring-web.adoc - +- **【完成】16.9.spring-web.adoc - [tequlia2pop](http://git.oschina.net/tequlia2pop)** - 16.10.spring-web.adoc - - 16.11.spring-web.adoc - -- 16.12.spring-web.adoc - -- 16.13.spring-web.adoc - -- 16.14.spring-web.adoc - +- **【完成】16.12.spring-web.adoc - [tequlia2pop](http://git.oschina.net/tequlia2pop)** +- **【完成】16.13.spring-web.adoc - [tequlia2pop](http://git.oschina.net/tequlia2pop)** +- **【完成】16.14.spring-web.adoc - [tequlia2pop](http://git.oschina.net/tequlia2pop)** - 16.15.spring-web.adoc - - **【完成】17.view.adoc - [顾浩](http://my.oschina.net/dagmom)** -- 17.1.view.adoc - +- **【完成】17.1.view.adoc - [bliver](http://my.oschina.net/bliver)** - 17.2.view.adoc - - 17.3.view.adoc - - 17.4.view.adoc - - 17.5.view.adoc - - 17.6.view.adoc - -- 17.7.view.adoc - -- 17.8.view.adoc - +- **【完成】17.7.view.adoc - [阿信sxq](http://my.oschina.net/songxinqiang)** +- **【完成】17.8.view.adoc - [阿信sxq](http://my.oschina.net/songxinqiang)** - 17.9.view.adoc - - 17.10.view.adoc - - 17.11.view.adoc - @@ -232,9 +232,9 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 17.19.view.adoc - - 17.20.view.adoc - - 17.21.view.adoc - -- **【完成】18.web-integration.adoc - [阿信](http://my.oschina.net/songxinqiang)** +- **【完成】18.web-integration.adoc - [阿信sxq](http://my.oschina.net/songxinqiang)** - 19.portlet.adoc - -- 19.1.portlet.adoc - +- **19.1.portlet.adoc - [Symars](http://www.symars.cn)** - 19.2.portlet.adoc - - 19.3.portlet.adoc - - 19.4.portlet.adoc - @@ -260,8 +260,8 @@ Spring文档为adoc格式,文件已经按基本的章节和内容拆分为大 - 21.7.spring-integration.adoc - - 21.8.spring-integration.adoc - - 21.9.spring-integration.adoc - -- 21.10.spring-integration.adoc - -- 21.11.spring-integration.adoc - +- **21.10.spring-integration.adoc - [isea533](http://blog.csdn.net/isea533)** +- **21.11.spring-integration.adoc - [isea533](http://blog.csdn.net/isea533)** - 21.12.spring-integration.adoc - - 21.13.spring-integration.adoc - - 22.ejb.adoc - @@ -482,10 +482,10 @@ Spring项目使用的**Gradle**,编译文档也需要用到**Gradle**,所以 # utf8 Encoding.default_external = Encoding::UTF_8 - + # Flag to indicate whether encoding of external strings needs to be forced to UTF-8 # _All_ input data must be force encoded to UTF-8 if Encoding.default_external is *not* UTF-8 - # Address failures performing string operations that are reported as "invalid byte sequence in US-ASCII" + # Address failures performing string operations that are reported as "invalid byte sequence in US-ASCII" # Ruby 1.8 doesn't seem to experience this problem (perhaps because it isn't validating the encodings) FORCE_ENCODING = RUBY_VERSION > '1.9' && Encoding.default_external != Encoding::UTF_8 diff --git a/src/asciidoc/chapter/1.introduction.adoc b/src/asciidoc/chapter/1.introduction.adoc index 5f1c6653913e770e6bdb3f20b795d09a868c607a..234e0c5aac56dc71b1d1fa3e4f7603f856007161 100644 --- a/src/asciidoc/chapter/1.introduction.adoc +++ b/src/asciidoc/chapter/1.introduction.adoc @@ -1,762 +1,737 @@ -= Spring Framework Reference Documentation -Rod Johnson; Juergen Hoeller; Keith Donald; Colin Sampaleanu; Rob Harrop; Thomas Risberg; Alef Arendsen; Darren Davison; Dmitriy Kopylenko; Mark Pollack; Thierry Templier; Erwin Vervaet; Portia Tung; Ben Hale; Adrian Colyer; John Lewis; Costin Leau; Mark Fisher; Sam Brannen; Ramnivas Laddad; Arjen Poutsma; Chris Beams; Tareq Abedrabbo; Andy Clement; Dave Syer; Oliver Gierke; Rossen Stoyanchev; Phillip Webb; Rob Winch; Brian Clozel; Stephane Nicoll; Sebastien Deleuze - - -:javadoc-baseurl: http://docs.spring.io/spring/docs/current/javadoc-api - -[[spring-introduction]] -= Spring 框架概述 - -[partintro] --- -The Spring Framework is a lightweight solution and a potential one-stop-shop for -building your enterprise-ready applications. However, Spring is modular, allowing you to -use only those parts that you need, without having to bring in the rest. You can use the -IoC container, with any web framework on top, but you can also use only the -<> or the <>. The Spring Framework supports declarative transaction management, remote access -to your logic through RMI or web services, and various options for persisting your data. -It offers a full-featured <>, and enables you to -integrate <> transparently into your software. - -Spring is designed to be non-intrusive, meaning that your domain logic code generally -has no dependencies on the framework itself. In your integration layer (such as the data -access layer), some dependencies on the data access technology and the Spring libraries -will exist. However, it should be easy to isolate these dependencies from the rest of -your code base. - -This document is a reference guide to Spring Framework features. If you have any -requests, comments, or questions on this document, please post them on the -https://groups.google.com/forum/#!forum/spring-framework-contrib[user mailing -list]. Questions on the Framework itself should be asked on StackOverflow -(see https://spring.io/questions[]). --- - - - - - -[[overview-getting-started-with-spring]] -== Getting Started With Spring -This reference guide provides detailed information about the Spring Framework. -It provides comprehensive documentation for all features, as well as some background -about the underlying concepts (such as __"Dependency Injection"__) that Spring has -embraced. - -If you are just getting started with Spring, you may want to begin with the lighter -https://spring.io/guides["Getting Started"] guides that are available from -https://spring.io. As well as being easier to digest, these guide are very -__task focused__. They also cover other projects from the Spring portfolio that you might - want to consider when solving a particular problem. - -https://spring.io/guides/gs/rest-service/[Getting Started Building a RESTful Web Service] -would be an excellent first choice to get your feet wet. - - - - - -[[overview]] -== Introduction to Spring Framework -Spring Framework is a Java platform that provides comprehensive infrastructure support -for developing Java applications. Spring handles the infrastructure so you can focus on -your application. - -Spring enables you to build applications from "plain old Java objects" (POJOs) and to -apply enterprise services non-invasively to POJOs. This capability applies to the Java -SE programming model and to full and partial Java EE. - -Examples of how you, as an application developer, can use the Spring platform advantage: - -* Make a Java method execute in a database transaction without having to deal with - transaction APIs. -* Make a local Java method a remote procedure without having to deal with remote APIs. -* Make a local Java method a management operation without having to deal with JMX APIs. -* Make a local Java method a message handler without having to deal with JMS APIs. - - - - -[[overview-dependency-injection]] -=== Dependency Injection and Inversion of Control - -[[background-ioc]] -.Background -**** -"__The question is, what aspect of control are [they] inverting?__" Martin Fowler posed -this question about Inversion of Control (IoC) on his site in 2004. Fowler suggested -renaming the principle to make it more self-explanatory and came up with __Dependency -Injection__. - -For insight into IoC and DI, refer to Fowler's article at -http://martinfowler.com/articles/injection.html[http://martinfowler.com/articles/injection.html]. -**** - -Java applications -- a loose term that runs the gamut from constrained applets to n-tier -server-side enterprise applications -- typically consist of objects that collaborate to -form the application proper. Thus the objects in an application have __dependencies__ on -each other. - -Although the Java platform provides a wealth of application development functionality, -it lacks the means to organize the basic building blocks into a coherent whole, leaving -that task to architects and developers. True, you can use design patterns such -as __Factory__, __Abstract Factory__, __Builder__, __Decorator__, and __Service Locator__ -to compose the various classes and object instances that make up an application. -However, these patterns are simply that: best practices given a name, with a description -of what the pattern does, where to apply it, the problems it addresses, and so forth. -Patterns are formalized best practices that __you must implement yourself__ in your -application. - -The Spring Framework __Inversion of Control__ (IoC) component addresses this concern by -providing a formalized means of composing disparate components into a fully working -application ready for use. The Spring Framework codifies formalized design patterns as -first-class objects that you can integrate into your own application(s). Numerous -organizations and institutions use the Spring Framework in this manner to engineer -robust, __maintainable__ applications. - - - - -[[overview-modules]] -=== Modules -The Spring Framework consists of features organized into about 20 modules. These modules -are grouped into Core Container, Data Access/Integration, Web, AOP (Aspect Oriented -Programming), Instrumentation, Messaging, and Test, as shown in the following diagram. - -.Overview of the Spring Framework -image::images/spring-overview.png[width=400] - -The following sections list the available modules for each feature along with their -artifact name and the topics they cover. Artifact names correlate to _artifact IDs_ used -in <>. - - -[[overview-core-container]] -==== Core Container -The <> consists of the `spring-core`, -`spring-beans`, `spring-context`, and `spring-expression` (Spring Expression Language) -modules. - -The `spring-core` and `spring-beans` modules <>, including the IoC and Dependency Injection features. The -`BeanFactory` is a sophisticated implementation of the factory pattern. It removes the -need for programmatic singletons and allows you to decouple the configuration and -specification of dependencies from your actual program logic. - -The <> (`spring-context`) module builds on the solid -base provided by the <> modules: it is a means to -access objects in a framework-style manner that is similar to a JNDI registry. The -Context module inherits its features from the Beans module and adds support for -internationalization (using, for example, resource bundles), event propagation, resource -loading, and the transparent creation of contexts by, for example, a Servlet container. -The Context module also supports Java EE features such as EJB, JMX, and basic remoting. -The `ApplicationContext` interface is the focal point of the Context module. - -The `spring-expression` module provides a powerful <> for querying and manipulating an object graph at runtime. It is an extension -of the unified expression language (unified EL) as specified in the JSP 2.1 -specification. The language supports setting and getting property values, property -assignment, method invocation, accessing the content of arrays, collections and indexers, -logical and arithmetic operators, named variables, and retrieval of objects by name from -Spring's IoC container. It also supports list projection and selection as well as common -list aggregations. - - -[[overview-aop-instrumentation]] -==== AOP and Instrumentation -The `spring-aop` module provides an <> Alliance-compliant -aspect-oriented programming implementation allowing you to define, for example, -method interceptors and pointcuts to cleanly decouple code that implements functionality -that should be separated. Using source-level metadata functionality, you can also -incorporate behavioral information into your code, in a manner similar to that of .NET -attributes. - -The separate `spring-aspects` module provides integration with AspectJ. - -The `spring-instrument` module provides class instrumentation support and classloader -implementations to be used in certain application servers. - - -[[overview-messaging]] -==== Messaging -Spring Framework 4 includes a `spring-messaging` module with key abstractions from the -_Spring Integration_ project such as `Message`, `MessageChannel`, `MessageHandler`, and -others to serve as a foundation for messaging-based applications. The module also -includes a set of annotations for mapping messages to methods, similar to the Spring MVC -annotation based programming model. - - -[[overview-data-access]] -==== Data Access/Integration -The __Data Access/Integration__ layer consists of the JDBC, ORM, OXM, JMS, and -Transaction modules. - -The `spring-jdbc` module provides a <>-abstraction layer that -removes the need to do tedious JDBC coding and parsing of database-vendor specific error -codes. - -The `spring-tx` module supports <> -management for classes that implement special interfaces and for __all your POJOs (Plain -Old Java Objects)__. - -The `spring-orm` module provides integration layers for popular -<> APIs, including <>, -<>, and <>. Using the `spring-orm` module you can -use all of these O/R-mapping frameworks in combination with all of the other features -Spring offers, such as the simple declarative transaction management feature mentioned -previously. - -The `spring-oxm` module provides an abstraction layer that supports <> implementations such as JAXB, Castor, XMLBeans, JiBX and XStream. - -The `spring-jms` module (<>) contains features for producing and -consuming messages. Since Spring Framework 4.1, it provides integration with the -`spring-messaging` module. - - -[[overview-web]] -==== Web -The __Web__ layer consists of the `spring-web`, `spring-webmvc`, `spring-websocket`, and -`spring-webmvc-portlet` modules. - -The `spring-web` module provides basic web-oriented integration features such as -multipart file upload functionality and the initialization of the IoC container using -Servlet listeners and a web-oriented application context. It also contains the -web-related parts of Spring's remoting support. - -The `spring-webmvc` module (also known as the __Web-Servlet__ module) contains Spring's -model-view-controller (<>) implementation for web applications. -Spring's MVC framework provides a clean separation between domain model code and web -forms and integrates with all of the other features of the Spring Framework. - -The `spring-webmvc-portlet` module (also known as the __Web-Portlet__ module) provides -the MVC implementation to be used in a Portlet environment and mirrors the functionality -of the `spring-webmvc` module. - - -[[overview-testing]] -==== Test -The `spring-test` module supports the <> and -<> of Spring components with JUnit or TestNG. It -provides consistent <> of Spring -++ApplicationContext++s and <> of those -contexts. It also provides <> that you can use to test your -code in isolation. - - - -[[overview-usagescenarios]] -=== Usage scenarios -The building blocks described previously make Spring a logical choice in many scenarios, -from applets to full-fledged enterprise applications that use Spring's transaction -management functionality and web framework integration. - -.Typical full-fledged Spring web application -image::images/overview-full.png[width=400] - -Spring's <> make -the web application fully transactional, just as it would be if you used EJB -container-managed transactions. All your custom business logic can be implemented with -simple POJOs and managed by Spring's IoC container. Additional services include support -for sending email and validation that is independent of the web layer, which lets you -choose where to execute validation rules. Spring's ORM support is integrated with JPA, -Hibernate and and JDO; for example, when using Hibernate, you can continue to use -your existing mapping files and standard Hibernate `SessionFactory` configuration. Form -controllers seamlessly integrate the web-layer with the domain model, removing the need -for `ActionForms` or other classes that transform HTTP parameters to values for your -domain model. - -.Spring middle-tier using a third-party web framework -image::images/overview-thirdparty-web.png[width=400] - -Sometimes circumstances do not allow you to completely switch to a different framework. -The Spring Framework does __not__ force you to use everything within it; it is not an -__all-or-nothing__ solution. Existing front-ends built with Struts, Tapestry, JSF -or other UI frameworks can be integrated with a Spring-based middle-tier, which allows -you to use Spring transaction features. You simply need to wire up your business logic -using an `ApplicationContext` and use a `WebApplicationContext` to integrate your web -layer. - -.Remoting usage scenario -image::images/overview-remoting.png[width=400] - -When you need to access existing code through web services, you can use Spring's -`Hessian-`, `Burlap-`, `Rmi-` or `JaxRpcProxyFactory` classes. Enabling remote access to -existing applications is not difficult. - -.EJBs - Wrapping existing POJOs -image::images/overview-ejb.png[width=400] - -The Spring Framework also provides an <> for -Enterprise JavaBeans, enabling you to reuse your existing POJOs and wrap them in -stateless session beans for use in scalable, fail-safe web applications that might need -declarative security. - - - -[[dependency-management]] -==== Dependency Management and Naming Conventions -Dependency management and dependency injection are different things. To get those nice -features of Spring into your application (like dependency injection) you need to -assemble all the libraries needed (jar files) and get them onto your classpath at -runtime, and possibly at compile time. These dependencies are not virtual components -that are injected, but physical resources in a file system (typically). The process of -dependency management involves locating those resources, storing them and adding them to -classpaths. Dependencies can be direct (e.g. my application depends on Spring at -runtime), or indirect (e.g. my application depends on `commons-dbcp` which depends on -`commons-pool`). The indirect dependencies are also known as "transitive" and it is -those dependencies that are hardest to identify and manage. - -If you are going to use Spring you need to get a copy of the jar libraries that comprise -the pieces of Spring that you need. To make this easier Spring is packaged as a set of -modules that separate the dependencies as much as possible, so for example if you don't -want to write a web application you don't need the spring-web modules. To refer to -Spring library modules in this guide we use a shorthand naming convention `spring-*` or -`spring-*.jar,` where `*` represents the short name for the module (e.g. `spring-core`, -`spring-webmvc`, `spring-jms`, etc.). The actual jar file name that you use is normally -the module name concatenated with the version number -(e.g. __spring-core-{spring-version}.jar__). - -Each release of the Spring Framework will publish artifacts to the following places: - -* Maven Central, which is the default repository that Maven queries, and does not - require any special configuration to use. Many of the common libraries that Spring - depends on also are available from Maven Central and a large section of the Spring - community uses Maven for dependency management, so this is convenient for them. The - names of the jars here are in the form `spring-*-.jar` and the Maven groupId - is `org.springframework`. -* In a public Maven repository hosted specifically for Spring. In addition to the final - GA releases, this repository also hosts development snapshots and milestones. The jar - file names are in the same form as Maven Central, so this is a useful place to get - development versions of Spring to use with other libraries deployed in Maven Central. - This repository also contains a bundle distribution zip file that contains all Spring - jars bundled together for easy download. - -So the first thing you need to decide is how to manage your dependencies: we generally -recommend the use of an automated system like Maven, Gradle or Ivy, but you can also do -it manually by downloading all the jars yourself. We provide detailed instructions later -in this chapter. - - -[[overview-spring-dependencies]] -===== Spring Dependencies and Depending on Spring -Although Spring provides integration and support for a huge range of enterprise and -other external tools, it intentionally keeps its mandatory dependencies to an absolute -minimum: you shouldn't have to locate and download (even automatically) a large number -of jar libraries in order to use Spring for simple use cases. For basic dependency -injection there is only one mandatory external dependency, and that is for logging (see -below for a more detailed description of logging options). - -Next we outline the basic steps needed to configure an application that depends on -Spring, first with Maven and then with Gradle and finally using Ivy. In all cases, if -anything is unclear, refer to the documentation of your dependency management system, or -look at some sample code - Spring itself uses Gradle to manage dependencies when it is -building, and our samples mostly use Gradle or Maven. - - -[[overview-maven-dependency-management]] -===== Maven Dependency Management -If you are using http://maven.apache.org/[Maven] for dependency management you don't even -need to supply the logging dependency explicitly. For example, to create an application -context and use dependency injection to configure an application, your Maven dependencies -will look like this: - -[source,xml,indent=0] -[subs="verbatim,quotes,attributes"] ----- - - - org.springframework - spring-context - {spring-version} - runtime - - ----- - -That's it. Note the scope can be declared as runtime if you don't need to compile -against Spring APIs, which is typically the case for basic dependency injection use -cases. - -The example above works with the Maven Central repository. To use the Spring Maven -repository (e.g. for milestones or developer snapshots), you need to specify the -repository location in your Maven configuration. For full releases: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - io.spring.repo.maven.release - http://repo.spring.io/release/ - false - - ----- - -For milestones: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - io.spring.repo.maven.milestone - http://repo.spring.io/milestone/ - false - - ----- - -And for snapshots: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - io.spring.repo.maven.snapshot - http://repo.spring.io/snapshot/ - true - - ----- - - -[[overview-maven-bom]] -===== Maven "Bill Of Materials" Dependency ===== -It is possible to accidentally mix different versions of Spring JARs when using Maven. -For example, you may find that a third-party library, or another Spring project, -pulls in a transitive dependency to an older release. If you forget to explicitly declare -a direct dependency yourself, all sorts of unexpected issues can arise. - -To overcome such problems Maven supports the concept of a "bill of materials" (BOM) -dependency. You can import the `spring-framework-bom` in your `dependencyManagement` -section to ensure that all spring dependencies (both direct and transitive) are at -the same version. - -[source,xml,indent=0] -[subs="verbatim,quotes,attributes"] ----- - - - - org.springframework - spring-framework-bom - {spring-version} - pom - import - - - ----- - -An added benefit of using the BOM is that you no longer need to specify the `` -attribute when depending on Spring Framework artifacts: - -[source,xml,indent=0] -[subs="verbatim,quotes,attributes"] ----- - - - org.springframework - spring-context - - - org.springframework - spring-web - - ----- - - -[[overview-gradle-dependency-management]] -===== Gradle Dependency Management -To use the Spring repository with the http://www.gradle.org/[Gradle] build system, -include the appropriate URL in the `repositories` section: - -[source,groovy,indent=0] -[subs="verbatim,quotes"] ----- - repositories { - mavenCentral() - // and optionally... - maven { url "http://repo.spring.io/release" } - } ----- - -You can change the `repositories` URL from `/release` to `/milestone` or `/snapshot` as -appropriate. Once a repository has been configured, you can declare dependencies in the -usual Gradle way: - -[source,groovy,indent=0] -[subs="verbatim,quotes,attributes"] ----- - dependencies { - compile("org.springframework:spring-context:{spring-version}") - testCompile("org.springframework:spring-test:{spring-version}") - } ----- - - -[[overview-ivy-dependency-management]] -===== Ivy Dependency Management -If you prefer to use http://ant.apache.org/ivy[Ivy] to manage dependencies then there -are similar configuration options. - -To configure Ivy to point to the Spring repository add the following resolver to your -`ivysettings.xml`: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - ----- - -You can change the `root` URL from `/release/` to `/milestone/` or `/snapshot/` as -appropriate. - -Once configured, you can add dependencies in the usual way. For example (in `ivy.xml`): - -[source,xml,indent=0] -[subs="verbatim,quotes,attributes"] ----- - ----- - - -[[overview-distribution-zip]] -===== Distribution Zip Files -Although using a build system that supports dependency management is the recommended -way to obtain the Spring Framework, it is still possible to download a distribution -zip file. - -Distribution zips are published to the Spring Maven Repository (this is just for our -convenience, you don't need Maven or any other build system in order to download them). - -To download a distribution zip open a web browser to -http://repo.spring.io/release/org/springframework/spring and select the appropriate -subfolder for the version that you want. Distribution files end `-dist.zip`, for example -+spring-framework-{spring-version}-RELEASE-dist.zip+. Distributions are also published -for http://repo.spring.io/milestone/org/springframework/spring[milestones] and -http://repo.spring.io/snapshot/org/springframework/spring[snapshots]. - - - -[[overview-logging]] -==== Logging -Logging is a very important dependency for Spring because __a)__ it is the only mandatory -external dependency, __b)__ everyone likes to see some output from the tools they are -using, and __c)__ Spring integrates with lots of other tools all of which have also made -a choice of logging dependency. One of the goals of an application developer is often to -have unified logging configured in a central place for the whole application, including -all external components. This is more difficult than it might have been since there are so -many choices of logging framework. - -The mandatory logging dependency in Spring is the Jakarta Commons Logging API (JCL). We -compile against JCL and we also make JCL `Log` objects visible for classes that extend -the Spring Framework. It's important to users that all versions of Spring use the same -logging library: migration is easy because backwards compatibility is preserved even -with applications that extend Spring. The way we do this is to make one of the modules -in Spring depend explicitly on `commons-logging` (the canonical implementation of JCL), -and then make all the other modules depend on that at compile time. If you are using -Maven for example, and wondering where you picked up the dependency on -`commons-logging`, then it is from Spring and specifically from the central module -called `spring-core`. - -The nice thing about `commons-logging` is that you don't need anything else to make your -application work. It has a runtime discovery algorithm that looks for other logging -frameworks in well known places on the classpath and uses one that it thinks is -appropriate (or you can tell it which one if you need to). If nothing else is available -you get pretty nice looking logs just from the JDK (java.util.logging or JUL for short). -You should find that your Spring application works and logs happily to the console out -of the box in most situations, and that's important. - - -[[overview-not-using-commons-logging]] -===== Not Using Commons Logging -Unfortunately, the runtime discovery algorithm in `commons-logging`, while convenient -for the end-user, is problematic. If we could turn back the clock and start Spring now -as a new project it would use a different logging dependency. The first choice would -probably be the Simple Logging Facade for Java ( http://www.slf4j.org[SLF4J]), which is -also used by a lot of other tools that people use with Spring inside their applications. - -There are basically two ways to switch off `commons-logging`: - -. Exclude the dependency from the `spring-core` module (as it is the only module that - explicitly depends on `commons-logging`) -. Depend on a special `commons-logging` dependency that replaces the library with - an empty jar (more details can be found in the - http://slf4j.org/faq.html#excludingJCL[SLF4J FAQ]) - -To exclude commons-logging, add the following to your `dependencyManagement` section: - -[source,xml,indent=0] -[subs="verbatim,quotes,attributes"] ----- - - - org.springframework - spring-core - {spring-version} - - - commons-logging - commons-logging - - - - ----- - -Now this application is probably broken because there is no implementation of the JCL -API on the classpath, so to fix it a new one has to be provided. In the next section we -show you how to provide an alternative implementation of JCL using SLF4J as an example. - - -[[overview-logging-slf4j]] -===== Using SLF4J -SLF4J is a cleaner dependency and more efficient at runtime than `commons-logging` -because it uses compile-time bindings instead of runtime discovery of the other logging -frameworks it integrates. This also means that you have to be more explicit about what -you want to happen at runtime, and declare it or configure it accordingly. SLF4J -provides bindings to many common logging frameworks, so you can usually choose one that -you already use, and bind to that for configuration and management. - -SLF4J provides bindings to many common logging frameworks, including JCL, and it also -does the reverse: bridges between other logging frameworks and itself. So to use SLF4J -with Spring you need to replace the `commons-logging` dependency with the SLF4J-JCL -bridge. Once you have done that then logging calls from within Spring will be translated -into logging calls to the SLF4J API, so if other libraries in your application use that -API, then you have a single place to configure and manage logging. - -A common choice might be to bridge Spring to SLF4J, and then provide explicit binding -from SLF4J to Log4J. You need to supply 4 dependencies (and exclude the existing -`commons-logging`): the bridge, the SLF4J API, the binding to Log4J, and the Log4J -implementation itself. In Maven you would do that like this - -[source,xml,indent=0] -[subs="verbatim,quotes,attributes"] ----- - - - org.springframework - spring-core - {spring-version} - - - commons-logging - commons-logging - - - - - org.slf4j - jcl-over-slf4j - 1.5.8 - - - org.slf4j - slf4j-api - 1.5.8 - - - org.slf4j - slf4j-log4j12 - 1.5.8 - - - log4j - log4j - 1.2.14 - - ----- - -That might seem like a lot of dependencies just to get some logging. Well it is, but it -__is__ optional, and it should behave better than the vanilla `commons-logging` with -respect to classloader issues, notably if you are in a strict container like an OSGi -platform. Allegedly there is also a performance benefit because the bindings are at -compile-time not runtime. - -A more common choice amongst SLF4J users, which uses fewer steps and generates fewer -dependencies, is to bind directly to http://logback.qos.ch[Logback]. This removes the -extra binding step because Logback implements SLF4J directly, so you only need to depend -on two libraries not four ( `jcl-over-slf4j` and `logback`). If you do that you might -also need to exclude the slf4j-api dependency from other external dependencies (not -Spring), because you only want one version of that API on the classpath. - - -[[overview-logging-log4j]] -===== Using Log4J -Many people use http://logging.apache.org/log4j[Log4j] as a logging framework for -configuration and management purposes. It's efficient and well-established, and in fact -it's what we use at runtime when we build and test Spring. Spring also provides some -utilities for configuring and initializing Log4j, so it has an optional compile-time -dependency on Log4j in some modules. - -To make Log4j work with the default JCL dependency ( `commons-logging`) all you need to -do is put Log4j on the classpath, and provide it with a configuration file ( -`log4j.properties` or `log4j.xml` in the root of the classpath). So for Maven users this -is your dependency declaration: - -[source,xml,indent=0] -[subs="verbatim,quotes,attributes"] ----- - - - org.springframework - spring-core - {spring-version} - - - log4j - log4j - 1.2.14 - - ----- - -And here's a sample log4j.properties for logging to the console: - -[literal] -[subs="verbatim,quotes"] ----- -log4j.rootCategory=INFO, stdout - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n - -log4j.category.org.springframework.beans.factory=DEBUG ----- - -[[overview-native-jcl]] -====== Runtime Containers with Native JCL -Many people run their Spring applications in a container that itself provides an -implementation of JCL. IBM Websphere Application Server (WAS) is the archetype. This -often causes problems, and unfortunately there is no silver bullet solution; simply -excluding `commons-logging` from your application is not enough in most situations. - -To be clear about this: the problems reported are usually not with JCL per se, or even -with `commons-logging`: rather they are to do with binding `commons-logging` to another -framework (often Log4J). This can fail because `commons-logging` changed the way they do -the runtime discovery in between the older versions (1.0) found in some containers and -the modern versions that most people use now (1.1). Spring does not use any unusual -parts of the JCL API, so nothing breaks there, but as soon as Spring or your application -tries to do any logging you can find that the bindings to Log4J are not working. - -In such cases with WAS the easiest thing to do is to invert the class loader hierarchy -(IBM calls it "parent last") so that the application controls the JCL dependency, not -the container. That option isn't always open, but there are plenty of other suggestions -in the public domain for alternative approaches, and your mileage may vary depending on -the exact version and feature set of the container. - - - - += Spring Framework Reference Documentation +Rod Johnson; Juergen Hoeller; Keith Donald; Colin Sampaleanu; Rob Harrop; Thomas Risberg; Alef Arendsen; Darren Davison; Dmitriy Kopylenko; Mark Pollack; Thierry Templier; Erwin Vervaet; Portia Tung; Ben Hale; Adrian Colyer; John Lewis; Costin Leau; Mark Fisher; Sam Brannen; Ramnivas Laddad; Arjen Poutsma; Chris Beams; Tareq Abedrabbo; Andy Clement; Dave Syer; Oliver Gierke; Rossen Stoyanchev; Phillip Webb; Rob Winch; Brian Clozel; Stephane Nicoll; Sebastien Deleuze + + +:javadoc-baseurl: http://docs.spring.io/spring/docs/current/javadoc-api + +[[spring-introduction]] += Spring 框架概述 + +[partintro] +-- +Spring框架是一个轻量级解决方案,也是潜在的开发企业级应用的一站式解决方案.而且Spring也是模块化的,它允许你只用需要的部分,不需要的部分可以舍弃.你可以使用IoC容器,上层使用其他web框架,但你依然可以仅使用 +<>或者 <>. Spring框架支持声明式事务管理,通过RMI或者web服务远程访问你的逻辑代码,及多选的持久化数据方式.它提供一个完整特性的 <>, 且使你能在不影响其它代码的前提下把 +<> 整合进你的应用. + +Spring被设计为非侵入式的,意思是你的域逻辑代码通常和框架本身是没依赖的,在你的整合层(比如数据访问层),会存在一些对数据访问技术和Spring库文件的依赖,但它可以轻易地从你的其它代码中独立出来. + +这篇文档是作为Spring框架特性的参考向导.如果对该文档有任何要求,意见或者问题,请发邮件到到 +https://groups.google.com/forum/#!forum/spring-framework-contrib[user mailing +list]. 如果对框架本身有疑问请前往StackOverflow提问 +(见 https://spring.io/questions[]). +-- + + + + + +[[overview-getting-started-with-spring]] +== 开始使用Spring +这篇参考向导提供了关于Spring框架的详细信息。它提供了对各种特性的完整介绍,以及一些Spring包含的基础概念的背景 (例如 __"Dependency Injection"__). + +如果你想立刻开始使用Spring, 可以从更为简洁的 +https://spring.io/guides["Getting Started"] 参考开始入手, 文档可以从 +https://spring.io 处获得。 此文档不仅更容易理解,而且更加 +__集中于任务__. 文档也包含了来自于Spring的其他工程,在解决一个特定 +问题是你可能会考虑使用这些工程。 + +https://spring.io/guides/gs/rest-service/[开始构建一个 RESTful Web Service] +会是一个很好的开始教程. + + + + + +[[overview]] +== Spring框架概述 +Spring框架是一个基于Java的框架平台,并且为实现Java应用程序提供了全面的基础架构支持。 +你可以专注于你的应用,让Spring来帮助你处理基础架构的问题。 + +Spring使你能够用"plain old Java objects" (POJOs,简单普通的Java对象)来构建应用,并且将企业服务 +用POJO来实现。你可以在Java SE编程模型、全部或者部分的Java EE编程模型中应用它。 + +作为一个应用开发者,以下例子告诉你如何使用Spring平台的功能: + +* 创建一个Java方法,无需关注事务API就可以执行数据库事务。 +* 创建一个本地Java方法,无需关注remote API从而进行远程操作。 +* 创建一个本地Java方法,无需关注JMX API就能实现管理操作。 +* 创建一个本地Java方法,无需关注JMS API就能实现消息处理。 + + + + +[[overview-dependency-injection]] +=== 依赖注入和控制反转 + +[[background-ioc]] +.背景 +**** +"__问题是,对哪个层面的控制进行反转__" Martin Fowler于2004年在他的站点上提出了这个有关控制反转(IoC)的问题。 +Fowler建议对这一原则进行重新命名以便让它变得更加“不言自明”,所以最后选择了__依赖注入__。 + +如果想要了解IoC和DI,可以在 +http://martinfowler.com/articles/injection.html[http://martinfowler.com/articles/injection.html] 参考Fowler的文章。 +**** + +“Java应用”是一个极为宽泛的定义,小到受限制的嵌入程序,大到n层结构的服务器端企业级应用,它包含着相互协作的对象,从而共同构成一个应用。 +因此,程序里的对象彼此之间都有各自的依赖。 + +尽管Java平台提供的很多应用开发的功能,但是它缺少将这些基础组件组织成一个相互协作的整体的方法,最终把这些整合工作交给了架构师或是开发者。 +尽管你可以使用设计模式,例如__工厂模式__,__抽象工厂模式__,__建造者模式__,__装饰__和__服务定位__来将这些不同的类和对象组合起来,从而构建一个应用。 +但是,这些模式仅仅只是:把必要的步骤封装起来,给这些步骤取名字,说明模式的功能,在何处应用,解决了哪些问题等等。 +模式是公式化的实现步骤,你必须在你的应用中手动继承它。 + +Spring框架中的__控制反转__ (IoC)组件部分解决了这个问题,通过提供一种规范化的方式将各个分开的组件组合成一个完全可供使用的应用。 +Spring框架将规范化的设计模型编写为优秀的类,这样你就可以在你的应用中继承它们。 +很多组织和机构都使用Spring框架来设计健壮的,__便于维护__的应用。 + + + + +[[overview-modules]] +=== 模块 +Spring框架的所有特性被组织成20个模块。这些模块被分组成Core Container(核心容器), Data Access/Integration(数据访问/集成), Web(网络端), AOP (Aspect Oriented +Programming,切面编程), Instrumentation, Messaging(消息),和Test(测试), +以下图片显示的就是Spring的各个模块: + +.Spring框架总览 +image::images/spring-overview.png[width=400] + +接下来的部分列出了每个特性的可用模块,同时也说明了组件名称和涵盖的主题。 +组件名称和在 <> 中使用的 _artifact IDs_ 相互关联。 + + +[[overview-core-container]] +==== 核心容器 +<> 包含了 `spring-core` , +`spring-beans` , `spring-context` , and `spring-expression` (Spring表达式语言) +四个模块。 + +`spring-core` 和 `spring-beans` 模块<>, +包括了IoC(控制反转)和Dependency Injection(依赖注入)特性。 +`BeanFactory` 是一个复杂的工厂模式实现类。 + 它让你不必再去自己编写实现类,并且能让你将依赖的配置和声明从你的实际的程序逻辑中分离开来。 + +<> ( `spring-context` )模块建立在<>模块 +提供的基础之上: 它提供了框架式访问对象的方式,类似于JNDI注册。 +Context模块从Beans模块中继承了其所有特性并且为如下功能提供支持:国际化(例如使用资源包), 事件传播, 资源加载和创建上下文,例如Servlet容器。 +Context模块也支持Java EE特性,例如EJB, JMX,和基本的远程处理功能. +`ApplicationContext` 接口是Context模块的核心所在. + +`spring-expression` 模块提供了一种强大的用于在运行时查询操作对象的<>。它是对于在JSP2.1规范中所声明的unified expression语言(统一表达式语言)的扩展。 +这种语言支持设置和修改属性值, 属性分配, 方法调用, 获得数组内容, 集合以及索引, +算术和逻辑运算符, 命名的变量和从Spring IoC容器中根据名称获得对象。它也支持列表投影、列表选择以及列表聚合。 + + +[[overview-aop-instrumentation]] +==== AOP和Instrumentation +`spring-aop` 模块提供了<>(联盟编程) +面向切面的编程实现,例如允许你定义 +拦截器方法和切入点从而将相应的功能代码分离开来。 利用源码中的元数据, 你可以将行为信息加入到你的代码中, 一定程度上类似于.NET属性。 + +独立的 `spring-aspects` 模块集成了AspectJ。 + +`spring-instrument` 模块提供了对类进行仪表化的功能,也提供了类加载器的实现,特定的服务器可能会需要类加载器。 + + +[[overview-messaging]] +==== Messaging +Spring 4框架包含了 `spring-messaging` 模块,包含了 +_Spring Integration_项目的高度抽象,比如 `Message` , `MessageChannel` , `MessageHandler` 等, +它们共同构成了一个基于信息的应用的基础。 +这个模块同时包含了一系列用来将messages映射到方法的注解,类似于Spring MVC中基于编程的注解。 + + +[[overview-data-access]] +==== 数据获取/整合 +数据__整合/获取__层包括JDBC、ORM、OXM、JMS以及 +事务等模块。 + +`spring-jdbc` 提供了一个<>抽象层从而不再需要编写繁琐的JDBC代码,也无需再手动解析基于特定数据库供应商的错误 +代码。 + +`spring-tx` 模块为那些实现特殊接口的类以及所有的 __POJO(普通Java类)__的<>管理提供支持。 + +`spring-orm` 模块为流行的<>的API们提供整合层,包括<>、 +<>以及<>。通过 `spring-orm` 模块你可以同时使用这些O/R-mapping以及 +Spring提供的其他功能,例如上面提到的声明式事务管理。 + +`spring-oxm` 模块提供了一种抽象层为<>的各种实现提供支持,例如JAXB、 Castor、 XMLBeans JiBX 和 XStream。 + +`spring-jms` 模块 (<>)包含生成以及接收信息的特征。在Spring框架4.1以后的版本中,它提供了与 `spring-messaging` 整合的模块。 + + + +[[overview-web]] +==== Web +__Web__层包括 `spring-web` 、`spring-webmvc` 、`spring-websocket` 以及 +`spring-webmvc-portlet` 等模块。 + +`spring-web` 模块提供了很多基础的面向web层的整合,例如文件上传功能以及使用Servlet listener和一个面向web层的 +application context来进行IOC容器的初始化的功能。同时它也包含涉及Spring远程支持的与网络相关的部分。 + +`spring-webmvc` 模块(也被称作__Web-Servlet__模块)提供了Spring在网络应用中对模型-视图-控制器 +(<>)的实现。Spring的MVC框架实现了域模型代码和web表单的完美分离, +同时也实现了与Spring的其他框架的功能的整合。 + + +`spring-webmvc` 模块(也被称作__Web-Servlet__模块)提供了在Portlet环境下的MVC实现并拥有 +`spring-webmvc` 模块的所有功能。 + + + +[[overview-testing]] +==== Test +`spring-test` 模块支持利用JUnit或者TestNGO对Spring组件进行<>以及<>。它能够加载Spring++ApplicationContext++并同时对其进行缓存。它也提供了<>用 +来单独对某段代码进行测试。 + +[[overview-usagescenarios]] +=== Usage scenarios +上面描述的构建模块使得Spring成为在很多应用场景下的合理选择,从applet程序到完备的企业级应用都可以使用Spring的 +事务管理功能以及网络框架整合。 + +.典型的完整Spring web application +image::images/overview-full.png[width=400] + +Spring的<> +使得网络应用是完全基于事务的,能够达到与使用由EJB +容器管理的事务同样的效果。你所有的自定义业务逻辑都可以由 +简单的普通Java类来实现,由Spring的IoC容器来管理。其他的服务包括支持 +发送email,支持独立于web层的数据验证,它能允许你 +在任何地方执行验证规则。Spring的ORM支持包括与JPA、 +Hibernate和JDO的整合;例如,当使用Hibernate时,你可以使用 +现有的映射文件和标准的Hibernate `SessionFactory` 配置。表单控制器 +无缝整合了web层和域模型,使得我们无需 +使用 `ActionForms` 或其他的类来吧HTTP参数转换成域模型 +所需要的值。 + +.使用第三方web框架的中间层 +image::images/overview-thirdparty-web.png[width=400] + +有时候条件不允许你完全切换到一个不同的框架。 +Spring框架__不会__强制你使用其所有的功能;它不是一个 +__要么全用,要么不用__的框架。现有的利用Struts, Tapestry, JSF +或其他UI框架构建的前端页面可以被整合到一个基于Spring的中间层,这将允许你 +使用Spring的事务特性。你只需使用 +`ApplicationContext` 来执行你的业务逻辑并使用 `WebApplicationContext` 来 +整合web层。 + + +.远程使用的应用场景 +image::images/overview-remoting.png[width=400] + +当你需要使用网络服务来获取现有的代码时,你可以使用Spring的 +`Hessian-` , `Burlap-`, `Rmi-` 或者 `JaxRpcProxyFactory` 类。允许远程访问网络应用 +并不是一件困难的事。 + + +.EJB-包装现有的POJOs +image::images/overview-ejb.png[width=400] + +Spring框架同样会提供为企业级JavaBeans提供<>, +这允许你重复使用现有的POJO并在 +无状态会话组件中对它们进行包装,用于构件可扩展的、高容错性的网络应用, +这些网络应用可能要求具有声明性安全。 + + + + +[[dependency-management]] +==== 依赖管理和命名规范 +依赖管理和依赖注入是两个不同的概念。为了在你的应用中使用Spring的功能 +(例如依赖注入) +你需要装配好所有必须的函数库(jar文件),并在程序运行时,或者编译时 +将文件添加到类路径中。这些依赖并不是被注入的虚拟组件, +而是通常位于文件系统中的实体资源。依赖管理的过程涉及到 +对相应的资源进行定位并将 +它们添加到类路径中。依赖可以是直接的(例如应用在运行时依赖Spring),也可以 +是间接的(例如应用依赖 `commons-dbcp` 而 +`commons-dbcp` 依赖 `commons-pool` )。间接的验证也被称为“传递”,而 +间接的验证是最难被鉴别和管理的。 + + +如果要使用Spring,你需要导入包含 +你所需功能的jar包。为了更好地实现这一点,Spring被尽量分离打包成了 +不同的模块。例如如果你不想 +编写一个web应用你就无需使用spring-web模块。为了方便在此指导文件中查阅Spring的库模块, +我们将采用一种便于记忆的命名规范 `spring-*` +或者 `spring-*.jar` ,此处 `*` 代表模块的简写名称(例如 `spring-core`, +`spring-webmvc` , `spring-jms` 等)。实际的jar文件名通常是 +模块名称加上版本号(例如 __spring-core-{spring-version}.jar__)。 + + +每次Spring框架的发行都会将资源公布到下面几个地方: + +* Maven Central,这是Maven进行查询的默认仓库,无需进行 + 任何特殊的配置就可以使用。许多Spring所依赖的通用函数库 + 都可以在Maven Central中获取,而且Spring社区很多都使用 + Maven进行依赖管理,这对他们来说使用非常方便。此处 + jar包的文件名是 `spring-*-.jar` 的形式,并且Maven的groupid是 + `org.springframework` 。 + +* 在一个特地为Spring创建的Maven公共仓库中除了最终的GA releases,这个仓库还会管理开发的日常记录以及里程碑事件。 + 仓库中jar包的文件名格式和在Maven Central中是一样的,你可以在其中 + 获取开发版本的Spring并与部署在Maven Central中的 + 其他函数库共同使用。 + 仓库中同样包含一个压缩文件,将Spring所需的所有jar文件捆在一起 + 从而便于下载。 + + +所以首先你需要决定如何管理你的依赖:我们通常 +建议使用像Maven、Gradle或Ivy那样的自动化系统,但你也可以 +自己手动下载所有的jar包。稍后我们会在本章中 +进行详尽的说明。 + + + +[[overview-spring-dependencies]] +===== Spring的依赖和依赖于Spring +尽管Spring对大范围的企业级工具或其他外部工具提供整合与支持, +Spring却刻意将强制需要的依赖数量尽可能降到 +最低:在使用Spring实现一些简单的功能的情况下,你无需寻找并下载(即使是自动下载)很多的 +jar包。对于基础的 +依赖注入功能来说只需一个必要的外部依赖,该依赖用于日志记录 +(后面会有详细的关于日志选项的介绍)。 + + +下面我们将列出构件一个依赖于Spring的应用的基本步骤, +先用Maven,然后用Gradle最后用Ivy。任何情况下如果有不清楚的地方,就去 +查阅你所用的依赖管理系统的文档或者 +去参照样本代码-Spring自身在构建时使用Gradle来管理依赖, +样本代码大多使用Gradle或者Maven。 + + + +[[overview-maven-dependency-management]] +===== Maven的依赖管理 +如果你使用 http://maven.apache.org/[Maven] 来进行依赖管理你甚至 +不用明确地去支持日志(logging)依赖。例如,当创建一个应用上下文并使用依赖注入来配置应用时, +你的Maven依赖会 +如下图所示: + + +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + + + org.springframework + spring-context + {spring-version} + runtime + + +---- + +以上就是具体的配置。如果编译时没有涉及到Spring的API就可以将上述的scope设置为 +runtime,这通常是使用基础的依赖注入时 +发生的情况。 + + +上面的例子可以使用Maven Central仓库来实现。如果要使用Spring Maven仓库(例如为了查看里程碑事件或开发快照), +你需要在你的Maven配置中确定 +仓库的位置。对于完整发布版: + + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + io.spring.repo.maven.release + http://repo.spring.io/release/ + false + + +---- + +对于里程碑事件: + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + io.spring.repo.maven.milestone + http://repo.spring.io/milestone/ + false + + +---- + +对于开发快照: + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + io.spring.repo.maven.snapshot + http://repo.spring.io/snapshot/ + true + + +---- + + +[[overview-maven-bom]] +===== Maven的“材料账单依赖” ===== +使用Maven时可能会无意间把不同版本的Spring Jar包混在一起。 +例如,你可能会发现一个第三方的函数库或者另一个Spring工程 +引入了一个老旧版本的传递性依赖(transitive dependency)。如果你忘记了明确地声明一个 +直接的依赖,就可能会出现各种出乎意料的问题。 + + +为了克服此类问题,Maven引入了“材料账单”(BOM)依赖的 +概念。你可以把 `spring-framework-bom` 导入到你的 `dependencyManagement` 部分 +来确保所有的spring依赖(无论直接的或间接的)都是 +同一个版本。 + + +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + + + + org.springframework + spring-framework-bom + {spring-version} + pom + import + + + +---- + +使用BOM的另一个好处就是在使用Spring框架构件时 +不再需要指定 `` 属性。 + + +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + + + org.springframework + spring-context + + + org.springframework + spring-web + + +---- + + +[[overview-gradle-dependency-management]] +===== Gradle的依赖管理 +如果要通过 http://www.gradle.org/[Gradle] 构建系统来使用Spring仓库, +`repositories` 中要包含相应的URL: + + +[source,groovy,indent=0] +[subs="verbatim,quotes"] +---- + repositories { + mavenCentral() + // 可选配置...... + maven { url "http://repo.spring.io/release" } + } +---- + +你可以根据需要把 `repositories` URL从 `/release` 改为 +`/milestone` 或者 `/snapshot` 。当配置好一个仓库后,就可以用平常的Gradle +方法声明依赖。 + + +[source,groovy,indent=0] +[subs="verbatim,quotes,attributes"] +---- + dependencies { + compile("org.springframework:spring-context:{spring-version}") + testCompile("org.springframework:spring-test:{spring-version}") + } +---- + + +[[overview-ivy-dependency-management]] +===== Ivy的依赖管理 +如果你想使用 http://ant.apache.org/ivy[Ivy] 管理依赖那么 +各种配置选项跟上面的是一样的。 + + +为了配置Ivy指向Spring仓库,将下面的解析器 +配置到 `ivysettings.xml` 中: + + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + +---- + +你可以根据需要把 `root` URL从 `/release` 改为 +`/milestone` 或者 `/snapshot` 。 + + +配置完成后,你就可以用一般的方式添加依赖。例如(在 `ivy.xml` 中): + +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + +---- + + +[[overview-distribution-zip]] +===== Distribution Zip 文件 +尽管我们推荐使用支持依赖管理的构建系统来使用Spring框架,你仍然 +可以直接下载distribution zip文件。 + + +各种distribution zip文件发布在Spring的Maven仓库中(这只是为了方便,你无须 +Maven或者其他的构建系统来下载这些文件)。 + + +打开浏览器访问 +http://repo.spring.io/release/org/springframework/spring +根据你所需要的版本号访问相应的子文件夹。distribution文件以 `-dist.zip` 结尾,例如 ++spring-framework-{spring-version}-RELEASE-dist.zip+.distribution文件同样发布在 +http://repo.spring.io/milestone/org/springframework/spring[milestones] +http://repo.spring.io/snapshot/org/springframework/spring[snapshots]. + + + + +[[overview-logging]] +==== Logging +Logging(日志)是Spring的一个非常重要的依赖,因为__a)__它是唯一一个 +强制要求的外部依赖, __b)__人们都会喜欢让他们使用的工具输出一些信息, +__c)__Spring整合了很多其他的工具,这些工具也具有 +logging依赖。开发人员的经常有的一个目标 +就是整个应用,包括所有的外部组件中, +都配置统一的logging。这比看起来要困难因为存在很多 +可供选择的logging框架。 + + +Spring中强制要求的logging依赖是Jakarta Commons Logging API (JCL)。JCL代码会被编译,而且 +那些继承了Spring框架的类 +可以访问到JCL `Log` 对象。对于用户来说很重要的一点就是各种版本的Spring使用同样的 +logging函数库:我们可以很容易实现迁移,因为即使是那些扩展Spring的 +应用也会保留着向后兼容性。实现这一点的方法就是让Spring中的一个模块 +明确地去依赖 `commons-logging` (JCL的典型继承), +并在编译时让其他模块去依赖那个模块。例如你正在使用 +Maven,你想知道 `commons-logging` 依赖在什么地方, +它就在Spring的核心模块 +`spring-core` 中。 + + +关于 `commons-logging` 很好的一点就是你不需要任何 +其他的依赖来运行你的应用。它在运行时会执行一种探索算法,该算法会在 +类路径下的几个典型位置寻找其他的logging框架,或者 +你也可以自己指定寻找路径。如果没有发现其他框架你会 +得到由JDK(java.util.logging或简写成JUL)生成的日志。 +你会发现在大多数情况下Spring应用会运行 +并且立即生成日志到控制台,这是很重要的。 + + + +[[overview-not-using-commons-logging]] +===== 不使用Commons Logging +不幸的是,`commons-logging` 在运行时的探索算法,尽管对于客户端用户 +来说非常方便,是有一定的问题的。当启动Spring工程时它可能会使用 +不同的logging依赖。第一个可能被采用的依赖是 +Simple Logging Facade for Java ( http://www.slf4j.org[SLF4J]), +很多与Spring一起工作的其他工具都使用这一依赖。 + + +大致上有两种关闭 `commons-logging` 的方法: + +.在 `spring-core` 模块中排除依赖(因为这是唯一 + 一个依靠 `commons-logging` 的模块) + +.依靠一个特殊的`commons-logging`依赖,此依赖中的函数库会被 + 替换为一个空的jar文件 + (可以在 http://slf4j.org/faq.html#excludingJCL[SLF4J FAQ] 中了解更多的细节) + + +为了排除commons-logging,将下列信息添加到 `dependencyManagement` 部分: + +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + + + org.springframework + spring-core + {spring-version} + + + commons-logging + commons-logging + + + + +---- + +现在这一应用很可能是不完整的因为在类路径中 +没有了JCL API的继承类,为了修正这一问题需要提供一个新的。下节会说明如何提供替代的JCL继承类, +例如使用SLF4J。 + + + +[[overview-logging-slf4j]] +===== 使用 SLF4J +SLF4J是一个更为干净的依赖,并且运行时的效率比`commons-logging`更高 +因为它采用编译时绑定机制而不是在运行时才去寻找 +其他与之整合的logging框架。这意味着你必须更为明确地指出你想在运行时要做哪些事情, +并相应地对其声明或配置。SLF4J +提供了与很多常用logging框架的绑定,所以你可以选择一个你经常使用的框架, +将SLF4J与之绑定,从而对其进行配置和管理。 + + +SLF4J提供了与很多常用logging框架的绑定,包括JCL,并且它也能做 +与之相反的是:为其他logging框架和它自己提供桥接。所以为了在Spring中使用SLF4J你需要 +把 `commons-logging` 依赖替换为SLF4J-JCL +桥接。当你做完这些后来自于Spring的logging calls会被翻译为 +对SLF4J API的logging calls,所以如果应用中其它的函数库使用那个API, +就只需在一处配置和管理logging。 + + +一个通常的选择是将Spring桥接到SLF4J,然后提供从SLF4J到Log4J的明确 +的绑定。你需要提供4个依赖(不包括现有的 +`commons-logging` ):桥接器,SLF4J API,到Log4J的绑定以及Log4J实现 +本身。在Maven中你需要这样去做: + + +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + + + org.springframework + spring-core + {spring-version} + + + commons-logging + commons-logging + + + + + org.slf4j + jcl-over-slf4j + 1.5.8 + + + org.slf4j + slf4j-api + 1.5.8 + + + org.slf4j + slf4j-log4j12 + 1.5.8 + + + log4j + log4j + 1.2.14 + + +---- + +看起来只是为了获得一些日志就需要如此多的依赖,事情也的确是这样,但 +这__是__可选的,而且涉及到类加载器方面,这样配置会比单纯配置平常的 `commons-logging` 表现的更好, +尤其是当你使用要求十分严格的容器例如 +OSGIF平台。根据假设,这样配置也会带来性能上的提升,因为 +绑定实在编译时而不是运行时完成的。 + + +在SLF4J用户中一个更为平常的选择是直接绑定到 http://logback.qos.ch[Logback] , +这会采用更少的步骤,产生更少的依赖,同时也免除了额外的绑定步骤,因为 +Logback直接继承了SLF4J。你只需要依赖两个函数库 +而不是四个( `jcl-over-slf4j`和`logback` )。如果要采用此种方案, +你需要将slf4j的api依赖从其他的外部依赖中排除(不是Spring), +因为在类路径中你只需要一种版本的此种API。 + + + +[[overview-logging-log4j]] +===== 使用 Log4J +很多人因为方便管理和配置的原因 +使用 http://logging.apache.org/log4j[Log4j] 作为logging框架。此框架效率高,构建完备。 +当我们建立和使用Spring时,在运行时就是使用的这个框架。Spring也同时提供了用来配置和 +初始化Log4J的工具,所以Spring的一些模块中 +具有可选的,依靠Log4J的编译时依赖。 + + +如果要把Log4J和默认的JCL依赖( `commons-logging` )一起使用,你只需要 +将Log4J放在类路径下并提供一个配置文件( +`log4j.properties` 或者 `log4j.xml` 放在类路径的根目录下)。对于Maven用户来说以下就是所需的 +依赖声明: + + +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + + + org.springframework + spring-core + {spring-version} + + + log4j + log4j + 1.2.14 + + +---- + +这是一个log4j.properties样板,用于配置输出到控制台的日志。 + +[literal] +[subs="verbatim,quotes"] +---- +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n + +log4j.category.org.springframework.beans.factory=DEBUG +---- + +[[overview-native-jcl]] +====== 使用本地JCL的运行时容器 +很多为Spring应用选择的容器本身就包含 +JCL的继承。这些容器的原始类型是IBM Websphere Application Server (WAS)。这通常会导致 +很多问题,遗憾的是对此没有完美的解决方案; +仅仅是吧 `commons-logging` 从应用中排除掉在大部分情况下并不能解决问题。 + + +说得更清楚一点:报告的问题通常不是关于JCL本身,甚至不是关于 +`commons-logging` :这些问题和 `commons-logging` 与其他框架的绑定 +有关(通常是Log4J)。有些容器使用的是老版本的 `commons-logging` (1.0),而人们现在大多使用 +1.1版本,而老版和新版 +有着不同的运行时搜索算法。Spring并不使用 +任何常用的JCL API,所以程序不会崩溃,可是一旦Spring尝试获取日志时, +与Log4J的绑定就会失效。 + + +若使用WAS,在失效的情况下最容易的解决方案就是将class loader体系进行反转 +(IBM称之为"parent last"),从而使应用控制JCL依赖,而不是由容器来控制 +。上面的解决办法并不总是有效的, +但在公共领域有很多替代的解决方案, +采用哪种方案取决于具体的版本以及容器的特性。 diff --git a/src/asciidoc/chapter/10.5.1.testing.adoc b/src/asciidoc/chapter/10.5.1.testing.adoc index afff5ebc8a1f8a04e43a2a8156b6904d0d2a6581..d2bcc1d383f0c5947c8213947ba06943f0e2fa0f 100644 --- a/src/asciidoc/chapter/10.5.1.testing.adoc +++ b/src/asciidoc/chapter/10.5.1.testing.adoc @@ -1,120 +1,106 @@ -[[spring-mvc-test-server-static-imports]] -====== Static Imports -The fluent API in the example above requires a few static imports such as -`MockMvcRequestBuilders.*`, `MockMvcResultMatchers.*`, and `MockMvcBuilders.*`. An easy -way to find these classes is to search for types matching __"MockMvc*"__. If using -Eclipse, be sure to add them as "favorite static members" in the Eclipse preferences -under__Java -> Editor -> Content Assist -> Favorites__. That will allow use of content -assist after typing the first character of the static method name. Other IDEs (e.g. -IntelliJ) may not require any additional configuration. Just check the support for code -completion on static members. - -[[spring-mvc-test-server-setup-options]] -====== Setup Options -The goal of server-side test setup is to create an instance of `MockMvc` that can be -used to perform requests. There are two main options. - -The first option is to point to Spring MVC configuration through the __TestContext -framework__, which loads the Spring configuration and injects a `WebApplicationContext` -into the test to use to create a `MockMvc`: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @RunWith(SpringJUnit4ClassRunner.class) - @WebAppConfiguration - @ContextConfiguration("my-servlet-context.xml") - public class MyWebTests { - - @Autowired - private WebApplicationContext wac; - - private MockMvc mockMvc; - - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); - } - - // ... - - } ----- - -The second option is to simply register a controller instance without loading any Spring -configuration. Instead basic Spring MVC configuration suitable for testing annotated -controllers is automatically created. The created configuration is comparable to that of -the MVC JavaConfig (and the MVC namespace) and can be customized to a degree through -builder-style methods: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - public class MyWebTests { - - private MockMvc mockMvc; - - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); - } - - // ... - - } ----- - -Which option should you use? - -The __"webAppContextSetup"__ loads the actual Spring MVC configuration resulting in a -more complete integration test. Since the __TestContext framework__ caches the loaded -Spring configuration, it helps to keep tests running fast even as more tests get added. -Furthermore, you can inject mock services into controllers through Spring configuration, -in order to remain focused on testing the web layer. Here is an example of declaring a -mock service with Mockito: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - ----- - -Then you can inject the mock service into the test in order set up and verify -expectations: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @RunWith(SpringJUnit4ClassRunner.class) - @WebAppConfiguration - @ContextConfiguration("test-servlet-context.xml") - public class AccountTests { - - @Autowired - private WebApplicationContext wac; - - private MockMvc mockMvc; - - @Autowired - private AccountService accountService; - - // ... - - } ----- - -The __"standaloneSetup"__ on the other hand is a little closer to a unit test. It tests -one controller at a time, the controller can be injected with mock dependencies -manually, and it doesn't involve loading Spring configuration. Such tests are more -focused in style and make it easier to see which controller is being tested, whether any -specific Spring MVC configuration is required to work, and so on. The "standaloneSetup" -is also a very convenient way to write ad-hoc tests to verify some behavior or to debug -an issue. - -Just like with integration vs unit testing, there is no right or wrong answer. Using the -"standaloneSetup" does imply the need for some additional "webAppContextSetup" tests to -verify the Spring MVC configuration. Alternatively, you can decide write all tests with -"webAppContextSetup" and always test against actual Spring MVC configuration. - +[[spring-mvc-test-server-static-imports]] +====== 静态导入 +上述例子中,使用了一些静态导入(staitc imports),例如 + `MockMvcRequestBuilders.*` , `MockMvcResultMatchers.*` , 和 +`MockMvcBuilders.*` 。想找到这些类,有个简单的办法是搜索 __“MockMvc*”__ 。如果使用Eclipse, +请在Eclipse 的 preferences 菜单中找到 __Java → Editor → Content Assist → Favorites__, +把这些类加入至“favorite static members”,那样就能在敲入这些静态方法的第一个字母时给出代码提示。 +其他IDE(例如 InteliJ)可能不需要额外的配置,请参考各自的帮助文档。 + +[[spring-mvc-test-server-setup-options]] +====== Setup 装配选项 + +服务器端的测试setup是为了创建一个能发起请求的 `MockMvc` 实例。有两种创建的办法(选项) + +第一种办法是通过 __TestContext__ 框架指向 Spring MVC 的配置文件,框架将配置载入并把 `WebApplicationContext` 注入至 测试类, +从而创建一个 `MockMvc` 。 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @RunWith(SpringJUnit4ClassRunner.class) + @WebAppConfiguration + @ContextConfiguration("my-servlet-context.xml") + public class MyWebTests { + + @Autowired + private WebApplicationContext wac; + + private MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + // ... + + } +---- + +另一种办法是简单地将被测试的Controller 实例注册至 MockMvc,而不加载任何配置,那样测试框架会自动创建一个 +合适的基础 Spring MVC 配置给 Controller 。自动创建的配置可与通过 MVC JavaConfig(以及MVC命名空间) +创建的配置相提并论,并且可以通过构造器风格的方法进行一定程度的定制化: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + public class MyWebTests { + + private MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); + } + + // ... + + } +---- + +你该使用那种方式呢? + +第一种方式 __“webAppContextSetup”__ 会加载实际的Spring MVC 配置,实现一个更完整的集成测试。 +因为 __TestContext 框架__ 会缓存已载入的Spring配置,所以即使加入更多的测试用例,整个测试过程也能运行的比较快。 +此外,你还可以通过 Spring 配置注入一个模拟service对象至Controller中,从而可以专注于web 层的测试。下面是一个使用了 Mockito 的例子。 + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + +---- + +然后就可以将模拟service对象注入至测试代码中,组装并验证测试是否符合期望结果 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @RunWith(SpringJUnit4ClassRunner.class) + @WebAppConfiguration + @ContextConfiguration("test-servlet-context.xml") + public class AccountTests { + + @Autowired + private WebApplicationContext wac; + + private MockMvc mockMvc; + + @Autowired + private AccountService accountService; + + // ... + + } +---- + +从另一个角度看,__“standaloneSetup”__ 更像单元测试:一次测试一个Controller,Controller可以被手动注入模拟的依赖, +也不会加载Spring配置。这样的测试从形式上来说更加专注、更容易看出某个Controller是否被测试了、 +是否要做某些特定的Spring MVC 配置,等等。要写一个即席ad-hoc测试去验证某些行为或debug某个问题时, +“standaloneSetup”是一个很方便的办法。 + +正如集成测试与单元测试之争,上面两种测试方式也没有对错之分。 +使用“standaloneSetup”的确需要某些额外的“webAppContextSetup”去验证 Spring MVC 的配置。 +另一方面,你也可以把“webAppContextSetup”与所有测试代码整合在一起,使用实际的Spring MVC 配置去测试。 \ No newline at end of file diff --git a/src/asciidoc/chapter/10.5.2.testing.adoc b/src/asciidoc/chapter/10.5.2.testing.adoc index afe32dc265dfe347336a64f3c67b6fd4d161edfd..e3cc643640c410d1d3953a62bc7fba38f673473d 100644 --- a/src/asciidoc/chapter/10.5.2.testing.adoc +++ b/src/asciidoc/chapter/10.5.2.testing.adoc @@ -1,76 +1,67 @@ -[[spring-mvc-test-server-performing-requests]] -====== Performing Requests -To perform requests, use the appropriate HTTP method and additional builder-style -methods corresponding to properties of `MockHttpServletRequest`. For example: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON)); ----- - -In addition to all the HTTP methods, you can also perform file upload requests, which -internally creates an instance of `MockMultipartHttpServletRequest`: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8"))); ----- - -Query string parameters can be specified in the URI template: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(get("/hotels?foo={foo}", "bar")); ----- - -Or by adding Servlet request parameters: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(get("/hotels").param("foo", "bar")); ----- - -If application code relies on Servlet request parameters, and doesn't check the query -string, as is most often the case, then it doesn't matter how parameters are added. Keep -in mind though that parameters provided in the URI template will be decoded while -parameters provided through the `param(...)` method are expected to be decoded. - -In most cases it's preferable to leave out the context path and the Servlet path from -the request URI. If you must test with the full request URI, be sure to set the -`contextPath` and `servletPath` accordingly so that request mappings will work: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main")) ----- - -Looking at the above example, it would be cumbersome to set the contextPath and -servletPath with every performed request. That's why you can define default request -properties when building the `MockMvc`: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - public class MyWebTests { - - private MockMvc mockMvc; - - @Before - public void setup() { - mockMvc = standaloneSetup(new AccountController()) - .defaultRequest(get("/") - .contextPath("/app").servletPath("/main") - .accept(MediaType.APPLICATION_JSON).build(); - } ----- - -The above properties will apply to every request performed through the `MockMvc`. If the -same property is also specified on a given request, it will override the default value. -That is why, the HTTP method and URI don't matter, when setting default request -properties, since they must be specified on every request. - +[[spring-mvc-test-server-performing-requests]] +====== 发起请求 +要发起请求,可以使用恰当的 HTTP 方法,以及 `MockHttpServletRequest` 相应的属性构造器方法。例如: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON)); +---- + +除了这些 HTTP 方法外,你也可以发起文件上传 file upload 请求,其内部会创建一个 `MockMultipartHttpServletRequest` 实例 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8"))); +---- + +可以使用 URI 模板(译者注:GET 方法)设置查询字符串: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(get("/hotels?foo={foo}", "bar")); +---- + +或添加 Servlet 请求参数(译者注:POST 方法): + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(get("/hotels").param("foo", "bar")); +---- + +就像在大部分情况下,如果应用程序依赖于 Servlet 请求参数,而且不检查查询字符串, +那么应用程序不会关心参数是怎样被加进来的。但是请记住, URI 模板(译者注:GET 方法)提供的参数需要手工解码, +而通过  `param(...)` 方法提供的参数会由 Servlet 容器自动解码。 + +在大多数情况下,最好将上下文路径(context path) 和Servlet路径 (Servlet path ) 从请求URI 中分离。 +如果你一定要按照全路径进行测试,请确保设置了相应的 `contextPath`  和 `servletPath` ,请求映射才能正常工作。 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main")) +---- + +上面的代码中,每次发起请求都要设置 contextPath  和servletPath 是件很麻烦的事情,你可以在构造 `MockMVC` 时定义默认的请求属性。 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + public class MyWebTests { + + private MockMvc mockMvc; + + @Before + public void setup() { + mockMvc = standaloneSetup(new AccountController()) + .defaultRequest(get("/") + .contextPath("/app").servletPath("/main") + .accept(MediaType.APPLICATION_JSON).build(); + } +---- + +在上述代码中,每次通过 `MockMVC` 发起请求时都会使用这些默认的请求属性。如果在一个特定的请求中设置了某个属性,就会覆盖它的默认值。 +这也就是为什么不需要设置 HTTP 方法和 URI 的默认值,因为每次发起请求都会设置,默认值都会被覆盖掉。 diff --git a/src/asciidoc/chapter/10.5.3.testing.adoc b/src/asciidoc/chapter/10.5.3.testing.adoc index 516c21cdb24f24e9ae3746b37b66ff48e7b4f07c..7ac3ca51da7539b5acd9cf2cffe1983d53c6e856 100644 --- a/src/asciidoc/chapter/10.5.3.testing.adoc +++ b/src/asciidoc/chapter/10.5.3.testing.adoc @@ -1,111 +1,90 @@ -[[spring-mvc-test-server-defining-expectations]] -====== Defining Expectations -Expectations can be defined by appending one or more `.andExpect(..)` after call to -perform the request: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(get("/accounts/1")).andExpect(status().isOk()); ----- - -`MockMvcResultMatchers.*` defines a number of static members, some of which return types -with additional methods, for asserting the result of the performed request. The -assertions fall in two general categories. - -The first category of assertions verify properties of the response, i.e the response -status, headers, and content. Those are the most important things to test for. - -The second category of assertions go beyond the response, and allow inspecting Spring -MVC specific constructs such as which controller method processed the request, whether -an exception was raised and handled, what the content of the model is, what view was -selected, what flash attributes were added, and so on. It is also possible to verify -Servlet specific constructs such as request and session attributes. The following test -asserts that binding/validation failed: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(post("/persons")) - .andExpect(status().isOk()) - .andExpect(model().attributeHasErrors("person")); ----- - -Many times when writing tests, it's useful to dump the result of the performed request. -This can be done as follows, where `print()` is a static import from -`MockMvcResultHandlers`: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(post("/persons")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(model().attributeHasErrors("person")); ----- - -As long as request processing causes an unhandled exception, the `print()` method will -print all the available result data to `System.out`. - -In some cases, you may want to get direct access to the result and verify something that -cannot be verified otherwise. This can be done by appending `.andReturn()` at the end -after all expectations: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn(); - // ... ----- - -When all tests repeat the same expectations, you can define the common expectations once -when building the `MockMvc`: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - standaloneSetup(new SimpleController()) - .alwaysExpect(status().isOk()) - .alwaysExpect(content().contentType("application/json;charset=UTF-8")) - .build() ----- - -Note that the expectation is __always__ applied and cannot be overridden without -creating a separate `MockMvc` instance. - -When JSON response content contains hypermedia links created with -https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], the resulting links can -be verified: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.links[?(@.rel == ''self'')].href").value("http://localhost:8080/people")); ----- - -When XML response content contains hypermedia links created with -https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], the resulting links can -be verified: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - Map ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom"); - mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML)) - .andExpect(xpath("/person/ns:link[@rel=''self'']/@href", ns).string("http://localhost:8080/people")); ----- - -[[spring-mvc-test-server-filters]] -====== Filter Registrations -When setting up a `MockMvc`, you can register one or more `Filter` instances: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); ----- - -Registered filters will be invoked through `MockFilterChain` from `spring-test` and the -last filter will delegates to the `DispatcherServlet`. - +[[spring-mvc-test-server-defining-expectations]] +====== 定义期望的结果 + +在发起请求后,可以连续调用多次  `.andExpect(..)` 方法来定义期望的结果 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(get("/accounts/1")).andExpect(status().isOk()); +---- + +`MockMvcResultMatchers.*` 类定义了很多静态成员,其中有些方法会返回特定类型的值,用于断言请求的结果。这些断言可分为两种类型: + +第一类断言用于验证响应报文的属性,例如响应状态码、头部和内容。这些一般是测试中要重点关注的。 + +第二类断言则不仅仅针对于响应报文,它们可以针对Spring MVC的特定内容进行验证。例如,请求是由哪个Controller的方法处理的、某个异常是否被抛出并且被处理了、model 的内容、选择了哪个view、增加了哪些 flash 属性,等等。这类断言还可以用于验证与 Servlet 相关的特定内容,例如Request 和 Session 域的属性。下列代码断言某个绑定/验证动作失败了: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(post("/persons")) + .andExpect(status().isOk()) + .andExpect(model().attributeHasErrors("person")); +---- + +测试的时候经常需要把结果 dump ,可以通过下列代码去实现,其中的 `print()` 是  `MockMvcResultHandlers` 的静态导入(static import)。 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(post("/persons")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(model().attributeHasErrors("person")); +---- + +只要请求处理过程中抛出了未捕获的异常,`print()` 方法就会通过 `System.out` 打印出所有信息。 + +有些时候,你可能想直接获取结果数据,进而做一些别的地方无法做的验证。你可以在所有的期望末尾增加一个  `.andReturn()` 方法调用。 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn(); + // ... +---- + +如果所有的测试都期望某个结果,你可以在构建 `MockMvc` 时定义公共的期望结果。 + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + standaloneSetup(new SimpleController()) + .alwaysExpect(status().isOk()) + .alwaysExpect(content().contentType("application/json;charset=UTF-8")) + .build() +---- + +注意,这里添加的期望结果会 __一直__ 有效、无法被覆盖,除非你定义一个新的 `MockMvc` + +如果返回的JSON 响应报文包含了使用  https://github.com/spring-projects/spring-hateoas[Spring HATEOAS] 创建的超媒体链接,可以这样去验证链接: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.links[?(@.rel == ''self'')].href").value("http://localhost:8080/people")); +---- + +如果返回的 XML 响应报文包含了使用  https://github.com/spring-projects/spring-hateoas[Spring HATEOAS] 创建的超媒体链接,可以这样去验证链接: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + Map ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom"); + mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML)) + .andExpect(xpath("/person/ns:link[@rel=''self'']/@href", ns).string("http://localhost:8080/people")); +---- + +[[spring-mvc-test-server-filters]] +====== 注册过滤器 +当你装配 MockMvc 时,可以注册一个或多个 `过滤器Filter` 实例: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); +---- + +注册的过滤器会由 `spring-test` 通过 `MockFilterChain` 进行调用,最后一个过滤器会把请求委派给 `DispatcherServlet`。 diff --git a/src/asciidoc/chapter/10.5.testing.adoc b/src/asciidoc/chapter/10.5.testing.adoc index f0e993d397ad9420ec066fd9a7d9c3d09aca090c..6754e1a255844bfabebea36a57aec36449e6d286 100644 --- a/src/asciidoc/chapter/10.5.testing.adoc +++ b/src/asciidoc/chapter/10.5.testing.adoc @@ -1,108 +1,95 @@ -[[spring-mvc-test-framework]] -==== Spring MVC Test Framework - -.Standalone project -**** -Before inclusion in Spring Framework 3.2, the Spring MVC Test framework had already -existed as a separate project on GitHub where it grew and evolved through actual use, -feedback, and the contribution of many. - -The standalone https://github.com/spring-projects/spring-test-mvc[spring-test-mvc project] -is still available on GitHub and can be used in conjunction with Spring Framework 3.1.x. -Applications upgrading to 3.2 or later should replace the `spring-test-mvc` dependency with a -dependency on `spring-test`. - -The `spring-test` module uses a different package `org.springframework.test.web` but -otherwise is nearly identical with two exceptions. One is support for features new in -3.2 (e.g. asynchronous web requests). The other relates to the options for creating a -`MockMvc` instance. In Spring Framework 3.2 and later, this can only be done through the -TestContext framework, which provides caching benefits for the loaded configuration. -**** - -The __Spring MVC Test framework__ provides first class JUnit support for testing client -and server-side Spring MVC code through a fluent API. Typically it loads the actual -Spring configuration through the __TestContext framework__ and always uses the -`DispatcherServlet` to process requests thus approximating full integration tests -without requiring a running Servlet container. - -Client-side tests are `RestTemplate`-based and allow tests for code that relies on the -`RestTemplate` without requiring a running server to respond to the requests. - - -[[spring-mvc-test-server]] -===== Server-Side Tests -Before Spring Framework 3.2, the most likely way to test a Spring MVC controller was to -write a unit test that instantiates the controller, injects it with mock or stub -dependencies, and then calls its methods directly, using a `MockHttpServletRequest` and -`MockHttpServletResponse` where necessary. - -Although this is pretty easy to do, controllers have many annotations, and much remains -untested. Request mappings, data binding, type conversion, and validation are just a few -examples of what isn't tested. Furthermore, there are other types of annotated methods -such as `@InitBinder`, `@ModelAttribute`, and `@ExceptionHandler` that get invoked as -part of request processing. - -The idea behind Spring MVC Test is to be able to re-write those controller tests by -performing actual requests and generating responses, as they would be at runtime, along -the way invoking controllers through the Spring MVC `DispatcherServlet`. Controllers can -still be injected with mock dependencies, so tests can remain focused on the web layer. - -Spring MVC Test builds on the familiar "mock" implementations of the Servlet API -available in the `spring-test` module. This allows performing requests and generating -responses without the need for running in a Servlet container. For the most part -everything should work as it does at runtime with the exception of JSP rendering, which -is not available outside a Servlet container. Furthermore, if you are familiar with how -the `MockHttpServletResponse` works, you'll know that forwards and redirects are not -actually executed. Instead "forwarded" and "redirected" URLs are saved and can be -asserted in tests. This means if you are using JSPs, you can verify the JSP page to -which the request was forwarded. - -All other means of rendering including `@ResponseBody` methods and `View` types (besides -JSPs) such as Freemarker, Velocity, Thymeleaf, and others for rendering HTML, JSON, XML, -and so on should work as expected, and the response will contain the generated content. - -Below is an example of a test requesting account information in JSON format: - -[source,java,indent=0] ----- - import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; - import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - - @RunWith(SpringJUnit4ClassRunner.class) - @WebAppConfiguration - @ContextConfiguration("test-servlet-context.xml") - public class ExampleTests { - - @Autowired - private WebApplicationContext wac; - - private MockMvc mockMvc; - - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); - } - - @Test - public void getAccount() throws Exception { - this.mockMvc.perform(get("/accounts/1").accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) - .andExpect(status().isOk()) - .andExpect(content().contentType("application/json")) - .andExpect(jsonPath("$.name").value("Lee")); - } - - } ----- - -The test relies on the `WebApplicationContext` support of the __TestContext framework__. -It loads Spring configuration from an XML configuration file located in the same package -as the test class (also supports JavaConfig) and injects the created -`WebApplicationContext` into the test so a `MockMvc` instance can be created with it. - -The `MockMvc` is then used to perform a request to `"/accounts/1"` and verify the -resulting response status is 200, the response content type is `"application/json"`, and -response content has a JSON property called "name" with the value "Lee". JSON content is -inspected with the help of Jayway's https://github.com/jayway/JsonPath[JsonPath -project]. There are lots of other options for verifying the result of the performed -request and those will be discussed later. - +[[spring-mvc-test-framework]] +==== Spring MVC Test Framework 测试框架 + +.独立项目 +**** + +在被Spring Framework 3.2 纳入之前,Spring MVC Test Framework 测试框架就已经作为Github的一个独立项目存在了,并且在许多人的实际使用、反馈和贡献下不断成长和发展。 + +独立的 https://github.com/spring-projects/spring-test-mvc[spring-test-mvc 项目] +仍然可以从github获取到,并与 Spring Framework 3.1.x 结合使用。 +使用Spring 3.2 及以上版本的应用程序不能再使用 `spring-test-mvc` 的依赖,而应当改为 `spring-test` 。 + +`spring-test` 模块使用的是另一个 `org.springframework.test.web` 包,除了两个地方以外, +与原来的包基本相同。这两个差别,一个是对 spring 3.2新特性的支持(例如异步 web 请求);另一个是与创建 `MockMvc` 实例的选项有关, +因为在 Spring Framework 3.2及以上版本中,`MockMvc` 实例的创建只能通过 TestContext Framework 来完成, +从而在加载配置时能够使用缓存等好处。 + +**** + +__Spring MVC Test framework__ 通过一套便利的 API ,为 基于 Spring MVC 的客户端和服务端代码Junit测试提供了非常好的支持。 +通常,它会通过 __TestContext framework__ 加载实际的Spring配置,并使用 `DispatcherServlet` 处理请求, +从而在不用运行 Servlet 容器的情况下几乎实现了完整的集成测试。 + +客户端测试是基于 `RestTemplate` 的,允许在没有服务器响应请求的情况下,测试那些依赖 `RestTemplate` 的代码。 + +[[spring-mvc-test-server]] +===== Server-Side Tests 服务端的测试 +在Spring Framework 3.2之前,测试一个Spring MVC 的 Controller最常用的方法是写一个单元测试,初始化Controller, +将模拟对象mock或桩对象stub的依赖注入至Controller,然后直接调用Controller的方法, +并且在必要时使用 `MockHttpServletRequest`  和 `MockHttpServletResponse` 。 + +虽然这样做很简单,但是对于Controller使用到的大部分注解,这种方法测试不了。例如 请求映射 Request mapping、 +数据绑定 data binding、类型转换,以及数据验证都无法被测试到,此外还有那些被 + `@InitBinder` , `@ModelAttribute` , 和 `@ExceptionHandler` 等注解的方法也无法被测试,这些方法恰恰是在请求处理时会被调用的。 + +Spring MVC Test 背后的思想是通过向Spring MVC 的 `DispatcherServlet` 发起真正的请求,从而去调用controller, +并生成响应,就像在Servlet 容器中运行时那样。controller 依旧可以被注入模拟的依赖,这样测试代码可以专注于web 层的逻辑 + +Spring MVC Test 构建于 `spring-test` 模块中我们耳熟能详的Servlet API “模拟” 实现之上。 +它允许我们在不运行Servlet 容器的情况下去发起请求并生成响应报文。大部分情况下, +这一执行过程都会和真正的运行时环境(在容器中)一样,除了 JSP 页面渲染, +因为只有在 Servlet容器内部才能渲染 JSP 。此外,如果你熟悉 `MockHttpServletResponse` 是如何运作的话, +你就会知道转发和重定向不会真正被执行。实际上,“转发”和“重定向”的URL会被保存起来并可以在测试代码中被断言assert, +因此如果你在使用JSP,你可以验证请求被转发到了哪个JSP页面。 + +其他渲染HTML、JSON、XML 等的手段包括  `@ResponseBody`  方法注解、不同的 `View` 类型(除了JSP以外)例如Freemarker,  +Velocity, Thymeleaf 及其他,这些都能正常工作,响应报文会包含相应的内容。 + + +下面是一个测试的例子,以JSON格式请求账户信息。 + +[source,java,indent=0] +---- + import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; + import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + + @RunWith(SpringJUnit4ClassRunner.class) + @WebAppConfiguration + @ContextConfiguration("test-servlet-context.xml") + public class ExampleTests { + + @Autowired + private WebApplicationContext wac; + + private MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Test + public void getAccount() throws Exception { + this.mockMvc.perform(get("/accounts/1").accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/json")) + .andExpect(jsonPath("$.name").value("Lee")); + } + + } +---- + +这个测试依赖于 __TestContext framework__ 对  `WebApplicationContext`  的支持。 +测试代码从与自身所在包 package 定位 Spring 的 XML 配置文件(也支持JavaConfig),并将其载入, +然后将 `WebApplicationContext`  注入进来,用于创建 `MockMvc` 。 + + + +测试代码通过 `MockMvc` 发起对 `"/accounts/1"` 路径的请求,验证其产生的响应报文是否状态码为200、 +内容类型 content type 是否为 `"application/json"` 、响应内容是否含有一个名字为“name”值为“Lee”的JSON 属性。 +这里使用了 Jayway's https://github.com/jayway/JsonPath[JsonPath +project] 去探查 JSON 内容。 +除此以外还有很多其他验证响应报文的选项,我们将在后续讨论。 + + diff --git a/src/asciidoc/chapter/12.dao.adoc b/src/asciidoc/chapter/12.dao.adoc index 8db7ca69bc980a309c1f6e75cfb30af726018623..bb5070ac4dbacc7cbc769560f6cc312fff8c5c48 100644 --- a/src/asciidoc/chapter/12.dao.adoc +++ b/src/asciidoc/chapter/12.dao.adoc @@ -1,142 +1,112 @@ -[[dao]] -== DAO support - - - - -[[dao-introduction]] -=== Introduction -The Data Access Object (DAO) support in Spring is aimed at making it easy to work with -data access technologies like JDBC, Hibernate, JPA or JDO in a consistent way. This -allows one to switch between the aforementioned persistence technologies fairly easily -and it also allows one to code without worrying about catching exceptions that are -specific to each technology. - - - - -[[dao-exceptions]] -=== Consistent exception hierarchy -Spring provides a convenient translation from technology-specific exceptions like -`SQLException` to its own exception class hierarchy with the `DataAccessException` as -the root exception. These exceptions wrap the original exception so there is never any -risk that one might lose any information as to what might have gone wrong. - -In addition to JDBC exceptions, Spring can also wrap Hibernate-specific exceptions, -converting them from proprietary, checked exceptions (in the case of versions of -Hibernate prior to Hibernate 3.0), to a set of focused runtime exceptions (the same is -true for JDO and JPA exceptions). This allows one to handle most persistence exceptions, -which are non-recoverable, only in the appropriate layers, without having annoying -boilerplate catch-and-throw blocks and exception declarations in one's DAOs. (One can -still trap and handle exceptions anywhere one needs to though.) As mentioned above, JDBC -exceptions (including database-specific dialects) are also converted to the same -hierarchy, meaning that one can perform some operations with JDBC within a consistent -programming model. - -The above holds true for the various template classes in Springs support for various ORM -frameworks. If one uses the interceptor-based classes then the application must care -about handling `HibernateExceptions` and `JDOExceptions` itself, preferably via -delegating to `SessionFactoryUtils`' `convertHibernateAccessException(..)` or -`convertJdoAccessException()` methods respectively. These methods convert the exceptions -to ones that are compatible with the exceptions in the `org.springframework.dao` -exception hierarchy. As `JDOExceptions` are unchecked, they can simply get thrown too, -sacrificing generic DAO abstraction in terms of exceptions though. - -The exception hierarchy that Spring provides can be seen below. (Please note that the -class hierarchy detailed in the image shows only a subset of the entire -`DataAccessException` hierarchy.) - -image::images/DataAccessException.gif[width=400] - - - - -[[dao-annotations]] -=== Annotations used for configuring DAO or Repository classes -The best way to guarantee that your Data Access Objects (DAOs) or repositories provide -exception translation is to use the `@Repository` annotation. This annotation also -allows the component scanning support to find and configure your DAOs and repositories -without having to provide XML configuration entries for them. - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - **@Repository** - public class SomeMovieFinder implements MovieFinder { - // ... - } ----- - -Any DAO or repository implementation will need to access to a persistence resource, -depending on the persistence technology used; for example, a JDBC-based repository will -need access to a JDBC `DataSource`; a JPA-based repository will need access to an -`EntityManager`. The easiest way to accomplish this is to have this resource dependency -injected using one of the `@Autowired,`, `@Inject`, `@Resource` or `@PersistenceContext` -annotations. Here is an example for a JPA repository: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Repository - public class JpaMovieFinder implements MovieFinder { - - @PersistenceContext - private EntityManager entityManager; - - // ... - - } ----- - -If you are using the classic Hibernate APIs than you can inject the SessionFactory: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Repository - public class HibernateMovieFinder implements MovieFinder { - - private SessionFactory sessionFactory; - - @Autowired - public void setSessionFactory(SessionFactory sessionFactory) { - this.sessionFactory = sessionFactory; - } - - // ... - - } ----- - -Last example we will show here is for typical JDBC support. You would have the -`DataSource` injected into an initialization method where you would create a -`JdbcTemplate` and other data access support classes like `SimpleJdbcCall` etc using -this `DataSource`. - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Repository - public class JdbcMovieFinder implements MovieFinder { - - private JdbcTemplate jdbcTemplate; - - @Autowired - public void init(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } - - // ... - - } ----- - -[NOTE] -==== -Please see the specific coverage of each persistence technology for details on how to -configure the application context to take advantage of these annotations. -==== - - - - +[[dao]] +== DAO 支持 + + + + +[[dao-introduction]] +=== 介绍 +Spring中对数据访问对象(DAO)的支持旨在简化Spring与数据访问技术的操作,使JDBC、Hibernate、JPA和JDO等采用统一的方式访问。 +这就允许使用者在各种持久化技术之间能够相对轻松的进行切换,同时,使用者也不必担心每种不同技术造成的异常处理的差异。 + + + + +[[dao-exceptions]] +=== 一致的异常层次结构 +Spring提供了一个方便地从特定技术异常(如`SQLException`)转换为以`DataAccessException`作为根异常的自身异常层次结构中的类。 +异常层次结构中的类将原始异常进行了包装,所以,我们不用担心发生错误后异常信息丢失的问题。 + +除了JDBC异常以外,Spring也可以包装特定的Hibernate异常,可以将所有的checked异常(支持Hibernate 3.0以前的版本)转换为 +一组集中的运行时异常,JDO和JPA异常也可以同样包装。这就允许使用者不必在DAO中写大量的烦人的死板的catch和throw语句以及对 +应的异常声明 ,就可以处理绝大多数不可恢复的只能在特定层处理的持久化异常。(使用者也还可以再需要的地方捕获和处理异常。) +如上所述,JDBC异常(包括特定的数据库方言)也会转换为同样的级别,这就意味着使用者可以使用一个一致的编程模型来使用JDBC。 + +Spring支持的各种ORM框架的模板类都支持上面的特性。如果使用者基于拦截器的类,那么程序必须关心如何处理`HibernateExceptions` 和`JDOExceptions`异常, +最好是分别委托给`SessionFactoryUtils`的`convertHibernateAccessException(..)`或`convertJdoAccessException()`方法。这两个方法可以将异常转换为与 +`org.springframework.dao`异常层次结构相兼容的异常类。就异常而言,虽然牺牲了通用的DAO抽象特性,但因为`JDOExceptions`异常是unchecked,它们也可以被简单的抛出。 +Spring提供的异常层次结构如下图所示。(请注意图中的类层次结构只是整个`DataAccessException`的一个子集。) + +image::images/DataAccessException.gif[width=400] + + + + +[[dao-annotations]] +=== 用于配置DAO或库类的注解 +确保自定义的数据访问对象(DAO)或库类提供的异常转换的最好方法就是使用`@Repository`注解。这一注解同样允许组件扫描来发现和配置自定义DAO和库,而不用提供XML配置信息。 + + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + **@Repository** + public class SomeMovieFinder implements MovieFinder { + // ... + } +---- + +任何DAO或者库实现都依赖所使用的持久化技术来需要访问某个持久化资源;例如,一个基于JDBC的库需要访问一个JDBC数据源`DataSource`;一个基于JPA的库需要访问一个`EntityManager`。 +完成这一操作的最简单的方式使用`@Autowired,`, `@Inject`, `@Resource` 或`@PersistenceContext`等这样的资源依赖注入的注解。下面有一个JPA库的例子: + + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Repository + public class JpaMovieFinder implements MovieFinder { + + @PersistenceContext + private EntityManager entityManager; + + // ... + + } +---- + +如果你使用了经典Hibernate的API,那么就可以注入SessionFactory: + + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Repository + public class HibernateMovieFinder implements MovieFinder { + + private SessionFactory sessionFactory; + + @Autowired + public void setSessionFactory(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } + + // ... + + } +---- + +在最后一个例子中,我们将介绍对典型JDBC的支持。你可以将`DataSource`注入到已创建的`JdbcTemplate`的初始化方法中,然后其他数据访问支持类(如`SimpleJdbcCall` 等)就可以使用这个数据源`DataSource`了。 + + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Repository + public class JdbcMovieFinder implements MovieFinder { + + private JdbcTemplate jdbcTemplate; + + @Autowired + public void init(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + // ... + + } +---- + +[NOTE] +==== +要想详细了解如何配置程序的上下文才能发挥这些注解的优势,请参阅每种持久化技术的具体的白皮书。 +==== \ No newline at end of file diff --git a/src/asciidoc/chapter/13.2.jdbc.adoc b/src/asciidoc/chapter/13.2.jdbc.adoc index 225ba86067d5fcd0c004a6fac3143059f6187e85..907bb257e35ca9dc25616f2b72e2a7c652b628d6 100644 --- a/src/asciidoc/chapter/13.2.jdbc.adoc +++ b/src/asciidoc/chapter/13.2.jdbc.adoc @@ -1,42 +1,23 @@ -[[jdbc-packages]] -==== Package hierarchy -The Spring Framework's JDBC abstraction framework consists of four different packages, -namely `core`, `datasource`, `object`, and `support`. - -The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class and its -various callback interfaces, plus a variety of related classes. A subpackage named -`org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and -`SimpleJdbcCall` classes. Another subpackage named -`org.springframework.jdbc.core.namedparam` contains the `NamedParameterJdbcTemplate` -class and the related support classes. See <>, <>, and -<> - -The `org.springframework.jdbc.datasource` package contains a utility class for easy -`DataSource` access, and various simple `DataSource` implementations that can be used -for testing and running unmodified JDBC code outside of a Java EE container. A -subpackage named `org.springfamework.jdbc.datasource.embedded` provides support for -creating in-memory database instances using Java database engines such as HSQL and H2. -See <> and <> - -The `org.springframework.jdbc.object` package contains classes that represent RDBMS -queries, updates, and stored procedures as thread safe, reusable objects. See -<>.This approach is modeled by JDO, although of course objects returned by -queries are "disconnected" from the database. This higher level of JDBC abstraction -depends on the lower-level abstraction in the `org.springframework.jdbc.core` package. - -The `org.springframework.jdbc.support` package provides `SQLException` translation -functionality and some utility classes. Exceptions thrown during JDBC processing are -translated to exceptions defined in the `org.springframework.dao` package. This means -that code using the Spring JDBC abstraction layer does not need to implement JDBC or -RDBMS-specific error handling. All translated exceptions are unchecked, which gives you -the option of catching the exceptions from which you can recover while allowing other -exceptions to be propagated to the caller. See <>. - - - - -[[jdbc-core]] -=== Using the JDBC core classes to control basic JDBC processing and error handling - - - +[[jdbc-packages]] +==== 包结构 +Spring 框架的JDBC抽象层由四个不同的包组成,即 `core`、 `datasource`、 `object` 和 `support`。 + +`org.springframework.jdbc.core` 这个包里包含了 `JdbcTemplate` 类、该类的回调接口,以及其他相关类。 +子包 `org.springframework.jdbc.core.simple` 包含`SimpleJdbcInsert` 和 `SimpleJdbcCall` 类。 +另一个子包 `org.springframework.jdbc.core.namedparam` 包含 `NamedParameterJdbcTemplate` + 类及其相关支持类。详情参见<>、<>、和 <>。 + +`org.springframework.jdbc.datasource` 这个包里包含了一个简化`DataSource` 访问的工具类及`DataSource`的简化实现类。这些实现使得在Java EE容器外不用修改代码即可测试和运行我们的JDBC代码。这个包中还有一个名为`org.springfamework.jdbc.datasource.embedded`的子包,该子包为使用如HSQL和H2这样的Java数据库引擎创建内存数据库提供了支持。 +详情参见 <> 和 <>。 + + +`org.springframework.jdbc.object`这个包包含了代表RDBMS中查询,修改和将存储过程作为可复用的线程安全对象存储的类。 详情参见<>.尽管查询返回的对象必然是“断开”了数据库的,这种方式是仍以JDO规范建模的。这种更高层级的JDBC抽象依赖于`org.springframework.jdbc.core`包中更低层级的JDBC抽象。 + +`org.springframework.jdbc.support`这个包提供了`SQLException` 转换功能和一些相关工具类。在JDBC执行过程中抛出的异常被转换为`org.springframework.dao`包中定义的异常。换句话说,使用了Sping JDBC抽象层的代码就不需要对JDBC或某种数据库特有的错误进行处理。所有被转换后的异常都是非受检异常--这给了我们选择的能力,使得在捕获那些可以被恢复的异常的同时允许其他异常被传递给调用者。详情请见<>。 + + + + +[[jdbc-core]] +=== 使用JDBC核心类控制基本的JDBC的运行过程和错误处理 + diff --git a/src/asciidoc/chapter/16.1.spring-web.adoc b/src/asciidoc/chapter/16.1.spring-web.adoc index 5e4dd496f295385dacef030422f52e6ac718d1f4..4f16b5c4b14fa8b8ec41b6b15bd74e117c0672be 100644 --- a/src/asciidoc/chapter/16.1.spring-web.adoc +++ b/src/asciidoc/chapter/16.1.spring-web.adoc @@ -1,216 +1,198 @@ -[[mvc-multipart]] -=== Spring's multipart (file upload) support - - - -[[mvc-multipart-introduction]] -==== Introduction -Spring's built-in multipart support handles file uploads in web applications. You enable -this multipart support with pluggable `MultipartResolver` objects, defined in the -`org.springframework.web.multipart` package. Spring provides one `MultipartResolver` -implementation for use with http://jakarta.apache.org/commons/fileupload[__Commons -FileUpload__] and another for use with Servlet 3.0 multipart request parsing. - -By default, Spring does no multipart handling, because some developers want to handle -multiparts themselves. You enable Spring multipart handling by adding a multipart -resolver to the web application's context. Each request is inspected to see if it -contains a multipart. If no multipart is found, the request continues as expected. If a -multipart is found in the request, the `MultipartResolver` that has been declared in -your context is used. After that, the multipart attribute in your request is treated -like any other attribute. - - - -[[mvc-multipart-resolver-commons]] -==== Using a MultipartResolver with __Commons FileUpload__ - -The following example shows how to use the `CommonsMultipartResolver`: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - - - - ----- - -Of course you also need to put the appropriate jars in your classpath for the multipart -resolver to work. In the case of the `CommonsMultipartResolver`, you need to use -`commons-fileupload.jar`. - -When the Spring `DispatcherServlet` detects a multi-part request, it activates the -resolver that has been declared in your context and hands over the request. The resolver -then wraps the current `HttpServletRequest` into a `MultipartHttpServletRequest` that -supports multipart file uploads. Using the `MultipartHttpServletRequest`, you can get -information about the multiparts contained by this request and actually get access to -the multipart files themselves in your controllers. - - - -[[mvc-multipart-resolver-standard]] -==== Using a MultipartResolver with __Servlet 3.0__ - -In order to use Servlet 3.0 based multipart parsing, you need to mark the -`DispatcherServlet` with a `"multipart-config"` section in `web.xml`, or with a -`javax.servlet.MultipartConfigElement` in programmatic Servlet registration, or in case -of a custom Servlet class possibly with a `javax.servlet.annotation.MultipartConfig` -annotation on your Servlet class. Configuration settings such as maximum sizes or -storage locations need to be applied at that Servlet registration level as Servlet 3.0 -does not allow for those settings to be done from the MultipartResolver. - -Once Servlet 3.0 multipart parsing has been enabled in one of the above mentioned ways -you can add the `StandardServletMultipartResolver` to your Spring configuration: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - ----- - - - -[[mvc-multipart-forms]] -==== Handling a file upload in a form -After the `MultipartResolver` completes its job, the request is processed like any -other. First, create a form with a file input that will allow the user to upload a form. -The encoding attribute ( `enctype="multipart/form-data"`) lets the browser know how to -encode the form as multipart request: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - Upload a file please - - -

Please upload a file

-
- - - -
- - ----- - -The next step is to create a controller that handles the file upload. This controller is -very similar to a <>, except that we -use `MultipartHttpServletRequest` or `MultipartFile` in the method parameters: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Controller - public class FileUploadController { - - @RequestMapping(value = "/form", method = RequestMethod.POST) - public String handleFormUpload(@RequestParam("name") String name, - @RequestParam("file") MultipartFile file) { - - if (!file.isEmpty()) { - byte[] bytes = file.getBytes(); - // store the bytes somewhere - return "redirect:uploadSuccess"; - } - - return "redirect:uploadFailure"; - } - - } ----- - -Note how the `@RequestParam` method parameters map to the input elements declared in the -form. In this example, nothing is done with the `byte[]`, but in practice you can save -it in a database, store it on the file system, and so on. - -When using Servlet 3.0 multipart parsing you can also use `javax.servlet.http.Part` for -the method parameter: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Controller - public class FileUploadController { - - @RequestMapping(value = "/form", method = RequestMethod.POST) - public String handleFormUpload(@RequestParam("name") String name, - @RequestParam("file") Part file) { - - InputStream inputStream = file.getInputStream(); - // store bytes from uploaded file somewhere - - return "redirect:uploadSuccess"; - } - - } ----- - - - -[[mvc-multipart-forms-non-browsers]] -==== Handling a file upload request from programmatic clients -Multipart requests can also be submitted from non-browser clients in a RESTful service -scenario. All of the above examples and configuration apply here as well. However, -unlike browsers that typically submit files and simple form fields, a programmatic -client can also send more complex data of a specific content type -- for example a -multipart request with a file and second part with JSON formatted data: - -[literal] -[subs="verbatim,quotes"] ----- -POST /someUrl -Content-Type: multipart/mixed - ---edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp -Content-Disposition: form-data; name="meta-data" -Content-Type: application/json; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -{ - "name": "value" -} ---edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp -Content-Disposition: form-data; name="file-data"; filename="file.properties" -Content-Type: text/xml -Content-Transfer-Encoding: 8bit -... File Data ... ----- - -You could access the part named "meta-data" with a `@RequestParam("meta-data") String -metadata` controller method argument. However, you would probably prefer to accept a -strongly typed object initialized from the JSON formatted data in the body of the -request part, very similar to the way `@RequestBody` converts the body of a -non-multipart request to a target object with the help of an `HttpMessageConverter`. - -You can use the `@RequestPart` annotation instead of the `@RequestParam` annotation for -this purpose. It allows you to have the content of a specific multipart passed through -an `HttpMessageConverter` taking into consideration the `'Content-Type'` header of the -multipart: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @RequestMapping(value="/someUrl", method = RequestMethod.POST) - public String onSubmit(**@RequestPart("meta-data") MetaData metadata, - @RequestPart("file-data") MultipartFile file**) { - - // ... - - } ----- - -Notice how `MultipartFile` method arguments can be accessed with `@RequestParam` or with -`@RequestPart` interchangeably. However, the `@RequestPart("meta-data") MetaData` method -argument in this case is read as JSON content based on its `'Content-Type'` header and -converted with the help of the `MappingJackson2HttpMessageConverter`. - - - - +[[mvc-multipart]] +=== Spring's multipart (文件上传) 支持 + + + +[[mvc-multipart-introduction]] +==== Introduction +Spring's built-in multipart support handles file uploads in web applications. You enable +this multipart support with pluggable `MultipartResolver` objects, defined in the +`org.springframework.web.multipart` package. Spring provides one `MultipartResolver` +implementation for use with http://jakarta.apache.org/commons/fileupload[__Commons +FileUpload__] and another for use with Servlet 3.0 multipart request parsing. + +By default, Spring does no multipart handling, because some developers want to handle +multiparts themselves. You enable Spring multipart handling by adding a multipart +resolver to the web application's context. Each request is inspected to see if it +contains a multipart. If no multipart is found, the request continues as expected. If a +multipart is found in the request, the `MultipartResolver` that has been declared in +your context is used. After that, the multipart attribute in your request is treated +like any other attribute. + + + +[[mvc-multipart-resolver-commons]] +==== Using a MultipartResolver with __Commons FileUpload__ + +下面的例子将展示如何使用 `CommonsMultipartResolver`: + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + + + + +---- + +当然,为了multipart resolver 能够正常运行,需要在类路径添加一些jar包. +对于 `CommonsMultipartResolver` 而言, 你需要使用 +`commons-fileupload.jar`. + +当 Spring `DispatcherServlet` 检测到一个 multi-part 请求时, 就激活在上下文定义的resolver +并移交请求. 然后,resolver 包装当前 `HttpServletRequest` 成支持multipart文件上传的 `MultipartHttpServletRequest`. +通过 `MultipartHttpServletRequest`, 你可以获取当前请求所包含multiparts信息,实际上你也可以在controllers获取多个multipart文件. + + +[[mvc-multipart-resolver-standard]] +==== Using a MultipartResolver with __Servlet 3.0__ + +为了使用基于Servlet 3.0 的 multipart 解析, 在 `web.xml` 中, `DispatcherServlet` 需要用 `"multipart-config"` 标记, +或者使用 `javax.servlet.MultipartConfigElement` 以编程的方式注册, +或者在自定义的servlet中使用 `javax.servlet.annotation.MultipartConfig`注解. +文件上传大小或者存储的地方需要在Servlet注册级别上设置,因为Servlet 3.0不允许这些被 MultipartResolver 设置. + +一旦 Servlet 3.0 multipart parsing 已经启用了上述的方式之一, +就可以在Spring配置中添加 `StandardServletMultipartResolver`: + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + +---- + + + +[[mvc-multipart-forms]] +==== 在表单中处理一个上传文件 +在完成添加 `MultipartResolver` 之后, 这个请求就会和普通请求一样被处理. +首先, 创建一个带上传文件的表单. +设置( `enctype="multipart/form-data"`) 告诉浏览器将表单编码成 multipart request: + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + Upload a file please + + +

Please upload a file

+
+ + + +
+ + +---- + +下一步是创建一个 controller 处理上传文件. +需要在请求参数中使用 `MultipartHttpServletRequest` 或者 `MultipartFile`, 这个 controller 和 +<>非常相似: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Controller + public class FileUploadController { + + @RequestMapping(value = "/form", method = RequestMethod.POST) + public String handleFormUpload(@RequestParam("name") String name, + @RequestParam("file") MultipartFile file) { + + if (!file.isEmpty()) { + byte[] bytes = file.getBytes(); + // 将bytes保存 + return "redirect:uploadSuccess"; + } + + return "redirect:uploadFailure"; + } + + } +---- + +注意 `@RequestParam` 将方法参数映射输入元素的声明形式. +在这个例子中, 对 `byte[]` 并没有做什么操作, 但是在实践中你可以保存在数据库, 存储在文件系统, 等等. + +当使用 Servlet 3.0 multipart 解析时,你也可以使用 `javax.servlet.http.Part` 作为方法参数: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Controller + public class FileUploadController { + + @RequestMapping(value = "/form", method = RequestMethod.POST) + public String handleFormUpload(@RequestParam("name") String name, + @RequestParam("file") Part file) { + + InputStream inputStream = file.getInputStream(); + // 将上传的bytes保存 + + return "redirect:uploadSuccess"; + } + + } +---- + + + +[[mvc-multipart-forms-non-browsers]] +==== Handling a file upload request from programmatic clients +在RESTful service场景中,Multipart requests 可以从一个没有浏览器的客户端提交. +上面的例子和配置同样可以在此适用. +但是, 与通常提交文件和简单表单域的浏览器不同, +一个programmatic clients 还可以发送一个特定内容类型的更复杂的数据, +比如说一个 multipart 请求可以第一部分包含文件第二部分包含 JSON 格式的数据: + +[literal] +[subs="verbatim,quotes"] +---- +POST /someUrl +Content-Type: multipart/mixed + +--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp +Content-Disposition: form-data; name="meta-data" +Content-Type: application/json; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +{ + "name": "value" +} +--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp +Content-Disposition: form-data; name="file-data"; filename="file.properties" +Content-Type: text/xml +Content-Transfer-Encoding: 8bit +... File Data ... +---- + +你可以通过 `@RequestParam("meta-data") String +metadata`获取 名字为 "meta-data" 的参数. +但是,你可能更喜欢接收一个从 JSON 格式数据强制转换的对象, +这和 `@RequestBody` 在 `HttpMessageConverter` 的协助下在 non-multipart 请求中转换目标对象非常相似. + +为了达到这个目的,你可以使用 `@RequestPart` 注解 代替 `@RequestParam` 注解. +通过一个 `HttpMessageConverter` 分析 multipart 的头部 `'Content-Type'`, +可以让你获取 multipart 的特定内容: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @RequestMapping(value="/someUrl", method = RequestMethod.POST) + public String onSubmit(**@RequestPart("meta-data") MetaData metadata, + @RequestPart("file-data") MultipartFile file**) { + + // ... + + } +---- +注意 `MultipartFile` 方法参数通过 `@RequestParam` 或 `@RequestPart` 的互换方式. +在基于 `'Content-Type'` 头和 `MappingJackson2HttpMessageConverter`的情况下 +`@RequestPart("meta-data") MetaData` 方法参数被读取为JSON. diff --git a/src/asciidoc/chapter/16.12.spring-web.adoc b/src/asciidoc/chapter/16.12.spring-web.adoc index f2f4be0f43b0582692b43fe7f487d2e7d0e8bbc1..418ca2d80635cb55f1d700ddf8c719d96c20de7d 100644 --- a/src/asciidoc/chapter/16.12.spring-web.adoc +++ b/src/asciidoc/chapter/16.12.spring-web.adoc @@ -1,27 +1,26 @@ [[mvc-config-view-controller]] -==== View Controllers -This is a shortcut for defining a `ParameterizableViewController` that immediately -forwards to a view when invoked. Use it in static cases when there is no Java controller -logic to execute before the view generates the response. +==== 视图控制器 -An example of forwarding a request for `"/"` to a view called `"home"` in Java: +这是定义一个 `ParameterizableViewController` 的快捷方式。当调用该控制器时,它会立即转发给一个视图。应该对静态视图使用它:即在视图生成响应之前,Java 控制器没有要执行的逻辑。 + +下面的例子展示了在 Java 配置方式下,如何将映射到 `"/"` 的请求转发给名为 `"home"` 的视图: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @Configuration - @EnableWebMvc - public class WebConfig extends WebMvcConfigurerAdapter { +@Configuration +@EnableWebMvc +public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/").setViewName("home"); - } + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/").setViewName("home"); + } - } +} ---- -And the same in XML use the `` element: +在 XML 配置方式下则使用 `` 元素: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -29,94 +28,86 @@ And the same in XML use the `` element: ---- - [[mvc-config-view-resolvers]] -==== View Resolvers -The MVC config simplifies the registration of view resolvers. +==== 视图解析器 + +MVC 配置简化了视图解析器的注册。 -The following is a Java config example that configures content negotiation view -resolution using FreeMarker HTML templates and Jackson as a default `View` for -JSON rendering: +下面的代码是一个 Java 配置的示例。它配置了内容协商的视图解决方案,其中使用 FreeMarker HTML 模板和 Jackson 作为默认的 `View` 来渲染 JSON。 [source,java,indent=0] [subs="verbatim,quotes"] ---- - @Configuration - @EnableWebMvc - public class WebConfig extends WebMvcConfigurerAdapter { +@Configuration +@EnableWebMvc +public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void configureViewResolvers(ViewResolverRegistry registry) { - registry.enableContentNegotiation(new MappingJackson2JsonView()); - registry.jsp(); - } + @Override + public void configureViewResolvers(ViewResolverRegistry registry) { + registry.enableContentNegotiation(new MappingJackson2JsonView()); + registry.jsp(); + } - } +} ---- -And the same in XML: +在 XML 配置中实现相同配置: [source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + + ---- -Note however that FreeMarker, Velocity, Tiles, and Groovy Markup also require -configuration of the underlying view technology. +注意,无论是 FreeMarker、Velocity、Tiles、Groovy Markup 和脚本模板还需要配置底层的视图技术。 -The MVC namespace provides dedicated elements. For example with FreeMarker: +MVC 命名空间提供了相应的元素。比如,使用 FreeMarker: [source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - - + + + + + + + + + + + + ---- -In Java config simply add the respective "Configurer" bean: +在 Java 配置方式下,添加一个对应的 "Configurer" bean即可: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @Configuration - @EnableWebMvc - public class WebConfig extends WebMvcConfigurerAdapter { - - @Override - public void configureViewResolvers(ViewResolverRegistry registry) { - registry.enableContentNegotiation(new MappingJackson2JsonView()); - registry.freeMarker().cache(false); - } - - @Bean - public FreeMarkerConfigurer freeMarkerConfigurer() { - FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); - configurer.setTemplateLoaderPath("/WEB-INF/"); - return configurer; - } - - } ----- - - - +@Configuration +@EnableWebMvc +public class WebConfig extends WebMvcConfigurerAdapter { + + @Override + public void configureViewResolvers(ViewResolverRegistry registry) { + registry.enableContentNegotiation(new MappingJackson2JsonView()); + registry.freeMarker().cache(false); + } + + @Bean + public FreeMarkerConfigurer freeMarkerConfigurer() { + FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); + configurer.setTemplateLoaderPath("/WEB-INF/"); + return configurer; + } + +} +---- \ No newline at end of file diff --git a/src/asciidoc/chapter/16.13.spring-web.adoc b/src/asciidoc/chapter/16.13.spring-web.adoc index 3f085a585744e922c64418dd0178fbb84ed41c1c..7c2939ef55b07a076815b426291386a347b7e9df 100644 --- a/src/asciidoc/chapter/16.13.spring-web.adoc +++ b/src/asciidoc/chapter/16.13.spring-web.adoc @@ -1,16 +1,7 @@ [[mvc-config-static-resources]] -==== Serving of Resources -This option allows static resource requests following a particular URL pattern to be -served by a `ResourceHttpRequestHandler` from any of a list of `Resource` locations. -This provides a convenient way to serve static resources from locations other than the -web application root, including locations on the classpath. The `cache-period` property -may be used to set far future expiration headers (1 year is the recommendation of -optimization tools such as Page Speed and YSlow) so that they will be more efficiently -utilized by the client. The handler also properly evaluates the `Last-Modified` header -(if present) so that a `304` status code will be returned as appropriate, avoiding -unnecessary overhead for resources that are already cached by the client. For example, -to serve resource requests with a URL pattern of `/resources/**` from a -`public-resources` directory within the web application root you would use: +==== 资源服务 + +该选项允许由 `ResourceHttpRequestHandler` 来处理遵循特定 URL 模式的静态资源请求,这些请求可以是来自一系列任意的 `Resource` 路径。它提供了一种处理静态资源的便利方式,静态资源可以处于任意位置(包括类路径下的位置)而不仅仅是 web 应用程序的根路径。`cache-period` 属性可以用来设置很久以后才到期的 header(比如 Page Speed 和 YSlow 等优化工具建议设置为一年),以便客户端可以更为有效的利用它们。该处理器还会正确计算 `Last-Modified` header(如果存在的话),因此将返回一个对应的 `304` 状态码,以避免不必要的客户端缓存资源的开销。例如,将 URL 模式 `/resources/**` 的资源请求映射到 web 应用程序根路径下的 `public-resources` 目录,应该使用下列配置: [source,java,indent=0] [subs="verbatim"] @@ -27,7 +18,7 @@ to serve resource requests with a URL pattern of `/resources/**` from a } ---- -And the same in XML: +在 XML 配置中实现相同配置: [source,xml,indent=0] [subs="verbatim"] @@ -35,25 +26,24 @@ And the same in XML: ---- -To serve these resources with a 1-year future expiration to ensure maximum use of the -browser cache and a reduction in HTTP requests made by the browser: +为了确保最大程度地利用浏览器缓存和减少浏览器发出的 HTTP 请求,可以使这些资源在一年后到期: [source,java,indent=0] [subs="verbatim"] ---- - @Configuration - @EnableWebMvc - public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926); - } + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926); + } - } + } ---- -And in XML: +XML 配置: [source,xml,indent=0] [subs="verbatim"] @@ -61,13 +51,9 @@ And in XML: ---- -The `mapping` attribute must be an Ant pattern that can be used by -`SimpleUrlHandlerMapping`, and the `location` attribute must specify one or more valid -resource directory locations. Multiple resource locations may be specified using a -comma-separated list of values. The locations specified will be checked in the specified -order for the presence of the resource for any given request. For example, to enable the -serving of resources from both the web application root and from a known path of -`/META-INF/public-web-resources/` in any jar on the classpath use: +查看 <> 来了解更多细节。 + +`mapping` 属性必须符合 Ant 模式,以便 `SimpleUrlHandlerMapping` 可以使用它,`location` 属性必须指定一个或多个有效的资源目录路径。可以使用逗号分隔的值列表来指定多个资源路径。对每一个请求,都会根据资源出现的顺序来检查指定的路径。例如,来开启来自 web 应用程序根路径和类路径下任意 jar 中的已知的 `/META-INF/public-web-resources/` 路径,使用下列 Java 配置: [source,java,indent=0] [subs="verbatim"] @@ -85,7 +71,7 @@ serving of resources from both the web application root and from a known path of } ---- -And in XML: +XML 配置: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -93,27 +79,13 @@ And in XML: ---- -When serving resources that may change when a new version of the application is -deployed it is recommended that you incorporate a version string into the mapping -pattern used to request the resources so that you may force clients to request the -newly deployed version of your application's resources. Support for versioned URLs is -built into the framework and can be enabled by configuring a resource chain -on the resource handler. The chain consists of one more `ResourceResolver` -instances followed by one or more `ResourceTransformer` instances. Together they -can provide arbitrary resolution and transformation of resources. +资源服务可能会发生变化。当部署了一个新版本的应用程序时,建议你在用于请求资源的映射模式中添加一个版本字符串,以便强制客户请求应用程序资源的新部署版本。框架支持版本化 URL,可以在资源处理器上配置资源链(resource chain)来进行启用。一个或多个 `ResourceResolver` 实例后可以跟一个或多个 `ResourceTransformer` 实例,这就组成了资源链。它们在一起可以对资源进行任意的解析和转换。 -The built-in `VersionResourceResolver` can be configured with different strategies. -For example a `FixedVersionStrategy` can use a property, a date, or other as the version. -A `ContentVersionStrategy` uses an MD5 hash computed from the content of the resource -(known as "fingerprinting" URLs). +可以为内建的 `VersionResourceResolver` 配置不同的策略。例如,`FixedVersionStrategy` 可以使用一个属性、日期等作为版本。`ContentVersionStrategy` 使用通过资源内容计算而来的 MD5 哈希值(称为 “指纹” URL)。 -`ContentVersionStrategy` is a good default choice to use except in cases where -it cannot be used (e.g. with JavaScript module loaders). You can configure -different version strategies against different patterns as shown below. Keep in mind -also that computing content-based versions is expensive and therefore resource chain -caching should be enabled in production. +使用 `ContentVersionStrategy` 是一个优秀的默认选择,除非在某些情况下不能使用它(例如,使用了 JavaScript 模块加载器)。针对不同的模式,你可以配置不同的版本策略,如下所示。还要注意的是,基于内容的计算需要一定开销,因此在生产环境下应该启用资源链的缓存。 -Java config example; +Java 配置示例: [source,java,indent=0] [subs="verbatim"] @@ -133,7 +105,7 @@ Java config example; } ---- -XML example: +XML 示例: [source,xml,indent=0] [subs="verbatim"] @@ -150,14 +122,6 @@ XML example: ---- -In order for the above to work the application must also -render URLs with versions. The easiest way to do that is to configure the -`ResourceUrlEncodingFilter` which wraps the response and overrides its `encodeURL` method. -This will work in JSPs, FreeMarker, Velocity, and any other view technology that calls -the response `encodeURL` method. Alternatively, an application can also inject and -use directly the `ResourceUrlProvider` bean, which is automatically declared with the MVC -Java config and the MVC namespace. - - - +为了使上述配置能够工作,应用程序还必须渲染带有版本的 URL。最简单的方式是配置 `ResourceUrlEncodingFilter`,它对响应进行包装并重写了其 `encodeURL` 方法。 包括 JSP、FreeMarker、Velocity,以及其他所有调用了响应的 `encodeURL` 方法的视图技术,这种方式都能工作。另外,也可以注入 `ResourceUrlProvider` bean,应用程序会直接使用它。可以使用 MVC Java 配置和 MVC 命名空间来自动声明这个 bean。 +`WebJarsResourceResolver` 也支持 Webjar。如果类路径下存在 `"org.webjars:webjars-locator"` 库的话,会自动注册 `WebJarsResourceResolver`。该解析器允许资源链解析未知版本的库,例如 `"GET /jquery/jquery.min.js"` 这个 HTTP GET 请求会返回 `"/jquery/1.2.0/jquery.min.js"` 这个资源。该解析器还支持在模板中重写资源 URL,例如 `