搜索词>>Spring WebFlux 耗时0.0060
  • Spring WebFlux

    Spring WebFlux,spring框架5.0将会新增的web增强框架,这里主要讲述什么是Spring WebFlux以及Spring WebFlux的新功能,Spring WebFlux与Spring MVC的关系。<h2><img alt="spring boot2.0 spring5.0" class="img-thumbnail" src="/resources/assist/images/blog/760dfcfe492b4e4b8c5fa8f2f899ea29.png" /><br /> 1.Spring WebFlux简介</h2>   包含在Spring框架中的原始Web框架Spring Web MVC是为Servlet API和Servlet容器而设计的。 反过来的堆栈,Web框架,Spring WebFlux,稍后在版本5.0中添加。 它是完全非阻塞的,支持反向流反向压力,并在诸如Netty,Undertow和Servlet 3.1+容器之类的服务器上运行。<br /> <br />   这两个Web框架镜像了Spring模块的名称spring-webmvc和spring-webflux,并在Spring框架中并存。 每个模块都是可选的。 应用可以使用一个或另一个模块,或者在一些情况下可以使用例如。 具有反应WebClient的Spring MVC控制器。<br /> <br />   除了Web框架之外,Spring WebFlux还提供了用于执行HTTP请求的WebClient,用于测试Web端点的WebTestClient以及客户端和服务器反应的WebSocket支持。 <h2>2.为何要新增一个web框架?</h2>   答案的一部分是需要一个非阻塞的Web栈来处理与少量线程的并发,并以较少的硬件资源进行扩展。 Servlet 3.1确实为非阻塞I / O提供了API。但是,使用它会远离Servlet API的其他部分,其中合同是同步的(Filter,Servlet)或阻塞(getParameter,getPart)。这是新的通用API作为任何非阻塞运行时基础的动机。这一点很重要,因为Netty这样的服务器在异步非阻塞空间中已经建立起来了。<br /> <br />   答案的另一部分是功能编程。很像在Java 5中添加注释创造的机会 - 例如。注释的REST控制器或单元测试,在Java 8中添加lambda表达式为Java中的功能API创造了机会。这对于非阻塞应用程序和连续样式API是一个福音 - 由CompletableFuture和ReactiveX普及,允许异步逻辑的声明性组合。在编程模型级别,Java 8启用了Spring WebFlux,可以为注释控制器提供功能性的Web端点。 <h2>3.Reactive什么和为什么?</h2>   我们触及非阻塞和功能,但为什么反应性,我们的意思是什么?<br /> <br />   术语“反应性”是指围绕响应变化构建的编程模型 - 网络组件对I / O事件的响应,UI控制器对鼠标事件的反应等。在这个意义上,非阻塞是反应的,因为不是被阻止现在在作为操作完成或数据变得可用的响应通知的模式。<br /> <br />   还有一个重要的机制,我们在春季团队与“反应”相关联,这是非阻碍的背压。在同步的命令式代码中,阻止调用可以作为强制呼叫者等待的背部压力的自然形式。在非阻塞代码中,重要的是控制事件的速率,以便快速生产者不会压倒其目的地。<br /> <br />   活动流是一种小规模,也是Java 9中采用的,它定义了具有背压的异步组件之间的交互。例如,作为发布者的数据存储库可以产生数据,作为订阅者的HTTP服务器可以写入响应。活动流的主要目的是允许用户控制发布商产生数据的速度或速度。<br /> <br /> <strong>提示:</strong><br /> 常见的问题:如果发布商不能放慢下去呢?<br /> 活动流的目的只是建立机制和边界。 如果发布商不能放慢速度,那么它必须决定是缓冲还是丢弃还是失败。 <h2>4.Reactive API</h2>   活动流对互操作性起着重要作用。图书馆和基础架构组件感兴趣,但作为应用程序API不太有用,因为它的级别太低。什么应用程序需要更高级别和更丰富的功能API来构成异步逻辑 - 类似于Java 8 Stream API,但不仅仅是集合。这是反应库的作用。<br /> <br />   反应堆是Spring WebFlux选择的活动库。它提供了Mono和Flux API类型,可以通过与运算符的ReactiveX词汇对齐的丰富的运算符来处理0..1和0..N的数据序列。反应堆是一个反应流库,因此所有的运营商都支持非阻塞的背压。反应堆非常重视服务器端的Java。它与Spring紧密合作开发。<br /> <br />   WebFlux要求Reactor作为核心依赖关系,但它可以通过Reactive Streams与其他反应库互操作。作为一般规则,WebFlux API接受普通的Publisher作为输入,在内部适应Reactor类型,使用它们,然后返回Flux或Mono作为输出。所以您可以将任何Publisher作为输入传递,您可以对输出应用操作,但是您需要调整输出以与另一个活动库一起使用。无论何时 - 例如注释控制器,WebFlux透明地适用于使用RxJava或其他反应库。有关详细信息,请参阅反应库。 <h2>5.编程模型</h2> Spring-Web模块包含基于Spring WebFlux - HTTP抽象,Reactive Streams服务器适配器,反应式编解码器和核心Web API的反应性基础,其角色与Servlet API相当,但具有非阻塞语义。<br /> <br /> 在此基础上,Spring WebFlux提供了两种编程模型的选择: <ul> <li>注释控制器 - 与Spring MVC相一致,并且基于与spring-web模块相同的注释。 Spring MVC和WebFlux控制器都支持反应(Reactor,RxJava)返回类型,因此不容易将它们分开。一个显着的区别是,WebFlux还支持反应性@RequestBody参数。</li> <li>功能端点 - 基于lambda的轻量级功能编程模型。将其视为小型库或应用程序可用于路由和处理请求的一组实用程序。注释控制器的巨大差异在于,应用程序负责从开始到结束的请求处理,并通过注释声明意图并被回调。</li> </ul> <h2>6.如何选择一个web框架?</h2>   你应该使用Spring MVC还是WebFlux?我们来看几个不同的观点。<br /> <br />   如果您有一个Spring MVC应用程序工作正常,则不需要更改。命令编程是编写,理解和调试代码的最简单方法。您最多可以选择库,因为历史上绝大多数都是阻止。<br /> <br />   如果您已经购买了一个非阻塞的Web堆栈,Spring WebFlux可以提供与这个空间中的其他人一样的执行模式优势,并且还提供了一些选择的服务器 - Netty,Tomcat,Jetty,Undertow,Servlet 3.1+容器,编程模型 - 注释控制器和功能Web端点,以及可选的反应库 - 反应器,RxJava或其他。<br /> <br />   如果您对使用Java 8 lambdas或Kotlin的轻量级功能Web框架感兴趣,则可以使用Spring WebFlux功能Web端点。对于具有较少复杂要求的较小应用或微服务,这也可能是更好的选择,可以从更大的透明度和控制中受益。<br /> <br />   在微服务架构中,您可以使用Spring MVC或Spring WebFlux控制器或Spring WebFlux功能端点组合应用程序。在两个框架中支持相同的基于注释的编程模型,使得重新使用知识更容易,同时为正确的工作选择正确的工具。<br /> <br />   评估应用程序的一种简单方法是检查其依赖性。如果您使用阻塞持久性API(JPA,JDBC)或网络API,则Spring MVC至少是常见架构的最佳选择。在技​​术上,Reactor和RxJava都可以在单独的线程上执行阻塞调用,但是您不会充分利用非阻塞的Web堆栈。<br /> <br />   如果您有一个Spring MVC应用程序调用远程服务,请尝试反向WebClient。您可以从Spring MVC控制器方法直接返回反应类型(Reactor,RxJava或其他)。每个呼叫的延迟或呼叫之间的相互依赖性越大,益处越大。 Spring MVC控制器也可以调用其他无效组件。<br /> <br />   如果你有一个庞大的团队,记住转向非阻塞,功能和声明性编程的陡峭的学习曲线。在没有完全切换的情况下启动的实际方法是使用反应性WebClient。除了这个开始之外,测量的好处。我们预计,对于广泛的应用,转移是不必要的。<br /> <br />   如果您不确定要查找哪些优点,首先了解非阻塞性I / O的工作原理(例如,单线程Node.js上的并发性不是矛盾)及其影响。标签行是“具有较少硬件的缩放”,但不能保证效果,而不是一些网络I / O可能会变慢或不可预测。这个Netflix博客文章是一个很好的资源。 <h2>7.Spring WebFlux Server的选择</h2>   Netnet,Undertow,Tomcat,Jetty和Servlet 3.1+容器支持Spring WebFlux。 每个服务器都适用于一个通用的Reactive Streams API。 Spring WebFlux编程模型基于该通用API。<br />    <strong>提示:</strong><br /> <em>  常见的问题:Tomcat和Jetty如何在两个堆栈中使用?<br />   Tomcat和Jetty的核心是无阻拦。 这是Servlet API,它添加了一个阻塞的外观。 从版本3.1开始,Servlet API为非阻塞I / O添加了一个选择。 然而,其使用需要注意避免<br />   其他同步和阻塞部件。 因此,Spring的反应式Web堆栈具有低级Servlet适配器来桥接到活动流,但是Servlet API否则不会直接使用。</em><br /> <br />   默认情况下,Spring Boot 2使用Netty WebFlux,因为Netty在异步非阻塞空间中被广泛使用,并且还提供可共享资源的客户端和服务器。 通过比较Servlet 3.1非阻塞I / O没有太多的使用,因为使用它的条数是如此之高。 Spring WebFlux打开了一条实用的通路。<br /> <br />   Spring Boot中的默认服务器选择主要是关于开箱即用的体验。 应用程序仍然可以选择任何其他受支持的服务器,这些服务器也对性能进行了高度优化,完全无阻塞,并适应了反应流反向压力。 在Spring Boot中,进行切换是微不足道的。 <h2>8.性能与规模</h2>   表现有很多特点和意义。 反应和非阻塞通常不会使应用程序运行更快。 在某些情况下,他们可以使用WebClient来并行执行远程调用。 总的来说,需要更多的工作来处理非阻塞的方式,并且可以稍微增加所需的处理时间。<br /> <br />   反应和非阻塞的关键预期好处是能够以小的固定数量的线程和较少的内存进行扩展。 这使得应用程序在负载下更具弹性,因为它们以更可预测的方式扩展。 为了观察这些好处,您需要有一些延迟,包括慢速和不可预测的网络I / O的混合。 这是反应堆栈开始显示其优势的地方,差异可能很大。
  • spring boot webflux client实战

    spring boot webflux client实战,webclient是spring webflux的一个小组件。对于Java的http通讯来说,webclient是非常简单易用的。<h2>引言</h2>     在spring 5.0发布的同时发布了webflux功能类似spring mvc。底层实现以及关注点不同。今天我们主要讲解spring webflux中的一个组件webclient。webclient是spring webflux的一个小组件。对于Java的http通讯来说,webclient是非常简单易用的。比起apache的httpclient组件更方便的集成到项目中 <h2>一.创建一个spring boot 项目包含webflux组件</h2> <h3>1.1创建Spring  boot项目并加入webflux组件依赖</h3> <img alt="选择依赖" class="img-thumbnail" src="/resources/assist/images/blog/039cefbac57f44eaa8bed5f3e1ff1bd2.png" /> <h2>1.2项目结构图展示</h2> <img alt="项目结构图" class="img-thumbnail" src="/resources/assist/images/blog/dbc64afd017e47e2a14e62f2c65fc74d.png" /> <h2>二.创建一个间的web服务</h2> <h3>2.1创建spring webflux基于Java的配置</h3> WebConfig: <pre> <code class="language-java">package net.xqlee.project.config; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.format.datetime.DateFormatter; import org.springframework.web.reactive.config.EnableWebFlux; import org.springframework.web.reactive.config.ResourceHandlerRegistry; import org.springframework.web.reactive.config.WebFluxConfigurer; /** * 基于Java代码配置启用Spring WebFlux * * @author xqlee * */ @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; /**** * 配置常用的转换器和格式化配置(与Spring MVC 5配置方式一样) */ @Override public void addFormatters(FormatterRegistry registry) { // 添加日期格式化转换 DateFormatter dateFormatter = new DateFormatter(DATE_FORMAT); registry.addFormatter(dateFormatter); } /**** * 资源路径映射配置(与Spring MVC 5一样,只是引入的类不同) */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/public", "classpath:/static/"); } } </code></pre> 通过spring 官方文档查询可以知道,spring webflux的配置使用和Spring mvc的配置使用方法基本相同。上面只是个简单的配置。 <h3>2.2创建一个controller用来做webclient的测试请求服务</h3> TestController: <pre> <code class="language-java">package net.xqlee.project.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import net.xqlee.project.pojo.Person; @Controller public class TestController { private static final Logger log = LoggerFactory.getLogger(TestController.class); @PostMapping("persion/getPersion/{id}.json") @ResponseBody public Person getPersion(@PathVariable("id") String id) { log.info("ID:" + id); return new Person("1", "leftso", 1, "重庆.大竹林"); } } </code></pre> 上面包含一个简单对象Persion: <pre> <code class="language-java">package net.xqlee.project.pojo; public class Person { public Person() { } public Person(String id, String name, int age, String address) { super(); this.id = id; this.name = name; this.age = age; this.address = address; } String id; String name; int age; String address; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } </code></pre> 好了一个spring boot的webflux服务已经创建完毕。接下来将进行webclient的调用方法。 <h2>三.创建webclient</h2> 3.1创建一个测试的类 <pre> <code class="language-java">package net.xqlee.project; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.WebClient; import net.xqlee.project.pojo.Person; import reactor.core.publisher.Mono; /** * reactive webClient * * @author xqlee * */ public class TestClient { private static final Logger log = LoggerFactory.getLogger(TestClient.class); public static void main(String[] args) { WebClient client = WebClient.create("http://localhost:8080/"); Mono<Person> result = client.post()// 请求方法,get,post... .uri("persion/getPersion/{id}.json", "123")// 请求相对地址以及参数 .accept(MediaType.APPLICATION_JSON).retrieve()// 请求类型 .bodyToMono(Person.class);// 返回类型 Person person = result.block(); log.info(JSONObject.wrap(person).toString()); } } </code></pre> 上面已经创建了一个webclient的请求。请求的地址是刚才我们创建的一个简单的测试服务。<br /> 首先运行spring boot项目启动测试服务: <pre> <code class="language-html"> . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.0.M6) 2017-11-19 13:53:09.290 INFO 6976 --- [ main] n.x.p.DemoSpringbootWebflux2Application : Starting DemoSpringbootWebflux2Application on DESKTOP-2RG0A6O with PID 6976 (D:\workplace\eclipse_mvn\demo-springboot-webflux-2\target\classes started by xqlee in D:\workplace\eclipse_mvn\demo-springboot-webflux-2) 2017-11-19 13:53:09.295 INFO 6976 --- [ main] n.x.p.DemoSpringbootWebflux2Application : No active profile set, falling back to default profiles: default 2017-11-19 13:53:09.377 INFO 6976 --- [ main] .r.c.ReactiveWebServerApplicationContext : Refreshing org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext@59402b8f: startup date [Sun Nov 19 13:53:09 CST 2017]; root of context hierarchy 2017-11-19 13:53:10.480 INFO 6976 --- [ main] s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/persion/getPersion/{id}.json],methods=[POST]}" onto public net.xqlee.project.pojo.Person net.xqlee.project.controller.TestController.getPersion(java.lang.String) 2017-11-19 13:53:10.542 INFO 6976 --- [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped URL path [/resources/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler] 2017-11-19 13:53:10.611 INFO 6976 --- [ main] o.s.w.r.r.m.a.ControllerMethodResolver : Looking for @ControllerAdvice: org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext@59402b8f: startup date [Sun Nov 19 13:53:09 CST 2017]; root of context hierarchy 2017-11-19 13:53:11.256 INFO 6976 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2017-11-19 13:53:11.523 INFO 6976 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /0:0:0:0:0:0:0:0:8080 2017-11-19 13:53:11.524 INFO 6976 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080 2017-11-19 13:53:11.530 INFO 6976 --- [ main] n.x.p.DemoSpringbootWebflux2Application : Started DemoSpringbootWebflux2Application in 2.554 seconds (JVM running for 3.059) </code></pre> 通过启动日志我们可以看到。默认情况下webflux已经不是使用tomcat容器启动项目。而是替换为netty容器启动的项目。<br /> <br /> 运行我们上面创建的webclient:<br /> 重点截图1:<br /> <img alt="webclient" class="img-thumbnail" src="/resources/assist/images/blog/b317c94a94254e9a9560f4d3374dc25c.png" />这里可以看到打印的请求日志http相关的头部信息<br /> <br /> 重点截图2:<br /> <img alt="webclient执行结果" class="img-thumbnail" src="/resources/assist/images/blog/322c8a6cf0e941f7ab70b1829a62718e.png" />上面可以看到调用的返回已经自动载入Persion对象。<br /> <br /> 通过上面的使用不难发现使用weblcient在Java中进行http通讯是非常方便的。而且支持nio异步操作。在效率上也应该有所提升。在使用微服务的时候,是否可以考虑服务之间通讯使用weblcient?
  • Spring WebFlux 项目实战 在Spring WebFlux中创建多个RouterFunctions

    Spring WebFlux 项目实战 在Spring WebFlux中创建多个RouterFunctions,在这篇文章中,我们将着眼于在Spring WebFlux中将多个路由器功能定义到不同的逻辑域。<h2>引言</h2> <blockquote> <p>在这篇文章中,我们将着眼于在Spring WebFlux中将多个路由器功能定义到不同的逻辑域。如果您创建“微服务”,这可能不会成为问题,因为您很可能只在每个服务的单个域中工作,但如果您不是,那么您可能需要在应用程序中包含多个域用户或您自己的服务可以与之交互。这样做的代码就像我希望的那样简单,可以用几句话来解释。为了使这篇文章更有趣一些,我们将看一些使这一切成为可能的Spring代码。</p> </blockquote> 如果你是WebFlux的新手,我推荐看看我以前的博客,[<a rel="" target="_blank"href="http://www.leftso.com/blog/379.html" rel="" target="_blank">Spring WebFlux 项目实战</a>],在那里我写了一些关于这个主题的完整例子和解释。<br />   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">所以我们先假设置场景。你的应用程序中有两个不同的域,例如人员和位置。你可能想要让它们不仅在逻辑上而且在你的代码中彼此分离。要做到这一点,您需要一种方法来定义与其他域相互隔离的路由。这是我们将在这篇文章中看到的内容。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">如果你认为你已经知道这个问题的答案,那么你可能是对的。这真的很简单。尽管如此,让我们继续努力吧。要为人员域创建路由,请创建一个<code>RouterFunction</code>映射到相关处理函数的bean,如下所示。</span></span></span></p> <pre> <code class="language-java">@Configuration public class MyRouter { // works for a single bean @Bean public RouterFunction<ServerResponse> routes(PersonHandler personHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry); } }</code></pre> <br /> 这将创建到各种处理函数的路由<code>PersonHandler</code>。<br /> 所以,现在我们要添加位置逻辑的路由。我们可以简单地将路由添加到此bean,如下所示。 <pre> <code class="language-java">@Configuration public class MyRouter { // not ideal! @Bean public RouterFunction<ServerResponse> routes(PersonHandler personHandler, LocationHandler locationHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry) .andRoute(GET("/locations/{id}").and(accept(APPLICATION_JSON)), locationHandler::get); } }</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这个bean现在包含一个引用,<code>LocationHandler</code>以便可以设置位置路由。这个解决方案的问题是它需要将代码耦合在一起。而且,如果你需要添加更多的处理程序,你很快就会被注入到这个bean中的依赖关系的数量所淹没。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">解决这个问题的方法是创建多个<code>RouterFunction</code>bean。而已。因此,如果我们在人员域中创建一个,<code>PersonRouter</code>并且在位置域中指定一个<code>LocationRouter</code>,则每个人都可以定义他们需要的路由,而Spring将完成剩下的任务。这是有效的,因为Spring遍历应用程序上下文并找到或创建任何<code>RouterFunction</code>bean并将它们合并为一个函数供以后使用。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">使用这些信息,我们可以编写下面的代码。</span></span></span><br />  </p> <pre> <code class="language-java">@Configuration public class PersonRouter { // solution @Bean public RouterFunction<ServerResponse> peopleRoutes(PersonHandler personHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry); } }</code></pre> 和 <pre> <code class="language-java">@Configuration public class LocationRouter { // solution @Bean public RouterFunction<ServerResponse> locationRoutes(LocationHandler locationHandler) { return RouterFunctions.route(GET("/locations/{id}").and(accept(APPLICATION_JSON)), locationHandler::get); } }</code></pre> <code>PersonRouter</code>可以与其他人/与人相关的代码保存,并<code>LocationRouter</code>可以做同样的事情。<br />   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">为了让这个更有趣,为什么这个工作?</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff"><code>RouterFunctionMapping</code>是检索<code>RouterFunction</code>应用程序上下文中创建的所有bean 的类。该<code>RouterFunctionMapping</code>豆创建中<code>WebFluxConfigurationSupport</code>这是春天WebFlux配置震中。通过<code>@EnableWebFlux</code>在配置类中包含注释或者依靠自动配置,一系列事件开始并收集我们所有的<code>RouterFunction</code>s就是其中之一。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">下面是这<code>RouterFunctionMapping</code>堂课。我已经删除了它的构造函数和一些方法来使这里的代码片段更容易消化。</span></span></span></p> <pre> <code class="language-java">public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean { @Nullable private RouterFunction<?> routerFunction; private List<HttpMessageReader<?>> messageReaders = Collections.emptyList(); // constructors // getRouterFunction // setMessageReaders @Override public void afterPropertiesSet() throws Exception { if (CollectionUtils.isEmpty(this.messageReaders)) { ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create(); this.messageReaders = codecConfigurer.getReaders(); } if (this.routerFunction == null) { initRouterFunctions(); } } /** * Initialized the router functions by detecting them in the application context. */ protected void initRouterFunctions() { if (logger.isDebugEnabled()) { logger.debug("Looking for router functions in application context: " + getApplicationContext()); } List<RouterFunction<?>> routerFunctions = routerFunctions(); if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) { routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction)); } this.routerFunction = routerFunctions.stream() .reduce(RouterFunction::andOther) .orElse(null); } private List<RouterFunction<?>> routerFunctions() { SortedRouterFunctionsContainer container = new SortedRouterFunctionsContainer(); obtainApplicationContext().getAutowireCapableBeanFactory().autowireBean(container); return CollectionUtils.isEmpty(container.routerFunctions) ? Collections.emptyList() : container.routerFunctions; } // getHandlerInternal private static class SortedRouterFunctionsContainer { @Nullable private List<RouterFunction<?>> routerFunctions; @Autowired(required = false) public void setRouterFunctions(List<RouterFunction<?>> routerFunctions) { this.routerFunctions = routerFunctions; } } }</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">检索所有路由的路径开始于创建bean <code>afterPropertiesSet</code>后调用<code>RouterFunctionMapping</code>。由于它是内部的<code>RouterFunction</code>,<code>null</code>它会<code>initRouterFunctions</code>触发一系列导致执行的方法<code>routerFunctions</code>。一个新<code>SortedRouterFunctionsContainer</code>的构造(私有静态类)<code>routerFunctions</code>通过注入<code>RouterFunction</code>应用程序上下文中的所有s来设置它的字段。这工作,因为Spring会注入类型的所有豆类<code>T</code>一个当<code>List<T></code>被注入。现在检索到的<code>RouterFunction</code>s被组合在一起以制作一个<code>RouterFunction</code>从现在开始用于将所有传入请求路由到适当处理程序的单个节点。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这里的所有都是它的。总而言之,<code>RouterFunction</code>为不同业务领域定义多个是非常简单的,因为您只需在最有意义的任何区域创建它们,Spring就会关闭并获取所有区域。为了使我们研究的一些魔法神秘化,<code>RouterFunctionMapping</code>以了解<code>RouterFunction</code>我们创建的s是如何收集和组合的,以便它们可以用来将请求路由到处理程序。作为结束语,我确实明白这篇文章在某些方面是相当微不足道的,但有时看起来很明显的信息会非常有帮助。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我建议看看我以前的帖子[<a rel="" target="_blank"href="http://www.leftso.com/blog/379.html" rel="" target="_blank">Spring WebFlux 项目实战</a>]</span></span></span></p>
  • Spring WebFlux教程Hello Word

    Spring WebFlux入门程序hello word。本文主要在于讲解如何创建和运行spring webflux入门程序hello word。其实不难发现和spring mvc相比代码层基本没区别的<h2>1.创建一个spring boot2.0项目并包含spring webflux模块</h2> <br /> eclipse中创建首先需要安装spring的STS工具,之前讲过可以搜索一下。这里直接开始创建项目<br /> <img alt="创建项目第一步" class="img-thumbnail" src="/resources/assist/images/blog/78cd327eb5d443f19b18a222ab6e6f44.png" /><br /> <br /> 注意选择spring boot版本2.0,由于现在还没有正式版本。选择M5版本<br /> <img alt="选择spring boot版本" class="img-thumbnail" src="/resources/assist/images/blog/0b9457cd7aee48379991634b8c81c00f.png" /><br />   <h2>2.项目结构图</h2> <img alt="项目结构图" class="img-thumbnail" src="/resources/assist/images/blog/eec0271552ee47c7a034196b74d07dc3.png" /><br /> 不难发现和之前的1.0没啥区别 <h2>3.Spring WebFlux编写Hello Word</h2> <em><strong>HelloController.java</strong></em> <pre> <code class="language-java">package com.example; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String handle() { return "Hello WebFlux"; } } </code></pre> <br /> 运行Application类与1.x版本一样,启动spring boot项目<br /> 访问地址:http://localhost:8080/hello<br /> <img alt="hello" class="img-thumbnail" src="/resources/assist/images/blog/a77ec83134764d4b8f3ef966ba10b5e6.png" /><br /> <br /> 好啦就这么简单,后面继续深入学习。<br /> <br />  
  • Spring WebFlux 项目实战 spring boot 2.0正式版

    Spring WebFlux 项目实战 spring boot 2.0正式版 源码下载。Spring Boot 2.0最近去了GA,所以我决定写我关于Spring的第一篇文章很长一段时间。自发布以来,我一直在看到越来越多的Spring WebFlux以​​及如何使用它的教程。<h2>引言</h2> <blockquote> <p>Spring Boot 2.0最近去了GA,所以我决定写我关于Spring的第一篇文章很长一段时间。自发布以来,我一直在看到越来越多的Spring WebFlux以​​及如何使用它的教程。但是在阅读完它们并尝试让它自己工作之后,我发现从包含在我阅读的文章和教程中的代码跳转到编写实际上比返回字符串更有趣的事情从后端。现在,我希望我不会在自己的脚下说自己可能会对我在这篇文章中使用的代码做出同样的批评,但这里是我试图给Spring WebFlux的教程,它实际上类似于你可能会在野外使用的东西。</p> </blockquote> <span style="color:#ff0000">项目结构:<br /> <img srcset="" width="" size="" class="img-thumbnail" alt="项目结构图" src="/resources/assist/images/blog/5b14b7c8551a41389f790ace59443849.jpg" /></span> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">在我继续之前,在提及WebFlux之后,究竟是什么呢?Spring WebFlux是Spring MVC的完全非阻塞反应式替代方案。它允许更好的垂直缩放而不增加硬件资源。被动反应它现在使用Reactive Streams来允许从调用返回到服务器的数据的异步处理。这意味着我们将看到更少的<code>List</code>s,<code>Collection</code>甚至单个对象,而不是他们的反应等价物,例如<code>Flux</code>和<code>Mono</code>(来自Reactor)。我不会深入研究Reactive Streams是什么,诚实地说,在我尝试向任何人解释它之前,我需要更加深入地研究它。相反,让我们回过头来关注WebFlux。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">像往常一样,我使用Spring Boot在本教程中编写代码。</span></span></span><br /> <span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">以下是我在这篇文章中使用的依赖关系。</span></span></span><br />  </p> <pre> <code class="language-xml"><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId> <version>2.0.0.RELEASE</version> </dependency> </dependencies></code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">尽管我没有将它包含在上面的依赖代码片段中,但是它<code>spring-boot-starter-parent</code>被使用了,最终可以将其提升到版本<code>2.0.0.RELEASE</code>。本教程是关于WebFlux的,包括这<code>spring-boot-starter-webflux</code>显然是一个好主意。<code>spring-boot-starter-data-cassandra-reactive</code>也被包括在内,因为我们将用它作为示例应用程序的数据库,因为它是少数几个有反应支持的数据库之一(在编写本文时)。通过一起使用这些依赖关系,我们的应用程序可以从前到后完全反应。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">WebFlux引入了一种不同的方式来处理请求,而不是使用Spring MVC 中使用的<code>@Controller</code>或<code>@RestController</code>编程模型。但是,它并没有取代它。相反,它已被更新以允许使用被动类型。这使您可以保持与使用Spring编写相同的格式,但对返回类型进行一些更改,以便返回<code>Flux</code>s或<code>Mono</code>s。下面是一个非常人为的例子。</span></span></span><br />  </p> <pre> <code class="language-java">@RestController public class PersonController { private final PersonRepository personRepository; public PersonController(PersonRepository personRepository) { this.personRepository = personRepository; } @GetMapping("/people") public Flux<Person> all() { return personRepository.findAll(); } @GetMapping("/people/{id}") Mono<Person> findById(@PathVariable String id) { return personRepository.findOne(id); } }</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">对我来说,这看起来非常熟悉,并且从一眼就可以看出它与标准的Spring MVC控制器没有任何区别,但通过阅读方法后,我们可以看到不同的返回类型。在这个例子中<code>PersonRepository</code>必须是一个被动库,因为我们已经能够直接返回他们的搜索查询的结果供参考,被动库会返回一个<code>Flux</code>集合和一个<code>Mono</code>单一的实体。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">注释方法不是我想在这篇文章中关注的内容。这对我们来说不够酷,时髦。没有足够的lambda表达式来满足我们以更有效的方式编写Java的渴望。但Spring WebFlux有我们的支持。它提供了一种替代方法来路由和处理请求到我们的服务器,轻轻地使用lambdas编写路由器功能。我们来看一个例子。</span></span></span><br />  </p> <pre> <code class="language-java">@Configuration public class PersonRouter { @Bean public RouterFunction<ServerResponse> route(PersonHandler personHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry); } }</code></pre> 这些都是<code>PersonHandler</code>我们稍后会看到的方法的所有路线。我们创建了一个将处理我们路由的bean。为了设置路由功能,我们使用了名为的<code>RouterFunctions</code>类为我们提供了一个静态方法,但现在我们只关心它的<code>route</code>方法。以下是该<code>route</code>方法的签名。 <pre> <code class="language-java">public static <T extends ServerResponse> RouterFunction<T> route( RequestPredicate predicate, HandlerFunction<T> handlerFunction) { // stuff }</code></pre>   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">该方法表明,它与a <code>RequestPredicate</code>一起<code>HandlerFunction</code>并输出a <code>RouterFunction</code>。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这<code>RequestPredicate</code>是我们用来指定路由的行为,比如我们处理函数的路径,它是什么类型的请求以及它可以接受的输入类型。由于我使用静态导入将所有内容读得更清晰,所以一些重要信息已经隐藏起来。要创建一个<code>RequestPredicate</code>我们应该使用<code>RequestPredicates</code>(复数),一个静态帮助类为我们提供我们需要的所有方法。就个人而言,我建议静态导入,<code>RequestPredicates</code>否则由于使用<code>RequestPredicates</code>静态方法可能需要的次数,您的代码将会一团糟。在上述例子中,<code>GET</code>,<code>POST</code>,<code>PUT</code>,<code>DELETE</code>,<code>accept</code>和<code>contentType</code>都是静态<code>RequestPredicates</code>方法。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">下一个参数是a <code>HandlerFunction</code>,它是一个功能接口。这里有三件重要的信息,它有一个泛型类型<code><T extends ServerResponse></code>,它的<code>handle</code>方法返回一个<code>Mono<T></code>并且需要一个<code>ServerRequest</code>。使用这些我们可以确定我们需要传递一个返回一个<code>Mono<ServerResponse></code>(或它的一个子类型)的函数。这显然对我们的处理函数返回的内容有严格的约束,因为它们必须满足这个要求,否则它们将不适合以这种格式使用。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">最后的结果是一个<code>RouterFunction</code>。这可以返回并用于路由到我们指定的任何函数。但通常情况下,我们希望一次将很多不同的请求发送给各种处理程序,这是WebFlux迎合的。由于<code>route</code>返回a <code>RouterFunction</code>以及<code>RouterFunction</code>也有其自己的路由方法的事实<code>andRoute</code>,我们可以将这些调用链接在一起并继续添加我们所需的所有额外路由。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">如果我们再回头看一下<code>PersonRouter</code>上面的例子,我们可以看到这些方法是以REST动词命名的,例如<code>GET</code>,<code>POST</code>它们定义了处理程序将要执行的请求的路径和类型。<code>GET</code>例如,如果我们以第一个请求为例,它将<code>/people</code>使用路径变量名称<code>id</code>(path表示的路径变量<code>{id}</code>)和返回内容的类型(具体来说,使用该方法定义的<code>APPLICATION_JSON</code>静态字段from <code>MediaType</code>)进行路由<code>accept</code>。如果使用不同的路径,则不会被处理。如果路径正确但Accept头不是可接受的类型之一,则请求将失败。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">在我们继续之前,我想了解一下<code>accept</code>和<code>contentType</code>方法。这两个设置请求标头都<code>accept</code>与Accept标头和<code>contentType</code>Content-Type 匹配。Accept头定义了响应可接受的媒体类型,因为我们返回的<code>Person</code>对象的JSON表示设置为<code>APPLICATION_JSON</code>(<code>application/json</code>在实际头文件中)是有意义的。Content-Type具有相同的想法,但是却描述了发送请求正文内的媒体类型。这就是为什么只有动词<code>POST</code>和<code>PUT</code>动词才<code>contentType</code>包括在内,因为其他人在他们的身体中没有任何东西。<code>DELETE</code>不包括<code>accept</code>和<code>contentType</code> 所以我们可以得出这样的结论:它既没有期望返回任何东西,也没有在其请求中包含任何东西。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">现在我们知道如何设置路由,让我们看看如何编写处理传入请求的处理程序方法。以下是处理前面示例中定义的路由的所有请求的代码。</span></span></span><br />  </p> <pre> <code class="language-java">@Component public class PersonHandler { private final PersonManager personManager; public PersonHandler(PersonManager personManager) { this.personManager = personManager; } public Mono<ServerResponse> get(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); final Mono<Person> person = personManager.findById(id); return person .flatMap(p -> ok().contentType(APPLICATION_JSON).body(fromPublisher(person, Person.class))) .switchIfEmpty(notFound().build()); } public Mono<ServerResponse> all(ServerRequest request) { return ok().contentType(APPLICATION_JSON) .body(fromPublisher(personManager.findAll(), Person.class)); } public Mono<ServerResponse> put(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); final Mono<Person> person = request.bodyToMono(Person.class); return personManager .findById(id) .flatMap( old -> ok().contentType(APPLICATION_JSON) .body( fromPublisher( person .map(p -> new Person(p, id)) .flatMap(p -> personManager.update(old, p)), Person.class))) .switchIfEmpty(notFound().build()); } public Mono<ServerResponse> post(ServerRequest request) { final Mono<Person> person = request.bodyToMono(Person.class); final UUID id = UUID.randomUUID(); return created(UriComponentsBuilder.fromPath("people/" + id).build().toUri()) .contentType(APPLICATION_JSON) .body( fromPublisher( person.map(p -> new Person(p, id)).flatMap(personManager::save), Person.class)); } public Mono<ServerResponse> delete(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); return personManager .findById(id) .flatMap(p -> noContent().build(personManager.delete(p))) .switchIfEmpty(notFound().build()); } public Mono<ServerResponse> getByCountry(ServerRequest serverRequest) { final String country = serverRequest.pathVariable("country"); return ok().contentType(APPLICATION_JSON) .body(fromPublisher(personManager.findAllByCountry(country), Person.class)); } }</code></pre>   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">有一点非常明显,就是缺少注释。酒吧的<code>@Component</code>注释自动创建一个<code>PersonHandler</code>豆没有其他Spring注解。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我试图将大部分存储库逻辑保留在这个类之外,并且通过经由它所包含的<code>PersonManager</code>代理来隐藏对实体对象的任何引用<code>PersonRepository</code>。如果你对代码感兴趣,<code>PersonManager</code>那么可以在我的<a href="https://github.com/lankydan/spring-boot-webflux/blob/master/src/main/java/com/lankydanblog/tutorial/person/PersonManager.java" rel="external nofollow" style="padding:0px; margin:0px; outline:none; list-style:none; border:0px none; color:#326693; text-decoration:none; transition:all 0.2s ease-in-out" target="_blank" >GitHub上</a>看到,关于它的进一步解释将被排除在这篇文章之外,所以我们可以专注于WebFlux本身。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">好的,回到手头的代码。让我们仔细看看<code>get</code>和<code>post</code>方法来弄清楚发生了什么。</span></span></span><br />  </p> <pre> <code class="language-java">public Mono<ServerResponse> get(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); final Mono<Person> person = personManager.findById(id); return person .flatMap(p -> ok().contentType(APPLICATION_JSON).body(fromPublisher(person, Person.class))) .switchIfEmpty(notFound().build()); }</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">此方法用于从支持此示例应用程序的数据库中检索单个记录。由于Cassandra是选择的数据库,我决定使用<code>UUID</code>每个记录的主键,这使得测试示例更令人讨厌的不幸效果,但没有任何复制和粘贴无法解决的问题。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">请记住,此<code>GET</code>请求的路径中包含路径变量。使用<code>pathVariable</code>的方法<code>ServerRequest</code>传递到我们能够提取它的价值通过提供变量的名称,在这种情况下,方法<code>id</code>。然后将ID转换成一个<code>UUID</code>,如果字符串格式不正确,它会抛出一个异常,我决定忽略这个问题,所以示例代码不会变得混乱。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">一旦我们有了ID,我们就可以查询数据库中是否存在匹配的记录。<code>Mono<Person></code>返回的A 包含映射到a的现有记录,<code>Person</code>或者它保留为空<code>Mono</code>。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">使用返回的,<code>Mono</code>我们可以根据它的存在输出不同的响应。这意味着我们可以将有用的状态代码返回给客户端以跟随主体的内容。如果记录存在,则<code>flatMap</code>返回一个<code>ServerResponse</code>与<code>OK</code>状态。伴随着这种状态,我们希望输出记录,为此,我们在这种情况下指定正文的内容类型<code>APPLICATION_JSON</code>,并将记录添加到记录中。<code>fromPublisher</code>需要我们<code>Mono<Person></code>(这是一个<code>Publisher</code>)与<code>Person</code>课程一起,因此它知道它映射到身体中的是什么。<code>fromPublisher</code>是类的静态方法<code>BodyInserters</code>。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">如果记录不存在,那么流程将移动到<code>switchIfEmpty</code>块中并返回<code>NOT FOUND</code>状态。因为没有发现,身体可以留空,所以我们只是创建<code>ServerResponse</code>那里。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">现在到<code>post</code>处理程序。</span></span></span><br />  </p> <pre> <code class="language-java">public Mono<ServerResponse> post(ServerRequest request) { final Mono<Person> person = request.bodyToMono(Person.class); final UUID id = UUID.randomUUID(); return created(UriComponentsBuilder.fromPath("people/" + id).build().toUri()) .contentType(APPLICATION_JSON) .body( fromPublisher( person.map(p -> new Person(p, id)).flatMap(personManager::save), Person.class)); }</code></pre>   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">即使只是从第一行开始,我们就可以看到,这种<code>get</code>方法的工作方式已经不同了。由于这是一个<code>POST</code>请求,它需要接受我们希望从请求主体持续存在的对象。由于我们试图插入单个记录,因此我们将使用请求的<code>bodyToMono</code>方法<code>Person</code>从正文中检索。如果您正在处理多个记录,则可能需要使用它们<code>bodyToFlux</code>。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我们将<code>CREATED</code>使用<code>created</code>接受a 的方法返回状态<code>URI</code>以确定插入记录的路径。然后,<code>get</code>通过使用该<code>fromPublisher</code>方法将新记录添加到响应主体,然后采用与该方法类似的设置。形成该代码的代码<code>Publisher</code>稍有不同,但输出仍然<code>Mono<Person></code>是一个重要的内容。为了进一步解释如何完成插入<code>Person</code>,从请求传入的内容将被映射到<code>Person</code>使用<code>UUID</code>我们生成的新内容,然后通过<code>save</code>调用传递给新内容<code>flatMap</code>。通过创建一个新的<code>Person</code>我们只将值插入我们允许的Cassandra中,在这种情况下,我们不希望<code>UUID</code>从请求体传入。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">所以说,这是关于处理程序。显然还有其他方法,我们没有经历。它们的工作方式都不相同,但都遵循相同的概念,<code>ServerResponse</code>如果需要,它返回一个包含适当状态代码和记录的体系。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">现在我们已经编写了所有我们需要的代码来获得基本的Spring WebFlux后端运行。剩下的就是将所有配置绑定在一起,这对Spring Boot来说很简单。</span></span></span></p> <pre> <code class="language-java">@SpringBootApplication public class Application { public static void main(String args[]) { SpringApplication.run(Application.class); } }</code></pre>   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我们应该研究如何真正使用代码,而不是结束这篇文章。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">Spring提供了<code>WebClient</code>该类来处理请求而不会阻塞。我们现在可以利用这个来测试应用程序,尽管<code>WebTestClient</code>我们也可以在这里使用它。该<code>WebClient</code>是你可以使用,而不是阻止什么<code>RestTemplate</code>产生反应的应用程序时。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">下面是一些调用在<code>PersonHandler</code>。中定义的处理程序的代码。</span></span></span><br />  </p> <pre> <code class="language-java">public class Client { private WebClient client = WebClient.create("http://localhost:8080"); public void doStuff() { // POST final Person record = new Person(UUID.randomUUID(), "John", "Doe", "UK", 50); final Mono<ClientResponse> postResponse = client .post() .uri("/people") .body(Mono.just(record), Person.class) .accept(APPLICATION_JSON) .exchange(); postResponse .map(ClientResponse::statusCode) .subscribe(status -> System.out.println("POST: " + status.getReasonPhrase())); // GET client .get() .uri("/people/{id}", "a4f66fe5-7c1b-4bcf-89b4-93d8fcbc52a4") .accept(APPLICATION_JSON) .exchange() .flatMap(response -> response.bodyToMono(Person.class)) .subscribe(person -> System.out.println("GET: " + person)); // ALL client .get() .uri("/people") .accept(APPLICATION_JSON) .exchange() .flatMapMany(response -> response.bodyToFlux(Person.class)) .subscribe(person -> System.out.println("ALL: " + person)); // PUT final Person updated = new Person(UUID.randomUUID(), "Peter", "Parker", "US", 18); client .put() .uri("/people/{id}", "ec2212fc-669e-42ff-9c51-69782679c9fc") .body(Mono.just(updated), Person.class) .accept(APPLICATION_JSON) .exchange() .map(ClientResponse::statusCode) .subscribe(response -> System.out.println("PUT: " + response.getReasonPhrase())); // DELETE client .delete() .uri("/people/{id}", "ec2212fc-669e-42ff-9c51-69782679c9fc") .exchange() .map(ClientResponse::statusCode) .subscribe(status -> System.out.println("DELETE: " + status)); } }</code></pre> 不要忘了在<code>Client</code>某个地方实例化,下面是一个很好的偷懒方式来做到这一点! <pre> <code class="language-java">@SpringBootApplication public class Application { public static void main(String args[]) { SpringApplication.run(Application.class); Client client = new Client(); client.doStuff(); } }</code></pre> 首先我们创建一个<code>WebClient</code>。 <pre> <code class="language-java">private final WebClient client = WebClient.create("http://localhost:8080"); </code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">一旦创建,我们就可以开始做它的东西,因此<code>doStuff</code>方法。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我们来分解一下<code>POST</code>发送给后端的请求。</span></span></span><br />  </p> <pre> <code class="language-java">final Mono<ClientResponse> postResponse = client .post() .uri("/people") .body(Mono.just(record), Person.class) .accept(APPLICATION_JSON) .exchange(); postResponse .map(ClientResponse::statusCode) .subscribe(status -> System.out.println("POST: " + status.getReasonPhrase()));</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我写下这个稍有不同,所以你可以看到a <code>Mono<ClientResponse></code>是从发送请求返回的。该<code>exchange</code>方法将HTTP请求发送到服务器。然后,只要响应到达,就会处理响应,如果有的话。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">使用<code>WebClient</code>我们指定我们想要<code>POST</code>使用<code>post</code>当然的方法发送请求。在<code>URI</code>随后与所添加的<code>uri</code>方法(重载的方法,这一个接受一个<code>String</code>但另一个接受<code>URI</code>)。我厌倦了说这个方法做了什么方法,所以,身体的内容随后与Accept头一起添加。最后我们通过电话发送请求<code>exchange</code>。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">请注意,媒体类型<code>APPLICATION_JSON</code>与<code>POST</code>路由器功能中定义的类型相匹配。如果我们要发送不同的类型,比如说<code>TEXT_PLAIN</code>我们会得到一个<code>404</code>错误,因为没有处理程序存在与请求期望返回的内容相匹配的地方。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">使用<code>Mono<ClientResponse></code>通过调用返回<code>exchange</code>,我们可以绘制它的内容给我们所需的输出。在上面的例子中,状态代码被打印到控制台。如果我们回想一下<code>post</code>方法<code>PersonHandler</code>,请记住它只能返回“创建”状态,但如果发送的请求没有正确匹配,则会打印出“未找到”。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我们来看看其他请求之一。</span></span></span><br />  </p> <pre> <code class="language-java">client .get() .uri("/people/{id}", "a4f66fe5-7c1b-4bcf-89b4-93d8fcbc52a4") .accept(APPLICATION_JSON) .exchange() .flatMap(response -> response.bodyToMono(Person.class)) .subscribe(person -> System.out.println("GET: " + person));</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这是我们的典型<code>GET</code>要求。它看起来与<code>POST</code>我们刚刚经历的请求非常相似。主要区别在于<code>uri</code>,请求路径和<code>UUID</code>(作为<code>String</code>在这种情况下)作为参数来取代路径变量<code>{id}</code>并且主体留空。响应如何处理也是不同的。在这个例子中,它提取了响应的主体并将其映射到a <code>Mono<Person></code>并打印出来。这可以在前面的<code>POST</code>例子中完成,但是响应的状态代码对于它的情况更有用。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">对于略有不同的观点,我们可以使用cURL发出请求并查看响应的样子。</span></span></span><br />  </p> <pre> <code class="language-html">CURL -H "Accept:application/json" -i localhost:8080/people HTTP/1.1 200 OK transfer-encoding: chunked Content-Type: application/json [ { "id": "13c403a2-6770-4174-8b76-7ba7b75ef73d", "firstName": "John", "lastName": "Doe", "country": "UK", "age": 50 }, { "id": "fbd53e55-7313-4759-ad74-6fc1c5df0986", "firstName": "Peter", "lastName": "Parker", "country": "US", "age": 50 } ] </code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">响应看起来像这样,显然它会根据您存储的数据而有所不同。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">请注意响应标题。</span></span></span><br />  </p> <pre> <code class="language-html">transfer-encoding: chunked Content-Type: application/json</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">    在<code>transfer-encoding</code>这里表示的是在可用于流式传输的数据块传输的数据。这就是我们需要的,因此客户可以对返回给它的数据采取反应态度。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我认为这应该是一个停止的好地方。我们在这里已经涵盖了相当多的材料,希望能够帮助您更好地理解Spring WebFlux。还有一些其他的话题我想关注WebFlux,但是我会在单独的帖子中做这些,因为我认为这个主题足够长。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">    总之,在这篇文章中,我们非常简要地讨论了为什么你想在典型的Spring MVC后端中使用Spring WebFlux。然后我们看看如何设置路由和处理程序来处理传入的请求。处理程序实现了可以处理大多数REST动词的方法,并在响应中返回了正确的数据和状态代码。最后,我们研究了向后端发送请求的两种方式,一种是使用a <code>WebClient</code>直接在客户端处理输出,另一种使用cURL查看返回的JSON的外观。</span></span></span></p> <br /> <span style="color:#ff0000"><strong>注意源码中使用的数据为:cassandra</strong></span><br /> <br /> <a href="http://www.leftso.com/resource/1004.html" target="_blank" ><strong>项目源码下载</strong></a>
  • Spring WebFlux 和Reactive MongoDB来构建Reactive Rest API

    Spring WebFlux 和Reactive MongoDB来构建Reactive Rest API,Spring 5通过引入一种名为Spring WebFlux的全新响应式框架来支持反应式编程范例。<h2>1.引言</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring 5通过引入一种名为<strong>Spring WebFlux的</strong>全新反应框架来支持响应式编程范例。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring WebFlux是一个自下而上的异步框架。它可以使用Servlet 3.1非阻塞IO API以及其他异步运行时环境(如netty或undertow)在Servlet容器上运行。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">它可以与Spring MVC一起使用。是的,Spring MVC不会去任何地方。这是一个开发人员长期以来使用的流行的Web框架。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">但是你现在可以在新的反应框架和传统的Spring MVC之间做出选择。您可以根据自己的使用情况选择使用它们中的任何一个。</span></span></span><br /> <br />  </p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring WebFlux使用一个名为Reactor的库作为响应支持。Reactor是<a href="https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Reactive Streams</a>规范的一个实现。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Reactor提供两种主要的类型,称为<code>Flux</code>和<code>Mono</code>。这两种类型都实现了<code>Publisher</code>Reactive Streams提供的接口。<code>Flux</code>用于表示0..N个元素的流,<code>Mono</code>用于表示0..1个元素的流。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">虽然Spring使用Reactor作为其大部分内部API的核心依赖,但它也支持在应用程序级别使用RxJava。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"> </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">2.Spring WebFlux支持的编程模型</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring WebFlux支持两种类型的编程模型:</span></span></span></p> <ol> <li>带有<code>@Controller</code>,<code>@RequestMapping</code>和其他注释的基于注释的传统模型,您在Spring MVC中一直使用。</li> <li>基于Java 8 lambda表达式的全新功能样式模型,用于路由和处理请求。</li> </ol> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在本文中,我们将使用传统的基于注释的编程模型。我将在未来的文章中撰写功能风格模型。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><br /> 3.让我们在Spring Boot中构建一个Reactive Restful服务</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在本文中,我们将为迷你Twitter应用程序构建一个Restful API。该应用程序将只有一个称为的域模型<code>Tweet</code>。每个<code>Tweet</code>人都有<code>text</code>一个<code>createdAt</code>领域。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">我们将使用MongoDB作为我们的数据存储以及反应型mongodb驱动程序。我们将构建用于创建,检索,更新和删除Tweet的REST API。所有的REST API都是异步的,并且会返回一个发布者。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">我们还将学习如何将数据从数据库传输到客户端。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">最后,我们将编写集成测试以使用Spring 5提供的新异步WebTestClient测试所有API。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">4.创建项目</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">我们使用Spring Initializr Web应用程序来生成我们的应用程序。按照以下步骤生成项目 -</span></span></span></p> <ol> <li>转到<a href="http://start.spring.io/" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >http://start.spring.io</a></li> <li>选择Spring Boot版本<strong>2.x</strong></li> <li>输入工件的值作为<strong>webflux-demo</strong></li> <li>添加<strong>Reactive Web</strong>和<strong>Reactive MongoDB</strong>依赖项</li> <li>点击<strong>生成项目</strong>生成并下载项目。</li> </ol> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><img srcset="" width="" size="" class="img-thumbnail" alt="spring官网生成项目" src="/resources/assist/images/blog/bc7bae06bc4c4868a7df2ed2f49828c8.png" /></span></span></span><br /> 下载项目后,将其解压缩并导入到您最喜欢的IDE中。该项目的目录结构应该如下所示 -<br /> <span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><img srcset="" width="" size="" class="img-thumbnail" alt="导入spring 官网生成的maven项目" src="/resources/assist/images/blog/94b74bcde26549c2a0f52a246f54b2e5.png" /></span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">配置MongoDB</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以通过简单地将以下属性添加到<code>application.properties</code>文件来配置MongoDB -</span></span></span></p> <pre> <code class="language-html">spring.data.mongodb.uri=mongodb://localhost:27017/webflux_demo</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring Boot将在启动时读取此配置并自动配置数据源。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">创建领域模型</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">让我们创建我们的领域模型 - <code>Tweet</code>。创建一个名为<code>model</code>inside <code>com.example.webfluxdemo</code>package 的新包,然后创建一个名为<code>Tweet.java</code>以下内容的文件-</span></span></span></p> <pre> <code class="language-java">import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Date; @Document(collection = "tweets") public class Tweet { @Id private String id; @NotBlank @Size(max = 140) private String text; @NotNull private Date createdAt = new Date(); public Tweet() { } public Tweet(String text) { this.id = id; this.text = text; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">够简单!Tweet模型包含一个<code>text</code>和一个<code>createdAt</code>字段。该<code>text</code>字段用注释<code>@NotBlank</code>和<code>@Size</code>注释确保它不是空白并且最多有140个字符。<br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">5.创建存储库</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">接下来,我们将创建将用于访问MongoDB数据库的数据访问层。创建一个名为<code>repository</code>inside 的新包<code>com.example.webfluxdemo</code>,然后<code>TweetRepository.java</code>使用以下内容创建一个新文件-</span></span></span></p> <pre> <code class="language-java">import com.example.webfluxdemo.model.Tweet; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.stereotype.Repository; @Repository public interface TweetRepository extends ReactiveMongoRepository<Tweet, String> { }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">该<code>TweetRepository</code>接口扩展<a href="https://docs.spring.io/spring-data/mongodb/docs/2.0.0.RC2/api/org/springframework/data/mongodb/repository/ReactiveMongoRepository.html" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" ><code>ReactiveMongoRepository</code></a>了文档中的各种CRUD方法。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring Boot自动插入在<a href="https://docs.spring.io/spring-data/mongodb/docs/2.0.0.RC2/api/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.html" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" ><code>SimpleReactiveMongoRepository</code></a>运行时调用的此接口的实现。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">因此,您无需编写任何代码就可以轻松获取文档上的所有CRUD方法。以下是一些可用的方法<code>SimpleReactiveMongoRepository</code>-</span></span></span></p> <pre> <code class="language-java">reactor.core.publisher.Flux<T> findAll(); reactor.core.publisher.Mono<T> findById(ID id); <S extends T> reactor.core.publisher.Mono<S> save(S entity); reactor.core.publisher.Mono<Void> delete(T entity);</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">请注意,所有方法都是异步的,并以a <code>Flux</code>或<code>Mono</code>类型的形式返回发布者。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">创建控制器端点</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">最后,让我们编写将暴露给客户端的API。创建一个名为<code>controller</code>inside 的新包<code>com.example.webfluxdemo</code>,然后<code>TweetController.java</code>使用以下内容创建一个新文件-</span></span></span><br />  </p> <pre> <code class="language-java">import com.example.webfluxdemo.model.Tweet; import com.example.webfluxdemo.repository.TweetRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.validation.Valid; @RestController public class TweetController { @Autowired private TweetRepository tweetRepository; @GetMapping("/tweets") public Flux<Tweet> getAllTweets() { return tweetRepository.findAll(); } @PostMapping("/tweets") public Mono<Tweet> createTweets(@Valid @RequestBody Tweet tweet) { return tweetRepository.save(tweet); } @GetMapping("/tweets/{id}") public Mono<ResponseEntity<Tweet>> getTweetById(@PathVariable(value = "id") String tweetId) { return tweetRepository.findById(tweetId) .map(savedTweet -> ResponseEntity.ok(savedTweet)) .defaultIfEmpty(ResponseEntity.notFound().build()); } @PutMapping("/tweets/{id}") public Mono<ResponseEntity<Tweet>> updateTweet(@PathVariable(value = "id") String tweetId, @Valid @RequestBody Tweet tweet) { return tweetRepository.findById(tweetId) .flatMap(existingTweet -> { existingTweet.setText(tweet.getText()); return tweetRepository.save(existingTweet); }) .map(updatedTweet -> new ResponseEntity<>(updatedTweet, HttpStatus.OK)) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @DeleteMapping("/tweets/{id}") public Mono<ResponseEntity<Void>> deleteTweet(@PathVariable(value = "id") String tweetId) { return tweetRepository.findById(tweetId) .flatMap(existingTweet -> tweetRepository.delete(existingTweet) .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))) ) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } // Tweets are Sent to the client as Server Sent Events @GetMapping(value = "/stream/tweets", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<Tweet> streamAllTweets() { return tweetRepository.findAll(); } }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">所有的控制器端点都以Flux或Mono的形式返回一个Publisher。我们将内容类型设置为的最后一个端点非常有趣<code>text/event-stream</code>。它以<strong><a href="https://en.wikipedia.org/wiki/Server-sent_events" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >服务器发送事件</a></strong>的形式将推文<strong><a href="https://en.wikipedia.org/wiki/Server-sent_events" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >发送</a></strong>到像这样的浏览器 -</p> <pre> <code class="language-json">data: {"id":"59ba5389d2b2a85ed4ebdafa","text":"tweet1","createdAt":1505383305602} data: {"id":"59ba5587d2b2a85f93b8ece7","text":"tweet2","createdAt":1505383814847}</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">现在我们正在讨论事件流,您可能会问以下端点是否也返回一个Stream?</p> <pre> <code class="language-java">@GetMapping("/tweets") public Flux<Tweet> getAllTweets() { return tweetRepository.findAll(); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">答案是肯定的。<code>Flux<Tweet></code>代表推文流。但是,默认情况下,它将生成一个JSON数组,因为如果将单个JSON对象流发送给浏览器,那么它将不会是一个有效的JSON文档。除了使用Server-Sent-Events或WebSocket之外,浏览器客户端无法使用流。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">但是,非浏览器客户端可以通过设置<code>Accept</code>标头来请求JSON流<code>application/stream+json</code>,并且响应将是类似于Server-Sent-Events的JSON流,但不需要额外的格式:</span></span></span></p> <pre> <code class="language-json">{"id":"59ba5389d2b2a85ed4ebdafa","text":"tweet1","createdAt":1505383305602} {"id":"59ba5587d2b2a85f93b8ece7","text":"tweet2","createdAt":1505383814847}</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">使用WebTestClient进行集成测试</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring 5还提供了一个异步和被动的http客户端,<code>WebClient</code>用于处理异步和流式API。这是一个被动的选择<code>RestTemplate</code>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">此外,你还可以得到一个<code>WebTestClient</code>写作集成测试。测试客户端可以运行在实时服务器上,也可以用于模拟请求和响应。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">我们将使用WebTestClient为我们的REST API编写集成测试。打开<code>WebfluxDemoApplicationTests.java</code>文件并将以下测试添加到它 -</span></span></span></p> <pre> <code class="language-java">import com.example.webfluxdemo.model.Tweet; import com.example.webfluxdemo.repository.TweetRepository; import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Mono; import java.util.Collections; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class WebfluxDemoApplicationTests { @Autowired private WebTestClient webTestClient; @Autowired TweetRepository tweetRepository; @Test public void testCreateTweet() { Tweet tweet = new Tweet("This is a Test Tweet"); webTestClient.post().uri("/tweets") .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaType.APPLICATION_JSON_UTF8) .body(Mono.just(tweet), Tweet.class) .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8) .expectBody() .jsonPath("$.id").isNotEmpty() .jsonPath("$.text").isEqualTo("This is a Test Tweet"); } @Test public void testGetAllTweets() { webTestClient.get().uri("/tweets") .accept(MediaType.APPLICATION_JSON_UTF8) .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8) .expectBodyList(Tweet.class); } @Test public void testGetSingleTweet() { Tweet tweet = tweetRepository.save(new Tweet("Hello, World!")).block(); webTestClient.get() .uri("/tweets/{id}", Collections.singletonMap("id", tweet.getId())) .exchange() .expectStatus().isOk() .expectBody() .consumeWith(response -> Assertions.assertThat(response.getResponseBody()).isNotNull()); } @Test public void testUpdateTweet() { Tweet tweet = tweetRepository.save(new Tweet("Initial Tweet")).block(); Tweet newTweetData = new Tweet("Updated Tweet"); webTestClient.put() .uri("/tweets/{id}", Collections.singletonMap("id", tweet.getId())) .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaType.APPLICATION_JSON_UTF8) .body(Mono.just(newTweetData), Tweet.class) .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8) .expectBody() .jsonPath("$.text").isEqualTo("Updated Tweet"); } @Test public void testDeleteTweet() { Tweet tweet = tweetRepository.save(new Tweet("To be deleted")).block(); webTestClient.delete() .uri("/tweets/{id}", Collections.singletonMap("id", tweet.getId())) .exchange() .expectStatus().isOk(); } }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">在上面的例子中,我为所有的CRUD API编写了测试。您可以通过转到项目的根目录并键入来运行测试<code>mvn test</code>。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">6.总结</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在本文中,我们学习了使用Spring进行反应式编程的基础知识,并使用Spring WebFlux框架提供的反应式支持构建了一个简单的Restful服务。我们还使用WebTestClient测试了所有Rest API。</span></span></span></p> <blockquote> <p style="margin-left:40px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">提示:<a href="http://www.leftso.com/resource/1012.html" target="_blank" >项目源码下载</a></span></span></span></p> </blockquote>
  • Spring 5 WebClient和WebTestClient使用教程

    Spring 5 WebClient和WebTestClient使用教程,Spring开发人员,您是否曾经觉得需要一个易于使用且高效的流畅功能样式 API 的异步/非阻塞 HTTP客户端? 如果是,那么我欢迎您阅读关于WebClient的文章,WebClient是Spring 5中引入的新的被动HTTP客户端。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">1.引言</span></span></span></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">Spring开发人员,您是否曾经觉得需要一个易于使用且高效的<strong>流畅功能样式</strong> API 的<strong>异步/非阻塞</strong> HTTP客户端?</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果是,那么我欢迎您阅读关于WebClient的文章,WebClient是Spring 5中引入的新的被动HTTP客户端。</span></span></span><br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">2.如何使用WebClient</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebClient是Spring 5的反应性Web框架Spring WebFlux的一部分。要使用WebClient,您需要将<code>spring-webflux</code>模块包含在您的项目中。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>在现有的Spring Boot项目中添加依赖项</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果您有一个现有的Spring Boot项目,则可以<code>spring-webflux</code>通过在该<code>pom.xml</code>文件中添加以下依赖项来添加该模块-</span></span></span></p> <pre> <code class="language-xml"><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency></code></pre> <blockquote> <p style="margin-left:0px; margin-right:0px; text-align:start">请注意,您需要Spring Boot 2.xx版本才能使用Spring WebFlux模块。</p> </blockquote> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>从Scratch创建一个新项目</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果您从头开始创建项目,那么您可以使用<a href="http://start.spring.io/" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Spring Initializr</a>网站的<code>spring-webflux</code>模块生成初始项目-</span></span></span></p> <ol> <li>转到<a href="http://start.spring.io/" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >http://start.spring.io</a>。</li> <li>选择弹簧引导版本<strong>2.xx的</strong>。</li> <li>在依赖项部分添加<strong>反应性Web</strong>依赖项。</li> <li>如果需要,请更改<em>组</em>和<em>工件的</em>详细信息,然后单击生成工程下载项目。</li> </ol> <h2 style="margin-left:0px; margin-right:0px; text-align:start">3.使用WebClient消费远程API</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><em>让我们做一些有趣的事情,并使用WebClient来使用Real World API。</em></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在本文中,我们将使用WebClient来使用<a href="https://developer.github.com/v3/" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Github的API</a>。我们将使用WebClient在用户的Github存储库上执行CRUD操作。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start">创建WebClient的一个实例</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>1.使用该<code>create()</code>方法创建WebClient</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以使用<code>create()</code>工厂方法创建WebClient的实例-</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.create();</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">如果您只使用特定服务的API,那么您可以使用该服务的baseUrl来初始化WebClient</p> <pre> <code class="language-java">WebClient webClient = WebClient.create("https://api.github.com");</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><strong>2.使用WebClient构建器创建WebClient</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebClient还附带了一个构建器,它为您提供了一些自定义选项,包括过滤器,默认标题,cookie,客户端连接器等 -</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.builder() .baseUrl("https://api.github.com") .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json") .defaultHeader(HttpHeaders.USER_AGENT, "Spring 5 WebClient") .build();</code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">使用WebClient发出请求并检索响应</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">以下是如何使用WebClient <code>GET</code>向<a href="https://developer.github.com/v3/repos/#list-your-repositories" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Github的List Repositories API</a>发出请求-</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri("/user/repos") .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToFlux(GithubRepo.class); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">了解API调用的简单性和简洁性!</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">假设我们有一个名为类<code>GithubRepo</code>,确认到GitHub的API响应,上面的函数会返回一个<code>Flux</code>的<code>GithubRepo</code>对象。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">请注意,我使用<a href="https://developer.github.com/v3/auth/#via-oauth-tokens" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >Github的基本认证</a>机制来调用API。它需要您的github用户名和个人访问令牌,您可以从<a href="https://github.com/settings/tokens" rel="external nofollow" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word" target="_blank" >https://github.com/settings/tokens中</a>生成该令牌。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">使用exchange()方法来检索响应</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">该<code>retrieve()</code>方法是获取响应主体的最简单方法。但是,如果您希望对响应拥有更多的控制权,那么您可以使用可<code>exchange()</code>访问整个<code>ClientResponse</code>标题和正文的方法 -</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri("/user/repos") .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .exchange() .flatMapMany(clientResponse -> clientResponse.bodyToFlux(GithubRepo.class)); }</code></pre> <h4 style="margin-left:0px; margin-right:0px; text-align:start">在请求URI中使用参数</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以在请求URI中使用参数,并在<code>uri()</code>函数中分别传递它们的值。所有参数都被花括号包围。在提出请求之前,这些参数将被WebClient自动替换 -</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri("/user/repos?sort={sortField}&direction={sortDirection}", "updated", "desc") .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToFlux(GithubRepo.class); }</code></pre> <h4 style="margin-left:0px; margin-right:0px; text-align:start">使用URIBuilder构造请求URI</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您也可以使用<code>UriBuilder</code>类似的方法获取对请求URI的完全程序控制,</span></span></span></p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories(String username, String token) { return webClient.get() .uri(uriBuilder -> uriBuilder.path("/user/repos") .queryParam("sort", "updated") .queryParam("direction", "desc") .build()) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToFlux(GithubRepo.class); }</code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">在WebClient请求中传递Request Body</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">如果你有一个<code>Mono</code>或一个形式的请求体<code>Flux</code>,那么你可以直接将它传递给<code>body()</code>WebClient中的方法,否则你可以从一个对象中创建一个单声道/通量并像这样传递 -</span></span></span></p> <pre> <code class="language-java">public Mono<GithubRepo> createGithubRepository(String username, String token, RepoRequest createRepoRequest) { return webClient.post() .uri("/user/repos") .body(Mono.just(createRepoRequest), RepoRequest.class) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToMono(GithubRepo.class); }</code></pre> 如果您具有实际值而不是<code>Publisher</code>(<code>Flux</code>/ <code>Mono</code>),则可以使用<code>syncBody()</code>快捷方式传递请求正文 - <pre> <code class="language-java">public Mono<GithubRepo> createGithubRepository(String username, String token, RepoRequest createRepoRequest) { return webClient.post() .uri("/user/repos") .syncBody(createRepoRequest) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToMono(GithubRepo.class); }</code></pre> 最后,你可以使用<code>BodyInserters</code>类提供的各种工厂方法来构造一个<code>BodyInserter</code>对象并将其传递给该<code>body()</code>方法。本<code>BodyInserters</code>类包含的方法来创建一个<code>BodyInserter</code>从<code>Object</code>,<code>Publisher</code>,<code>Resource</code>,<code>FormData</code>,<code>MultipartData</code>等- <pre> <code class="language-java">public Mono<GithubRepo> createGithubRepository(String username, String token, RepoRequest createRepoRequest) { return webClient.post() .uri("/user/repos") .body(BodyInserters.fromObject(createRepoRequest)) .header("Authorization", "Basic " + Base64Utils .encodeToString((username + ":" + token).getBytes(UTF_8))) .retrieve() .bodyToMono(GithubRepo.class); }</code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">添加过滤器功能</h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebClient支持使用<code>ExchangeFilterFunction</code>。您可以使用过滤器函数以任何方式拦截和修改请求。例如,您可以使用过滤器函数为<code>Authorization</code>每个请求添加一个标头,或记录每个请求的详细信息。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">这<code>ExchangeFilterFunction</code>需要两个参数 -</span></span></span></p> <ol style="margin-left:30px; margin-right:0px"> <li>在<code>ClientRequest</code>与</li> <li><code>ExchangeFilterFunction</code>过滤器链中的下一个。</li> </ol> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">它可以修改<code>ClientRequest</code>并调用<code>ExchangeFilterFucntion</code>过滤器链中的下一个来继续下一个过滤器或<code>ClientRequest</code>直接返回修改以阻止过滤器链。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">1.使用过滤器功能添加基本认证</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在上面的所有示例中,我们都包含一个<code>Authorization</code>用于使用Github API进行基本身份验证的标头。由于这是所有请求共有的内容,因此您可以在创建过滤器函数时将此逻辑添加到过滤器函数中<code>WebClient</code>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">该<code>ExchaneFilterFunctions</code>API已经为基本认证提供了一个过滤器。你可以像这样使用它 -</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.builder() .baseUrl(GITHUB_API_BASE_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE) .filter(ExchangeFilterFunctions .basicAuthentication(username, token)) .build();</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">现在,您不需要<code>Authorization</code>在每个请求中添加标题。过滤器函数将拦截每个WebClient请求添加此标头。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">2.使用过滤器功能记录所有请求</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">我们来看一个习惯的例子<code>ExchangeFilterFunction</code>。我们将编写一个过滤器函数来拦截并记录每个请求 -</span></span></span></p> <pre> <code class="language-java">WebClient webClient = WebClient.builder() .baseUrl(GITHUB_API_BASE_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE) .filter(ExchangeFilterFunctions .basicAuthentication(username, token)) .filter(logRequest()) .build();</code></pre> 这里是<code>logRequest()</code>过滤器功能的实现- <pre> <code class="language-java">private ExchangeFilterFunction logRequest() { return (clientRequest, next) -> { logger.info("Request: {} {}", clientRequest.method(), clientRequest.url()); clientRequest.headers() .forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value))); return next.exchange(clientRequest); }; }</code></pre> <h4 style="margin-left:0px; margin-right:0px; text-align:start">3.使用ofRequestProcessor()和ofResponseProcessor()工厂方法来创建过滤器</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">ExchangeFilterFunction API提供两个名为工厂方法<code>ofRequestProcessor()</code>和<code>ofResponseProcessor()</code>用于创建分别截获该请求和响应滤波器的功能。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff"><code>logRequest()</code>我们在前一节中创建的过滤器函数可以使用<code>ofRequestProcessor()</code>这种工厂方法创建-</span></span></span></p> <pre> <code class="language-java">private ExchangeFilterFunction logRequest() { ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { logger.info("Request: {} {}", clientRequest.method(), clientRequest.url()); clientRequest.headers() .forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value))); return Mono.just(clientRequest); }); } </code></pre> 如果您想拦截WebClient响应,则可以使用该<code>ofResponseProcessor()</code>方法创建像这样的过滤器功能 - <pre> <code class="language-java">private ExchangeFilterFunction logResposneStatus() { return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { logger.info("Response Status {}", clientResponse.statusCode()); return Mono.just(clientResponse); }); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">处理WebClient错误</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">只要接收到状态码为4xx或5xx的响应<code>retrieve()</code>,WebClient中的方法<code>WebClientResponseException</code>就会抛出一个。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以使用<code>onStatus()</code>像这样的方法来自定义,</span></span></span><br />  </p> <pre> <code class="language-java">public Flux<GithubRepo> listGithubRepositories() { return webClient.get() .uri("/user/repos?sort={sortField}&direction={sortDirection}", "updated", "desc") .retrieve() .onStatus(HttpStatus::is4xxClientError, clientResponse -> Mono.error(new MyCustomClientException()) ) .onStatus(HttpStatus::is5xxServerError, clientResponse -> Mono.error(new MyCustomServerException()) ) .bodyToFlux(GithubRepo.class); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">请注意,与<code>retrieve()</code>方法不同,该<code>exchange()</code>方法在4xx或5xx响应的情况下不会引发异常。您需要自己检查状态代码,并以您想要的方式处理它们。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">使用<code>@ExceptionHandler</code>控制器内部的WebClientResponseExceptions处理</h4> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">您可以<code>@ExceptionHandler</code>在控制器内部使用这种方式来处理<code>WebClientResponseException</code>并返回适当的响应给客户端 -</span></span></span></p> <pre> <code class="language-java">@ExceptionHandler(WebClientResponseException.class) public ResponseEntity<String> handleWebClientResponseException(WebClientResponseException ex) { logger.error("Error from WebClient - Status {}, Body {}", ex.getRawStatusCode(), ex.getResponseBodyAsString(), ex); return ResponseEntity.status(ex.getRawStatusCode()).body(ex.getResponseBodyAsString()); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">使用Spring 5 WebTestClient测试Rest API</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">WebTestClient包含类似于WebClient的请求方法。另外,它还包含检查响应状态,标题和正文的方法。您也可以像<code>AssertJ</code>使用WebTestClient 一样使用断言库。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:rgba(0, 0, 0, 0.87)"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">查看以下示例以了解如何使用WebTestClient执行其他API测试 -</span></span></span></p> <blockquote> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif">提示:<a href="http://www.leftso.com/resource/1011.html" target="_blank" >项目源码下载</a></span></p> </blockquote>
  • Spring Context 与Spring MVC Context

    Spring Context 与Spring MVC Context那些坑<h2>引言</h2> Spring Context与Spring MVC容器常见的问题和疑问讲解。 <h2>一.Spring MVC WEB配置</h2> <p>Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext,使得拥有web功能。那么,Spring MVC是如何在web环境中创建IoC容器呢?web环境中的IoC容器的结构又是什么结构呢?web环境中,Spring IoC容器是怎么启动呢?</p> <p>  以Tomcat为例,在Web容器中使用Spirng MVC,必须进行四项的配置:</p> <ul> <li> <p>修改web.xml,添加servlet定义;</p> </li> <li> <p>编写servletname-servlet.xml(servletname是在web.xm中配置DispactherServlet时使servlet-name的值)配置;</p> </li> <li> <p>contextConfigLocation初始化参数、配置ContextLoaderListerner; </p> </li> </ul> <p>Web.xml配置如下:<br />  </p> <pre> <code class="language-xml"><!-- servlet定义:前端处理器,接受的HTTP请求和转发请求的类 --> <servlet> <servlet-name>court</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- court-servlet.xml:定义WebAppliactionContext上下文中的bean --> <param-name>contextConfigLocation</param-name> <param-value>classpath*:court-servlet.xml</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>court</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 配置contextConfigLocation初始化参数:指定Spring IoC容器需要读取的定义了非web层的Bean(DAO/Service)的XML文件路径 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/court-service.xml</param-value> </context-param> <!-- 配置ContextLoaderListerner:Spring MVC在Web容器中的启动类,负责Spring IoC容器在Web上下文中的初始化 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener></code></pre>   <p>在web.xml配置文件中,有两个主要的配置:ContextLoaderListener和DispatcherServlet。同样的关于spring配置文件的相关配置也有两部分:context-param和DispatcherServlet中的init-param。那么,这两部分的配置有什么区别呢?它们都担任什么样的职责呢?</p> <p> </p> <p>  在Spring MVC中,Spring Context是以父子的继承结构存在的。Web环境中存在一个ROOT Context,这个Context是整个应用的根上下文,是其他context的双亲Context。同时Spring MVC也对应的持有一个独立的Context,它是ROOT Context的子上下文。</p> <p> </p> <p>  对于这样的Context结构在Spring MVC中是如何实现的呢?下面就先从ROOT Context入手,ROOT Context是在ContextLoaderListener中配置的,ContextLoaderListener读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context。</p> <p> </p> <p>Spring MVC启动过程大致分为两个过程:</p> <ol> <li> <p>ContextLoaderListener初始化,实例化IoC容器,并将此容器实例注册到ServletContext中;</p> </li> <li> <p>DispatcherServlet初始化;</p> </li> </ol> <h2>二.Web容器中Spring根上下文的加载与初始化</h2> <p>   Web容器调用contextInitialized方法初始化ContextLoaderListener,在此方法中,ContextLoaderListener通过调用继承自ContextLoader的initWebApplicationContext方法实例化Spring Ioc容器。</p> <p> </p> <p>  先看一下WebApplicationContext是如何扩展ApplicationContext来添加对Web环境的支持的。WebApplicationContext接口定义如下:<br />  </p> <pre> <code class="language-java">public interface WebApplicationContext extends ApplicationContext { //根上下文在ServletContext中的名称 String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; //取得web容器的ServletContext ServletContext getServletContext(); }</code></pre> <br /> 下面看一下ContextLoaderListener中创建context的源码:ContextLoader.java <pre> <code class="language-java">public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //PS : ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName() + ".ROOT" 根上下文的名称 //PS : 默认情况下,配置文件的位置和名称是: DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml" //在整个web应用中,只能有一个根上下文 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { // 在这里执行了创建WebApplicationContext的操作 this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); } } // PS: 将根上下文放置在servletContext中 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }</code></pre> 再看一下WebApplicationContext对象是如何创建的:ContextLoader.java <pre> <code class="language-java">protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) { //根据web.xml中的配置决定使用何种WebApplicationContext。默认情况下使用XmlWebApplicationContext //web.xml中相关的配置context-param的名称“contextClass” Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } //实例化WebApplicationContext的实现类 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); // Assign the best possible id value. if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { // Servlet <= 2.4: resort to name specified in web.xml, if any. String servletContextName = sc.getServletContextName(); if (servletContextName != null) { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX); } } else { // Servlet 2.5's getContextPath available! wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath()); } wac.setParent(parent); wac.setServletContext(sc); //设置spring的配置文件 wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM)); customizeContext(sc, wac); //spring容器初始化 wac.refresh(); return wac; }</code></pre> ContextLoaderListener构建Root Context时序图:<br /> <img alt="Spring Context" class="img-thumbnail" src="/resources/assist/images/blog/477cdee6630644738ce9c6386ad886c5.gif" /> <h2>三.Spring MVC对应的上下文加载与初始化</h2> <p>Spring MVC中核心的类是DispatcherServlet,在这个类中完成Spring context的加载与创建,并且能够根据Spring Context的内容将请求分发给各个Controller类。DispatcherServlet继承自HttpServlet,关于Spring Context的配置文件加载和创建是在init()方法中进行的,主要的调用顺序是init-->initServletBean-->initWebApplicationContext。</p> <p>先来看一下initWebApplicationContext的实现:FrameworkServlet.java<br />  </p> <pre> <code class="language-java">protected WebApplicationContext initWebApplicationContext() { //先从web容器的ServletContext中查找WebApplicationContext WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { // No fixed context defined for this servlet - create a local one. //从ServletContext中取得根上下文 WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); //创建Spring MVC的上下文,并将根上下文作为起双亲上下文 wac = createWebApplicationContext(parent); } if (!this.refreshEventReceived) { // Apparently not a ConfigurableApplicationContext with refresh support: // triggering initial onRefresh manually here. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. // 取得context在ServletContext中的名称 String attrName = getServletContextAttributeName(); //将Spring MVC的Context放置到ServletContext中 getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }</code></pre>     通过initWebApplicationContext方法的调用,创建了DispatcherServlet对应的context,并将其放置到ServletContext中,这样就完成了在web容器中构建Spring IoC容器的过程。<br /> <br />     DispatcherServlet创建context时序图:<br /> <img alt="Spring框架" class="img-thumbnail" src="/resources/assist/images/blog/a9d2eacfb40947ecb425fa840d45dead.gif" /><br /> <br /> DispatcherServlet初始化的大体流程:<br /> <img alt="DispatcherServlet初始化的大体流程:" class="img-thumbnail" src="/resources/assist/images/blog/adff9a97f46b40bda975a3127e7fdc53.gif" /><br /> 控制器DispatcherServlet的类图及继承关系:<br /> <img alt="333" class="img-thumbnail" src="/resources/assist/images/blog/234bbfda94ca446784d555f51379f726.jpg" /><br />   <h2>四.Spring中DispacherServlet、WebApplicationContext、ServletContext的关系</h2> <p>要想很好理解这三个上下文的关系,需要先熟悉Spring是怎样在web容器中启动起来的。Spring的启动过程其实就是其IOC容器的启动过程,对于web程序,IOC容器启动过程即是建立上下文的过程。</p> <p> </p> <p><strong>Spring的启动过程:</strong></p> <ol> <li> <p>首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;</p> </li> <li> <p>其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取; </p> </li> <li> <p>再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。</p> <p> </p> </li> </ol> <p><strong>在Web容器(比如Tomcat)中配置Spring时,你可能已经司空见惯于web.xml文件中的以下配置代码:</strong><br />  </p> <pre> <code class="language-xml"><context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></span></code></pre> 以上配置首先会在ContextLoaderListener中通过<context-param>中的applicationContext.xml创建一个ApplicationContext,再将这个ApplicationContext塞到ServletContext里面,通过ServletContext的setAttribute方法达到此目的,在ContextLoaderListener的源代码中,我们可以看到这样的代码: <pre> <code class="language-java">servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);</code></pre> 以上由ContextLoaderListener创建的ApplicationContext是共享于整个Web应用程序的,而你可能早已经知道,DispatcherServlet会维持一个自己的ApplicationContext,默认会读取/WEB-INFO/<dispatcherServletName>-servlet.xml文件,而也可以重新配置: <pre> <code class="language-xml"><servlet> <servlet-name> customConfiguredDispacherServlet </servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name> contextConfigLocation </param-name> <param-value> /WEB-INF/dispacherServletContext.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet></code></pre> <p><strong>问题是:以上两个ApplicationContext的关系是什么,它们的作用作用范围分别是什么,它们的用途分别是什么?</strong></p> <p>  ContextLoaderListener中创建ApplicationContext主要用于整个Web应用程序需要共享的一些组件,比如DAO,数据库的ConnectionFactory等。而由DispatcherServlet创建的ApplicationContext主要用于和该Servlet相关的一些组件,比如Controller、ViewResovler等。</p> <p>  对于作用范围而言,在DispatcherServlet中可以引用由ContextLoaderListener所创建的ApplicationContext,而反过来不行。</p> <p>在Spring的具体实现上,这两个ApplicationContext都是通过ServletContext的setAttribute方法放到ServletContext中的。但是,ContextLoaderListener会先于DispatcherServlet创建ApplicationContext,DispatcherServlet在创建ApplicationContext时会先找到由ContextLoaderListener所创建的ApplicationContext,再将后者的ApplicationContext作为参数传给DispatcherServlet的ApplicationContext的setParent()方法,在Spring源代码中,你可以在FrameServlet.java中找到如下代码:<br />  </p> <pre> <code class="language-java">wac.setParent(parent);</code></pre> <p>其中,wac即为由DisptcherServlet创建的ApplicationContext,而parent则为有ContextLoaderListener创建的ApplicationContext。此后,框架又会调用ServletContext的setAttribute()方法将wac加入到ServletContext中。</p> <p>  当Spring在执行ApplicationContext的getBean时,如果在自己context中找不到对应的bean,则会在父ApplicationContext中去找。这也解释了为什么我们可以在DispatcherServlet中获取到由ContextLoaderListener对应的ApplicationContext中的bean。</p>
  • spring boot 入门之spring session&RESTful APIs

    spring boot 入门之spring session实现restful apis。通过spring boot或者spring mvc整合spring session的方式来实现session的共享达到应用可以水平扩展集群。<h2>一.简介</h2>    spring boot 入门之spring session实现restful apis。通过前面的了解和使用,我们知道可以通过spring boot或者spring mvc整合spring session的方式来实现session的共享达到应用可以水平扩展集群。session默认情况是存了一个cookie在响应的数据里面,然后每次通过cookie来验证session的,但是restful设计中一般不会使用cookie,所以spring session提供了将session id存放在http协议的header中,本文主要讲解如何使用。 <h2>二.编码介绍</h2> <h3>HttpSession & RESTful APIs</h3> 原理:Spring Session 可以允许session在header里面提供来实现RESTful APIs <h3>Spring配置</h3> 在创建一个spring boot项目后,加入相关的依赖。现在我们就可以来进行相关配置。这个配置主要负责创建一个servlet Filter替换原有的HttpSession 使用spring session.主要添加以下spring 相关配置: <pre> <code class="language-java">@Configuration @EnableRedisHttpSession //① public class HttpSessionConfig { @Bean public LettuceConnectionFactory connectionFactory() { return new LettuceConnectionFactory(); //②注意,spring boot项目这里不需要创建一个链接工厂,在application配置文件中配置了相关信息会默认生成一个链接工厂供使用。 } @Bean public HttpSessionStrategy httpSessionStrategy() { return new HeaderHttpSessionStrategy(); //③ } }</code></pre>   ①<code>@EnableRedisHttpSession</code> 注解用来创建一个spring的过滤器类,类的名称为 <code>springSessionRepositoryFilter</code> 这个过滤器用来替换原生的 <code>HttpSession</code> 使用spring session。这个例子中,Spring Session通过Redis来实现存储。<br />   ②创建了一个<code>RedisConnectionFactory</code> 来使Spring Session链接到Redis Server。关于spring data redis相关配置可以参考之前的spring redis整合。(注意,spring boot项目这里不需要创建一个链接工厂,在application配置文件中配置了相关信息会默认生成一个链接工厂供使用。)<br />   ③我们自定义了Spring  Session的HttpSession ,将它整合到了HTTP协议的header里面,替换掉默认的放在cookie中。 <h3>HttpSessionListener 即session监听处理</h3> Spring Session支持<code>HttpSessionListener</code>,通过将<code>SessionDestroyedEvent</code> 和SessionCreatedEvent转换成 <code>HttpSessionEvent</code> 被定义在SessionEventHttpSessionListenerAdapter来实现。需要使用监听,注意以下几点: <ul> <li>确保你所实现的<code>SessionRepository</code> 支持并且配置到启动<code>SessionDestroyedEvent</code> 和SessionCreatedEvent</li> <li>将<code>SessionEventHttpSessionListenerAdapter</code> 配置为spring容器中的一个bean</li> <li>注入每一个<code>HttpSessionListener</code> 到SessionEventHttpSessionListenerAdapter</li> </ul> 如果你使用Redis的方式记录HttpSession,你需要将每一个HttpSessionListener配置为bean。例如,假设你想要支持Spring Security的并发控制,并且需要使用HttpSessionEventPublisher,你可以简单地将HttpSessionEventPublisher添加为一个bean。 在Java配置中,这可能如下所示: <pre> <code class="language-java">@Configuration @EnableRedisHttpSession public class RedisHttpSessionConfig { @Bean public HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); } // ... }</code></pre> 或者xml <pre> <code class="language-xml"><bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/></code></pre>
  • spring框架5.0介绍说明/概述

    java编程中spring框架5.0介绍说明/概述,spring5,spring框架,java编程spring框架一直以来都是java编程中流行的框架。 <p>Spring框架5.0介绍说明</p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">1.Spring入门</h2> <div style="text-align:start"> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">本参考指南提供有关Spring框架的详细信息。它为所有功能提供了全面的文档,以及Spring所接受的基本概念(如<em>“依赖注入”</em>)的背景知识。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">如果您刚开始使用Spring,则可能需要通过创建基于Spring Boot的应用程序来开始使用Spring Framework 。Spring Boot提供了一种快速(和有意见的)方式来创建一个基于生产的基于Spring的应用程序。它基于Spring框架,有利于配置的约定,并且旨在尽可能快地让您运行。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">您可以使用start.spring.io生成一个基本项目或遵循的一个“入门”指南类的 入门构建一个RESTful Web服务 之一。除了易于消化以外,这些指南非常<em>重视任务</em>,其中大多数都是基于Spring Boot的。他们还涵盖了Spring解决方案中您可能想要考虑的其他项目。</span></span></span></span></p> </div> </div> <h2 style="margin-left:0px; margin-right:0px; text-align:start">2. Spring框架简介</h2> <div style="text-align:start"> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring Framework是一个Java平台,为开发Java应用程序提供全面的基础设施支持。Spring处理基础设施,以便您可以专注于您的应用程序。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring使您能够从“简单的Java对象”(POJO)构建应用程序,并将企业服务非侵入式应用于POJO。此功能适用于Java SE编程模型以及完整和部分Java EE。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">作为应用程序开发人员,您可以从Spring平台中获益如何:</span></span></span></span></p> </div> <div> <ul style="margin-left:1.5em; margin-right:0px"> <li> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">使Java方法在数据库事务中执行,而不必处理事务API。</span></span></span></span></p> </li> <li> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">使本地Java方法成为HTTP端点,而无需处理Servlet API。</span></span></span></span></p> </li> <li> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">使本地Java方法成为一个消息处理程序,而无需处理JMS API。</span></span></span></span></p> </li> <li> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">使本地Java方法成为管理操作,而无需处理JMX API。</span></span></span></span></p> </li> </ul> </div> </div> <div style="text-align:start"> <h3 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.1。依赖注入和控制反转</span></span></span></h3> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Java应用程序 - 从受限嵌入式应用程序到n层服务器端企业应用程序运行范围的宽松术语通常由协作形成应用程序的对象组成。因此,应用程序中的对象彼此具有<em>依赖关系</em>。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">虽然Java平台提供了丰富的应用程序开发功能,但它缺乏将基本构建块组织成一个连贯的整体的手段,将该任务留给架构师和开发人员。虽然可以使用<em>Factory</em>,<em>Abstract Factory</em>,<em>Builder</em>,<em>Decorator</em>和<em>Service Locator</em>这样的设计模式 来构成组成应用程序的各种类和对象实例,但这些模式只是简单地说明:给出名称的最佳实践,以及什么样的模式,应用于哪里,它解决的问题等等。模式是<em>您必须</em>在应用程序中<em>实现自己的</em>正式的最佳实践。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring框架<em>反转控制</em>(IoC)组件通过提供将不同组件组成完整工作应用程序的正式方法来解决这一问题。Spring Framework将形式化的设计模式作为可以集成到您自己的应用程序中的第一类对象进行编译。许多组织和机构以这种方式使用Spring框架来设计强大的,可<em>维护的</em>应用程序。</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <div style="margin-left:0px; margin-right:0px; text-align:center"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="background-color:#f1f1f1"><span style="font-family:Montserrat,sans-serif"><span style="color:#0b0a0a">背景</span></span></span></span></span></span></div> <div> <p><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="background-color:#f1f1f1"><span style="font-family:inherit">“ <em>问题是,控制的哪个方面是(他们)反转?</em> ”Martin Fowler 在2004年在他的网站上提出了有关反转控制(IoC)的 问题 .Fowler建议重新命名原则,使其更加自明,并提出<em>依赖注入</em>。</span></span></span></span></span></p> </div> </div> </div> </div> <div style="text-align:start"> <h3 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.2。框架模块</span></span></span></h3> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring框架由组织成约20个模块的功能组成。这些模块分为Core Container,数据访问/集成,Web,AOP(面向对象编程),Instrumentation,Messaging和Test,如下图所示。</span></span></span></span></p> </div> </div> <img alt="spring 框架图" class="img-thumbnail" src="/resources/assist/images/blog/2937672202ba41c6862b026d169f0528.png" /> <div style="text-align:start"> <div style="margin-left:0px; margin-right:0px"> <div style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="color:#0b0a0a"><span style="font-family:"Varela Round",sans-serif"><em>图1. Spring框架概述</em></span></span></span></span></span></div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">以下部分列出了每个功能的可用模块及其工件名称及其涵盖的主题。工件名称与依赖关系管理工具中使用的<em>工件ID </em>相关。</span></span></span></span></p> </div> <div> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.2.1。核心集装箱</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">所述<em>核心容器</em>由以下部分组成<code>spring-core</code>, <code>spring-beans</code>,<code>spring-context</code>,<code>spring-context-support</code>,和<code>spring-expression</code>(弹簧表达式语言)模块。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">的<code>spring-core</code>和<code>spring-beans</code>模块提供框架的基本零件,包括IOC和依赖注入特征。这<code>BeanFactory</code>是工厂模式的复杂实施。它消除了对编程单例的需要,并允许您将依赖关系的配置和规范与实际程序逻辑分离。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">所述<em>上下文</em>(<code>spring-context</code>)模块建立由设置在固体基体上<em>的核心和豆类</em>模块:它是访问一个框架式的方式是类似于一个JNDI注册表对象的装置。上下文模块从Beans模块继承其功能,并增加了对国际化的支持(例如使用资源束),事件传播,资源加载以及例如Servlet容器透明地创建上下文。Context模块还支持Java EE功能,如EJB,JMX和基本远程处理。该<code>ApplicationContext</code>接口是语境模块的焦点。 <code>spring-context-support</code>提供了将常见第三方库集成到Spring应用程序环境中的支持,特别是用于缓存(EhCache,JCache)和调度(CommonJ,Quartz)。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-expression</code>模块提供了强大的<em>表达式语言,</em>用于在运行时查询和操作对象图。它是JSP 2.1规范中规定的统一表达语言(统一EL)的扩展。该语言支持设置和获取属性值,属性分配,方法调用,访问数组,集合和索引器的内容,逻辑和算术运算符,命名变量以及从Spring的IoC容器中的名称检索对象。它还支持列表投影和选择以及通用列表聚合。</span></span></span></span></p> </div> </div> <div> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.2.2。AOP和仪器仪表</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-aop</code>模块提供了一个符合<em>AOP</em>联盟标准的面向方面的编程实现,允许您定义方法拦截器和切入点,以便干净地解除实现应分隔的功能的代码。使用源级元数据功能,您还可以将行为信息与.NET属性类似。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">单独的<code>spring-aspects</code>模块提供与AspectJ的集成。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-instrument</code>模块提供了在某些应用服务器中使用的类检测支持和类加载器实现。该<code>spring-instrument-tomcat</code> 模块包含Spring的Tomcat测试代理。</span></span></span></span></p> </div> </div> <div> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.2.3。消息</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring框架4包括<code>spring-messaging</code>从关键抽象模块 <em>Spring集成</em>项目,例如<code>Message</code>,<code>MessageChannel</code>,<code>MessageHandler</code>,和其他人作为基于消息的应用奠定了基础。该模块还包括一组用于将消息映射到方法的注释,类似于基于Spring MVC注释的编程模型。</span></span></span></span></p> </div> </div> <div> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.2.4。数据访问/集成</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">所述<em>数据访问/集成</em>层由JDBC,ORM,OXM,JMS和交易模块。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-jdbc</code>模块提供了一个JDBC抽象层,无需执行繁琐的JDBC编码和解析数据库供应商特定的错误代码。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-tx</code>模块支持 针对实现特殊接口和<em>所有POJO(普通Java对象)的</em>类的编程和声明式事务管理。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-orm</code>模块为流行的对象关系映射 API(包括JPA和Hibernate)提供集成层 。使用该<code>spring-orm</code>模块,您可以将这些O / R映射框架与Spring提供的所有其他功能结合使用,例如前面提到的简单的声明性事务管理功能。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-oxm</code>模块提供了一个支持Object / XML映射实现的抽象层, 如JAXB,Castor,JiBX和XStream。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-jms</code>模块(Java消息服务)包含用于生成和消费消息的功能。从Spring Framework 4.1开始,它提供了与<code>spring-messaging</code>模块的集成 。</span></span></span></span></p> </div> </div> <div> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.2.5。卷筒纸</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">所述<em>网络</em>层由的<code>spring-web</code>,<code>spring-webmvc</code>和<code>spring-websocket</code> 模块。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-web</code>模块提供基本的面向Web的集成功能,例如多部分文件上传功能,以及使用Servlet侦听器和面向Web的应用程序上下文初始化IoC容器。它还包含一个HTTP客户端和Spring的远程支持的Web相关部分。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-webmvc</code>模块(也称为<em>Web-Servlet</em>模块)包含用于Web应用程序的Spring的模型视图控制器(<em>MVC</em>)和REST Web Services实现。Spring的MVC框架提供了领域模型代码和Web表单之间的清晰分离,并与Spring Framework的所有其他功能集成。</span></span></span></span></p> </div> </div> <div> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.2.6。测试</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">该<code>spring-test</code>模块支持使用JUnit或TestNG对Spring组件进行单元测试和 集成测试。它提供了Spring 的一致加载<code>ApplicationContext</code>和这些上下文的缓存。它还提供可用于孤立测试代码的模拟对象。</span></span></span></span></p> </div> </div> </div> <div style="text-align:start"> <h3 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.3。使用场景</span></span></span></h3> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">之前描述的构建模块使Spring成为许多场景中的逻辑选择,从在资源受限设备上运行的嵌入式应用程序到使用Spring的事务管理功能和Web框架集成的全面的企业应用程序。<br /> <img alt="流程" class="img-thumbnail" src="/resources/assist/images/blog/4ef2d77e2fa6406791ea64ad87b24c68.png" /></span></span></span></span><br />  </p> <div style="margin-left:0px; margin-right:0px; text-align:start"> <div style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="color:#0b0a0a"><span style="font-family:"Varela Round",sans-serif"><em>图2.典型的成熟的Spring Web应用程序</em></span></span></span></span></span></div> </div> <div style="text-align:start"> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring的声明式事务管理功能 使Web应用程序完全事务性,就像使用EJB容器管理的事务一样。所有您的定制业务逻辑都可以使用简单的POJO实现,并由Spring的IoC容器进行管理。其他服务包括支持发送电子邮件和独立于Web层的验证,可让您选择执行验证规则的位置。Spring的ORM支持与JPA和Hibernate集成; 例如,当使用Hibernate时,可以继续使用现有的映射文件和标准的Hibernate <code>SessionFactory</code>配置。表单控制器将Web层与域模型无缝集成,消除了<code>ActionForms</code>将HTTP参数转换为域模型值的需求 或其他类。<br /> <img alt="3" class="img-thumbnail" src="/resources/assist/images/blog/218014be82624d42ab35c430c91b7b98.png" /></span></span></span></span><br />  </p> <div style="margin-left:0px; margin-right:0px; text-align:start"> <div style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="color:#0b0a0a"><span style="font-family:"Varela Round",sans-serif"><em>图3.使用第三方Web框架的Spring中间层</em></span></span></span></span></span></div> </div> <div style="text-align:start"> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">有时情况不允许您完全切换到不同的框架。Spring框架并<em>没有</em>强迫你在它使用的一切; 这不是一个 <em>全无或缺的</em>解决方案。使用Struts,Tapestry,JSF或其他UI框架构建的现有前端可以与基于Spring的中间层集成,从而允许您使用Spring事务功能。您只需要使用一个连接您的业务逻辑,<code>ApplicationContext</code>并使用它<code>WebApplicationContext</code>来集成您的Web层。<br /> <img alt="4" class="img-thumbnail" src="/resources/assist/images/blog/48658620eeaf4d7d9defac9736e058f7.png" /></span></span></span></span><br />  </p> <div style="margin-left:0px; margin-right:0px; text-align:start"> <div style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="color:#0b0a0a"><span style="font-family:"Varela Round",sans-serif"><em>图4.远程使用场景</em></span></span></span></span></span></div> </div> <div style="text-align:start"> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">当你需要通过Web服务来访问现有的代码,你可以使用Spring的 <code>Hessian-</code>,<code>Rmi-</code>或<code>HttpInvokerProxyFactoryBean</code>类。启用对现有应用程序的远程访问并不困难。<br /> <img alt="5" class="img-thumbnail" src="/resources/assist/images/blog/88119b88334c40f7b0178ccc9fa384e3.png" /></span></span></span></span><br />  </p> <div style="margin-left:0px; margin-right:0px; text-align:start"> <div style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="color:#0b0a0a"><span style="font-family:"Varela Round",sans-serif"><em>图5. EJB - 包装现有的POJO</em></span></span></span></span></span></div> </div> <div style="text-align:start"> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring Framework还为Enterprise JavaBeans 提供了一个访问和抽象层,使您能够重用现有的POJO,并将其包装在无状态会话bean中,以用于可能需要声明式安全性的可伸缩的,故障安全的Web应用程序。</span></span></span></span></p> </div> <div style="text-align:start"> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.3.1。依赖管理和命名约定</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">依赖关系管理和依赖注入是不同的。要将Spring的这些不错的功能带入应用程序(如依赖注入),您需要组装所有需要的库(jar文件),并在运行时,并且可能在编译时将它们存入您的类路径。这些依赖关系不是注入的虚拟组件,而是文件系统中的物理资源(通常为)。依赖关系管理的过程包括定位这些资源,存储它们并将其添加到类路径中。依赖关系可以是直接的(例如,我的应用程序依赖于Spring在运行时)或间接(例如我的应用程序取决于<code>commons-dbcp</code>哪个依赖于 <code>commons-pool</code>)。间接依赖关系也被称为“传递性”,它们是最难识别和管理的依赖关系。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">如果要使用Spring,您需要获取构成您所需要的Spring部分的jar库的副本。为了使这更容易,Spring被打包为一组尽可能分离依赖关系的模块,例如,如果您不想编写Web应用程序,则不需要spring-web模块。要参照本指南中,我们使用速记命名约定到Spring库模块<code>spring-*</code>或 <code>spring-*.jar,</code>其中<code>*</code>代表该模块的短名称(例如<code>spring-core</code>,<code>spring-webmvc</code>,<code>spring-jms</code>等)。您使用的实际jar文件名通常是与版本号连接的模块名称(例如<em>spring-core-5.0.0.RC2.jar</em>)。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring Framework的每个版本都会将工件发布到以下位置:</span></span></span></span></p> </div> <div> <ul style="margin-left:1.5em; margin-right:0px"> <li> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Maven Central,它是Maven查询的默认存储库,不需要任何特殊配置。Spring的许多常见的库也可以从Maven Central获得,Spring社区的大部分使用Maven进行依赖关系管理,所以这对他们来说很方便。这里的jar的名字是形式<code>spring-*-<version>.jar</code>,Maven groupId是<code>org.springframework</code>。</span></span></span></span></p> </li> <li> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">在专门用于Spring的公共Maven存储库中。除了最终的GA版本,该存储库还承载开发快照和里程碑。jar文件名与Maven Central格式相同,因此这是一个有用的地方,可以让Spring的开发版本与在Maven Central中部署的其他库一起使用。该存储库还包含捆绑包分发zip文件,其中包含所有Spring jar,捆绑在一起以便于下载。</span></span></span></span></p> </li> </ul> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">所以您需要决定的第一件事是如何管理您的依赖关系:我们通常建议使用自动化系统,如Maven,Gradle或Ivy,但您也可以手动下载所有的jar。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">下面你将会找到Spring工件的列表。有关每个模块的更完整的描述,请参阅框架模块。</span></span></span></span></p> </div> <table class="table table-bordered table-hover"> <caption style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">表1. Spring Framework人工制品</span></span></span></caption> <thead> <tr> <th style="border-color:#dedede; text-align:left; vertical-align:top"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">GroupId的</span></span></span></th> <th style="border-color:#dedede; text-align:left; vertical-align:top"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">的artifactId</span></span></span></th> <th style="border-color:#dedede; text-align:left; vertical-align:top"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">描述</span></span></span></th> </tr> </thead> <tbody> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-aop</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">基于代理的AOP支持</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-aspects</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">基于AspectJ的方面</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-beans</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">豆支持,包括Groovy</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-context</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">应用程序上下文运行时,包括调度和远程抽象</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-context-support</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">支持类将常见的第三方库集成到Spring应用程序上下文中</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-core</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">许多其他Spring模块使用的核心实用程序</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-expression</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">Spring表达语言(Spel)</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-instrument</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">用于JVM自举的仪表代理</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-instrument-tomcat</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">Tomcat的仪表代理</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-jdbc</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">JDBC支持包,包括DataSource设置和JDBC访问支持</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-jms</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">JMS支持包,包括发送/接收JMS消息的帮助类</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-messaging</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">支持消息架构和协议</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-orm</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">对象/关系映射,包括JPA和Hibernate支持</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-oxm</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">对象/ XML映射</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-test</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">支持单元测试和集成测试Spring组件</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-tx</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">交易基础设施,包括DAO支持和JCA整合</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-web</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">基础网络支持,包括Web客户端和基于Web的远程处理</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-webmvc</p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">基于HTTP的Model-View-Controller和REST端点,用于Servlet堆栈</span></span></span></span></span></p> </td> </tr> <tr> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">org.springframework</span></span></span></span></span></p> </td> <td style="background-color:#ffffff; border-color:#dedede"> <p style="text-align:left">spring-websocket</p> </td> <td style="background-color:#ffffff"> <p style="text-align:left"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><span style="color:#34302d">WebSocket和SockJS基础架构,包括STOMP消息传递支持</span></span></span></span></span></p> </td> </tr> </tbody> </table> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">Spring依赖和依靠Spring</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">虽然Spring为大量的企业和其他外部工具提供集成和支持,但它有意将其强制性依赖性保持在绝对最小值:您不必定位和下载(甚至自动)大量的jar库,以便使用Spring用于简单的用例。对于基本的依赖注入,只有一个强制性的外部依赖关系,即日志记录(有关日志记录选项的更详细描述,请参见下文)。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">接下来,我们概述了配置依赖于Spring的应用程序所需的基本步骤,首先是使用Maven,然后使用Gradle,最后使用Ivy。在任何情况下,如果有什么不清楚,请参阅依赖关系管理系统的文档,或查看一些示例代码 - Spring本身在构建时使用Gradle来管理依赖关系,而我们的示例主要使用Gradle或Maven。</span></span></span></span></p> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">Maven依赖管理</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">如果您使用Maven进行依赖关系管理,则甚至不需要显式提供记录依赖关系。例如,要创建应用程序上下文并使用依赖注入来配置应用程序,您的Maven依赖项将如下所示:</span></span></span></span><br />  </p> <pre> <code class="language-xml"><dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.0.RC2</version> <scope>runtime</scope> </dependency> </dependencies></code></pre> </div> </div> </div> </div> </div> </div> </div> </div> <div style="text-align:start"> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">而已。注意,如果您不需要针对Spring API进行编译,那么范围可以被声明为运行时,通常情况下这是基本依赖注入用例的情况。</span></span></span></span></p> </div> <div style="text-align:start"> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">以上示例适用于Maven Central存储库。要使用Spring Maven存储库(例如,用于里程碑或开发人员快照),您需要在Maven配置中指定存储库位置。完整版本:</span></span></span></span><br />  </p> <pre> <code class="language-xml"><repositories> <repository> <id>io.spring.repo.maven.release</id> <url>http://repo.spring.io/release/</url> <snapshots><enabled>false</enabled></snapshots> </repository> </repositories></code></pre> <div style="text-align:start"> <div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">对于里程碑:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><repositories> <repository> <id>io.spring.repo.maven.milestone</id> <url>http://repo.spring.io/milestone/</url> <snapshots><enabled>false</enabled></snapshots> </repository> </repositories></code></pre> </div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">对于快照:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><repositories> <repository> <id>io.spring.repo.maven.snapshot</id> <url>http://repo.spring.io/snapshot/</url> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories></code></pre> </div> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">Maven“物料清单”依赖</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">当使用Maven时,可能会意外混合不同版本的Spring JAR。例如,您可能会发现第三方库或另一个Spring项目会将旧的版本的依赖关系传递给旧版本。如果您忘记自己明确声明直接依赖,可能会出现各种意外问题。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">为了克服这些问题,Maven支持“物料清单”(BOM)依赖的概念。您可以导入<code>spring-framework-bom</code>您的<code>dependencyManagement</code> 部分,以确保所有弹簧依赖(直接和传递)都是相同的版本。</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>5.0.0.RC2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement></code></pre> </div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">使用BOM的另一个好处是,您不再需要<code><version></code> 根据Spring Framework工件指定属性:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependencies></code></pre> </div> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">渐进依赖管理</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">要使用具有<a href="http://www.gradle.org/" rel="nofollow" style="box-sizing:border-box; background:transparent; color:#548e2e; text-decoration:underline; line-height:inherit" target="_blank">Gradle</a>构建系统的Spring存储库,请在该<code>repositories</code>部分中包含相应的URL :</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="background-color:#f5f5f5"><span style="font-family:Monaco,Menlo,Consolas,"Courier New",monospace"><span style="color:rgba(0, 0, 0, 0.9)"><code>repositories { mavenCentral() <span style="color:#999988"><em>// and optionally...</em></span> maven { url <span style="color:#dd2200"><span style="color:#dd1144">"</span><span style="color:#dd1144">http://repo.spring.io/release</span><span style="color:#dd1144">"</span></span> } }</code></span></span></span></span></span></span></pre> </div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">您可以更改<code>repositories</code>从URL <code>/release</code>到<code>/milestone</code>或<code>/snapshot</code>适当。一旦存储库被配置,你可以按照通常的Gradle方式声明依赖:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="background-color:#f5f5f5"><span style="font-family:Monaco,Menlo,Consolas,"Courier New",monospace"><span style="color:rgba(0, 0, 0, 0.9)"><code>dependencies { compile(<span style="color:#dd2200"><span style="color:#dd1144">"</span><span style="color:#dd1144">org.springframework:spring-context:5.0.0.RC2</span><span style="color:#dd1144">"</span></span>) testCompile(<span style="color:#dd2200"><span style="color:#dd1144">"</span><span style="color:#dd1144">org.springframework:spring-test:5.0.0.RC2</span><span style="color:#dd1144">"</span></span>) }</code></span></span></span></span></span></span></pre> </div> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">常春藤依赖管理</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">如果您喜欢使用<a href="https://ant.apache.org/ivy" rel="nofollow" style="box-sizing:border-box; background:transparent; color:#548e2e; text-decoration:underline; line-height:inherit" target="_blank">Ivy</a>来管理依赖项,那么还有类似的配置选项。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">要配置Ivy以指向Spring存储库,请将以下解析器添加到 <code>ivysettings.xml</code>:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><resolvers> <ibiblio name="io.spring.repo.maven.release" m2compatible="true" root="http://repo.spring.io/release/"/> </resolvers></code></pre> </div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">您可以更改<code>root</code>从URL <code>/release/</code>到<code>/milestone/</code>或<code>/snapshot/</code>适当。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">配置完成后,您可以按通常的方式添加依赖项。例如(in <code>ivy.xml</code>):</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code><dependency org="org.springframework" name="spring-core" rev="5.0.0.RC2" conf="compile->runtime"/>xx</code></pre> </div> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">分发Zip文件</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">虽然使用支持依赖关系管理的构建系统是推荐的获取Spring框架的方法,但仍然可以下载分发zip文件。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">分发的拉链发布到Spring Maven Repository(这只是为了方便起见,您不需要Maven或任何其他构建系统才能下载它们)。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">要下载一个分发zip打开一个Web浏览器到 http://repo.spring.io/release/org/springframework/spring,并为所需的版本选择相应的子文件夹。分发文件结束<code>-dist.zip</code>,例如spring-framework- {spring-version} -RELEASE-dist.zip。发行的分发也是针对里程碑和 快照发布的。</span></span></span></span></p> </div> </div> </div> <div style="text-align:start"> <h4 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">2.3.2。记录</span></span></span></h4> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring的日志记录设置已经修改为Spring 5:它仍然基于Apache Commons Logging API,也称为Jakarta Commons Logging(JCL)。但是,现在<code>spring-core</code> 指的是<code>spring-jcl</code>模块中定制的Commons Logging桥,具有Spring特定的<code>LogFactory</code>实现,它自动地连接到 Log4j 2,SLF4J或JDK自己的<code>java.util.logging</code>(JUL)。该实现类似于JCL-over-SLF4J桥,但具有一系列动态检测的提供程序,类似于JBoss Logging的常见目标(如Hibernate和Undertow所支持的)。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">作为一个好处,不再需要像JCL-over-SLF4J这样的外部桥接器,相应地不需要从<code>spring-core</code>依赖关系手动排除标准Commons Logging jar 。相反,它只是在运行时在Spring的自动检测样式中工作:只需将Log4j 2.x或SLF4J放在您的类路径上,而不需要任何额外的桥接jar,或者依靠通过JUL(具有可自定义的JUL设置)的默认日志记录。并且很好地对齐,默认的Hibernate设置将选择相同的通用日志目标。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">如果Log4j和SLF4J都存在,则Log4j API将被优选使用(因为它直接匹配JCL的签名,并且本机支持“致命”日志级别以及延迟解析的消息对象),类似于JBoss Logging的提供程序首选项。可以将Log4j配置为委派给SLF4J,否则SLF4J可能被配置为委托给Log4j:请检查其网站上的说明,了解如何在这种混合场景中达成一致的结果。</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <table class="table table-bordered table-hover"> <tbody> <tr> <td style="background-color:none; text-align:center; width:80px"> </td> <td style="background-color:none"> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="background-color:#ebf1e7"><span style="color:rgba(0, 0, 0, 0.6)"><span style="font-family:inherit">从Spring 5起,删除对外部Commons Logging网桥的任何引用,还可以从现有的<code>spring-core</code> 依赖关系设置中任意手动排除标准Commons Logging jar 。您的Log4j或SLF4J或JUL设置将继续工作,无需更改。请注意,您可能仍然需要<code>commons-logging</code>其他库(例如Apache HttpClient,Castor,HtmlUnit)的排除,以便代替Spring的JCL桥接器。</span></span></span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="background-color:#ebf1e7"><span style="color:rgba(0, 0, 0, 0.6)"><span style="font-family:inherit"><code>LogFactory</code>Commons Logging级别的自定义实现将不会被拾取,因为Spring的Bridge不支持定制的commons-logging.properties设置。对于任何其他日志提供程序,请设置相应的SLF4J或JUL桥(您很可能需要其他库,如Hibernate)。请注意,Log4j 1.x已经达到其使用寿命; 请迁移到Log4j 2.x.</span></span></span></span></span></span></p> </div> <div> <p><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="background-color:#ebf1e7"><span style="color:rgba(0, 0, 0, 0.6)"><span style="font-family:inherit">如果您遇到Spring Commons Logging实现的任何其他问题,请考虑排除<code>spring-jcl</code>和切换到标准<code>commons-logging</code>工件(支持<code>commons-logging.properties' setup) or to `jcl-over-slf4j</code>。</span></span></span></span></span></span></p> </div> </td> </tr> </tbody> </table> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">使用Log4j 2.x</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Log4j 2本身就是将原来的Log4j项目(1.x现在是EOL)重新进行重写。从Spring 5开始,嵌入式日志桥将在类路径上可用时自动委托给Log4j 2.x。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">因此,要使用Log4j与Spring,所有你需要做的就是把Log4j的在类路径,并为其提供一个配置文件(<code>log4j2.xml</code>,<code>log4j2.properties</code>或其他 支持的配置格式)。对于Maven用户,所需的最小依赖是:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.2</version> </dependency> </dependencies></code></pre> </div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">如果您还希望启用SLF4J委托Log4j,例如默认使用SLF4J的其他库,则还需要以下依赖关系:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.8.2</version> </dependency> </dependencies></code></pre> </div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">以下是<code>log4j2.xml</code>登录控制台的示例:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Logger name="org.springframework.beans.factory" level="DEBUG"/> <Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration></code></pre> </div> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">使用SLF4J与Logback</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Java的简单日志门面(SLF4J)是Spring通常使用的其他库使用的流行API。它通常与Logback一起使用, 它是SLF4J API的本机实现,因此在添加到应用程序类路径时由Spring自动检测:</span></span></span></span></p> </div> <div style="margin-left:0px; margin-right:0px"> <div> <pre> <code class="language-xml"><dependencies> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.2</version> </dependency> </dependencies></code></pre> </div> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">或者,您还可以将SLF4J配置为委托给Log4j(见上文)或JUL,特别是对于默认使用SLF4J的其他库。请注意,所有图书馆通过同一个日志门面并不重要; 这只是重要的,他们最终委托给同一个日志提供商。所以当Spring可以直接去Log4j时,其他库可能会通过Log4j的SLF4J绑定,或类似于JUL。</span></span></span></span></p> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">使用JUL(java.util.logging)</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit"><code>java.util.logging</code>如果类路径中没有检测到Log4j或SLF4J API,Spring将默认委派。所以没有特殊的依赖关系:只要<code>java.util.logging</code>在独立的应用程序(在JDK级别使用自定义或默认的JUL设置)或应用程序服务器的日志系统(和它的系统范围的JUL设置)。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">请注意,该<code>java.logging</code>模块在JDK 9中默认不存在,因为它不包括在内<code>java.base</code>。当使用Spring与Log4j或SLF4J时,这可以正常工作,因为在这种情况下不引用JUL API。但是,当选择使用JUL作为默认日志提供程序时,请记住激活该<code>java.logging</code>模块。</span></span></span></span></p> </div> </div> <div> <h5 style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff">Commons登录WebSphere</span></span></span></h5> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">Spring应用程序可以在本身提供JCL实现的容器上运行,例如IBM的WebSphere Application Server(WAS)。这不会引起问题本身,而是导致需要了解的两种不同的场景:</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">在“父第一”ClassLoader委托模型(WAS中的默认值)中,应用程序将始终选择Commons Logging提供的服务器版本,委托给WAS记录子系统(实际上基于JUL)。JCL的应用程序提供的变体,无论是Spring 5还是JCL-over-SLF4J桥,将随着任何本地包含的日志提供程序而被有效地忽略。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">使用“父进程”委托模式(常规Servlet容器中的默认值,但WAS上的显式配置选项),将提取应用程序提供的Commons Logging变体,使您能够设置本地包含的日志提供程序,例如Log4j或Logback,在您的应用程序。在没有本地日志提供程序的情况下,Spring(如常规Commons Logging)将默认委托给JUL,有效地记录到WebSphere的日志记录子系统,如“父第一”方案。</span></span></span></span></p> </div> <div> <p style="margin-left:0px; margin-right:0px"><span style="color:#34302d"><span style="font-family:"Varela Round",sans-serif"><span style="background-color:#ffffff"><span style="font-family:inherit">总而言之,我们建议您将“Spring”应用程序部署在“最后一个”模式中,因为它自然允许本地提供程序以及服务器的日志子系统。</span></span></span></span></p> </div> </div> </div> </div>