New in Spring Framework 5

https://www.ibm.com/developerworks/cn/java/j-whats-new-in-spring-framework-5-theedom/index.html

The General Availability (GA) release of Spring 5 in September 2017 marked the first major Spring Framework release since December 2013. It offers some long-awaited improvements, and also employs a whole new programming paradigm based on the reactive principles stated in the Reactive Manifesto .

This release is the most exciting Spring Framework release in a long time. Compatible with Java™ 8 and JDK 9, Spring 5 integrates reactive streams to provide a disruptive approach to endpoint and web application development.

Granted, reactive programming isn't just the subject of this release, it's a major feature that has many developers excited. There is a growing need for disaster recovery and reactive services that can scale seamlessly against load fluctuations, and Spring 5 addresses this need well.

This article will provide a comprehensive introduction to Spring 5. I'll cover the baseline upgrade of the Java SE 8 and Java EE 7 APIs, Spring 5's new reactive programming model, HTTP/2  support, and Spring's full support for functional programming via Kotlin. I'll also briefly cover testing and performance enhancements, and conclude with general revisions to the Spring core and container.

Upgrade to Java SE 8 and Java EE 7

Until now, Spring Framework still supported some deprecated Java versions, but Spring 5 has been freed from the old baggage. Its codebase has been improved to take full advantage of Java 8 features, and the framework requires Java 8 as the minimum JDK version.

Spring 5 is fully Java 9 compatible on the classpath (and modulepath), and it passes the JDK 9 test suite. This is good news for Java 9 enthusiasts, as Spring will be able to use Java 9 right after its release.

At the API level, Spring 5 is compatible with Java EE 8 technology, meeting the requirements for Servlet 4.0, Bean Validation 2.0, and the new JSON Binding API. The minimum requirement for the Java EE API is V7, which introduces minor versions for the Servlet, JPA, and Bean Validation APIs.

reactive programming model

The most exciting new feature of Spring 5 is its reactive programming model. The Spring 5 Framework is built on a reactive foundation and is fully asynchronous and non-blocking. With a small number of threads, the new event loop execution model scales vertically.

The framework employs reactive streams to provide a mechanism for propagating negative pressure in reactive components. Negative pressure is a concept that ensures that data from multiple producers does not overwhelm consumers.

Spring WebFlux, the reactive core of Spring 5, provides developers with two programming models designed for Spring Web programming: an annotation-based model and the Functional Web Framework ( WebFlux.fn).

The annotation-based model is a modern alternative to Spring WebMVC, which is built on a reactive foundation, and the Functional Web Framework is @Controller an alternative to the annotation-based programming model. These models all operate on the same reactive foundation, which adapts non-blocking HTTP to a reactive streaming API.

Programming with annotations

WebMVC programmers should be very familiar with Spring 5's annotation-based programming model. Spring 5 adjusts the programming model of WebMVC to  @Controller adopt the same annotations.

In Listing 1, the BookController class provides two methods that respond to HTTP requests for a book list and HTTP requests for a given  id book. Note the object ( Mono and  Flux) returned by the resource method. These objects are reactive types that implement the  interfaces in the reactive streams specification  . PublisherTheir job is to handle the flow of data. Mono Objects handle a stream of only 1 element, while Flux represents a stream of N elements.

Listing 1. Reactive controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class BookController {
 
     @GetMapping("/book")
     Flux< Book > list() {
         return this.repository.findAll();
     }
 
     @GetMapping("/book/{id}")
     Mono< Book > findById(@PathVariable String id) {
         return this.repository.findOne(id);
     }
 
     // Plumbing code omitted for brevity
}

This is an annotation for Spring Web programming. Now we use a functional web framework to solve the same problem.

functional programming

Spring 5's new functional approach delegates requests to handler functions that take a server request instance and return a reactive type. Listing 2 demonstrates this process, with  a listBook neutralization  getBook method similar to the function in Listing 1.

Listing 2. Listing 2. BookHandler function class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BookHandler {
 
     public Mono< ServerResponse > listBooks(ServerRequest request) {
         return ServerResponse.ok()
             .contentType(APPLICATION_JSON)
             .body(repository.allPeople(), Book.class);
     }
     
     public Mono< ServerResponse > getBook(ServerRequest request) {
         return repository.getBook(request.pathVariable("id"))
             .then(book -> ServerResponse.ok()
             .contentType(APPLICATION_JSON)
             .body(fromObject(book)))
             .otherwiseIfEmpty(ServerResponse.notFound().build());
     }
     // Plumbing code omitted for brevity
}

Route client requests to handler functions by routing functions to match HTTP request verbs and media types. Listing 3 shows the book resource endpoint URI delegating the call to the appropriate handler function:

Listing 3. Router function
1
2
3
4
5
6
7
8
9
BookHandler handler = new BookHandler();
 
RouterFunction< ServerResponse > personRoute =
     route(
         GET("/books/{id}")
         .and(accept(APPLICATION_JSON)), handler::getBook)
         .andRoute(
     GET("/books")
         .and(accept(APPLICATION_JSON)), handler::listBooks);

The data repository behind these examples also supports a full reactive experience, enabled by Spring Data's support for reactive Couchbase, Reactive MongoDB, and Cassandra.

Reactive programming with REST endpoints

The new programming model breaks away from the traditional Spring WebMVC model and introduces some nice new features.

For example, the WebFlux module  RestTemplate provides a completely non-blocking, reactive alternative to  WebClient. Listing 4 creates one  WebClientand calls  books the endpoint to request a book given  id as  1234 .

Listing 4. Calling the REST endpoint via WebClient
1
2
3
4
5
6
Mono< Book > book = WebClient.create("http://localhost:8080")
       .get()
       .url("/books/{id}", 1234)
       .accept(APPLICATION_JSON)
       .exchange(request)
       .then(response -> response.bodyToMono(Book.class));

HTTP/2 support

Spring Framework 5.0 将提供专门的 HTTP/2 特性支持,还支持人们期望出现在 JDK 9 中的新 HTTP 客户端。尽管 HTTP/2 的服务器推送功能已通过 Jetty servlet 引擎的 ServerPushFilter 类向 Spring 开发人员公开了很长一段时间,但如果发现 Spring 5 中开箱即用地提供了 HTTP/2 性能增强,Web 优化者们一定会为此欢呼雀跃。

Java EE Servlet 规范预计将于 2017 年第 4 季度发布,Servlet 4.0 支持将在 Spring 5.1 中提供。到那时,HTTP/2 特性将由 Tomcat 9.0、Jetty 9.3 和 Undertow 1.4 原生提供。

Kotlin 和 Spring WebFlux

Kotlin 是一种来自 JetBrains 的面向对象的语言,它支持函数式编程。它的主要优势之一是与 Java 有非常高的互操作性。通过引入对 Kotlin 的专门支持,Spring 在 V5 中全面吸纳了这一优势。它的函数式编程风格与 Spring WebFlux 模块完美匹配,它的新路由 DSL 利用了函数式 Web 框架以及干净且符合语言习惯的代码。可以像清单 5 中这样简单地表达端点路由:

清单 5. Kotlin 的用于定义端点的路由 DSL
1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
fun apiRouter() = router {
     (accept(APPLICATION_JSON) and "/api").nest {
         "/book".nest {
             GET("/", bookHandler::findAll)
             GET("/{id}", bookHandler::findOne)
         }
         "/video".nest {
             GET("/", videoHandler::findAll)
             GET("/{genre}", videoHandler::findByGenre)
         }
     }
}

使用 Kotlin 1.1.4+ 时,还添加了对 Kotlin 的不可变类的支持(通过带默认值的可选参数),以及对完全支持 null 的 API 的支持。

使用 Lambda 表达式注册 bean

作为传统 XML 和 JavaConfig 的替代方案,现在可以使用 lambda 表达式注册 Spring bean,使 bean 可以实际注册为提供者。清单 6 使用 lambda 表达式注册了一个 Book bean。

清单 6. 将 Bean 注册为提供者
1
2
3
4
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Book.class, () -> new
               Book(context.getBean(Author.class))
         );

Spring WebMVC 支持最新的 API

全新的 WebFlux 模块提供了许多新的、令人兴奋的功能,但 Spring 5 也迎合了愿意继续使用 Spring MVC 的开发人员的需求。Spring 5 中更新了模型-视图-控制器框架,以兼容 WebFlux 和最新版的 Jackson 2.9 和 Protobuf 3.0,甚至包括对新的 Java EE 8 JSON-Binding API 的支持。

除了 HTTP/2 特性的基础服务器实现之外,Spring WebMVC 还通过 MVC 控制器方法的一个参数来支持 Servlet 4.0 的 PushBuilder。最后,WebMVC 全面支持 Reactor 3.1 的 Flux 和 Mono 对象,以及 RxJava 1.3 和 2.1,它们被视为来自 MVC 控制器方法的返回值。这项支持的最终目的是支持 Spring Data 中的新的反应式 WebClient 和反应式存储库。

使用 JUnit 5 执行条件和并发测试

Spring 5 的测试套件通过多种方式得到了增强,但最明显的是它对 JUnit 5 的支持。现在可以在您的单元测试中利用 Java 8 中提供的函数式编程特性。清单 7 演示了这一支持:

清单 7. 清单 7.JUnit 5 全面接纳了 Java 8 流和 lambda 表达式
1
2
3
4
5
6
7
@Test
void givenStreamOfInts_SumShouldBeMoreThanFive() {
     assertTrue(Stream.of(20, 40, 50)
       .stream()
       .mapToInt(i -> i)
       .sum() > 110, () -> "Total should be more than 100");
}

Spring 5 继承了 JUnit 5 在 Spring TestContext Framework 内实现多个扩展 API 的灵活性。举例而言,开发人员可以使用 JUnit 5 的条件测试执行注解 @EnabledIf 和 @DisabledIf 来自动计算一个 SpEL (Spring Expression Language) 表达式,并适当地启用或禁用测试。借助这些注解,Spring 5 支持以前很难实现的复杂的条件测试方案。Spring TextContext Framework 现在能够并发执行测试。

使用 Spring WebFlux 执行集成测试

Spring Test 现在包含一个 WebTestClient,后者支持对 Spring WebFlux 服务器端点执行集成测试。WebTestClient 使用模拟请求和响应来避免耗尽服务器资源,并能直接绑定到 WebFlux 服务器基础架构。

WebTestClient 可绑定到真实的服务器,或者使用控制器或函数。在清单 8 中,WebTestClient 被绑定到 localhost:

清单 8. 绑定到 localhost 的 WebTestClient
1
2
3
4
WebTestClient testClient = WebTestClient
   .bindToServer()
   .baseUrl("http://localhost:8080")
   .build();

在清单 9 中,测试了 RouterFunction

清单 9. 将 WebTestClient 绑定到 RouterFunction
1
2
3
4
5
6
7
8
9
10
11
RouterFunction bookRouter = RouterFunctions.route(
   RequestPredicates.GET("/books"),
   request -> ServerResponse.ok().build()
);
  
WebTestClient
   .bindToRouterFunction(bookRouter)
   .build().get().uri("/books")
   .exchange()
   .expectStatus().isOk()
   .expectBody().isEmpty();

包清理和弃用

Spring 5 中止了对一些过时 API 的支持。遭此厄运的还有 Hibernate 3 和 4,为了支持 Hibernate 5,它们遭到了弃用。另外,对 Portlet、Velocity、JasperReports、XMLBeans、JDO 和 Guava 的支持也已中止。

包级别上的清理工作仍在继续:Spring 5 不再支持 beans.factory.accessjdbc.support.nativejdbcmock.staticmock(来自 spring-aspects 模块)或 web.view.tiles2M。Tiles 3 现在是 Spring 的最低要求。

对 Spring 核心和容器的一般更新

Spring Framework 5 改进了扫描和识别组件的方法,使大型项目的性能得到提升。目前,扫描是在编译时执行的,而且向 META-INF/spring.components 文件中的索引文件添加了组件坐标。该索引是通过一个为项目定义的特定于平台的应用程序构建任务来生成的。

标有来自 javax 包的注解的组件会添加到索引中,任何带 @Index 注解的类或接口都会添加到索引中。Spring 的传统类路径扫描方式没有删除,而是保留为一种后备选择。有许多针对大型代码库的明显性能优势,而托管许多 Spring 项目的服务器也会缩短启动时间。

Spring 5 还添加了对 @Nullable 的支持,后者可用于指示可选的注入点。使用者现在必须准备接受 null 值。此外,还可以使用此注解来标记可以为 null 的参数、字段和返回值。@Nullable 主要用于 IntelliJ IDEA 等 IDE,但也可用于 Eclipse 和 FindBugs,它使得在编译时处理 null 值变得更方便,而无需在运行时发送 NullPointerExceptions

Spring Logging 还提升了性能,自带开箱即用的 Commons Logging 桥接器。现在已通过资源抽象支持防御性编程,为 getFile访问提供了 isFile 指示器。

结束语

Spring 5 的首要特性是新的反应式编程模型,这代表着对提供可无缝扩展、基于 Spring 的响应式服务的重大保障。随着人们对 Spring 5 的采用,开发人员有望看到反应式编程将会成为使用 Java 语言的 Web 和企业应用程序开发的未来发展道路。

未来的 Spring Framework 版本将继续反映这一承诺,因为 Spring Security、Spring Data 和 Spring Integration 有望采用反应式编程的特征和优势。

总之,Spring 5 代表着一次大受 Spring 开发人员欢迎的范例转变,同时也为其他框架指出了一条发展之路。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324603214&siteId=291194637