搜索词>>Websocket 耗时0.0080
  • Spring Boot 2.0 Websocket Angular整合

    Spring Boot 2.0 Websocket Angular整合Spring Boot 2.0 Websocket Angular整合
  • spring框架之bean的Scopes(作用域)

    Spring框架中,可以在6个内置的Scope中创建bean,也可以定义自定义范围。 在这六个范围中,只有在使用Web感知的ApplicationContext时才有四个范围可用。singleton 和prototype (单例和多例)作用域 在任何的spring框架IOC容器都可用。<h2>引言</h2>     在Java编程中spring框架已经使用的非常广泛,如今由spring框架派生的spring周边项目也逐渐流行起来,如spring boot,在现在的微服务里面用的甚多。但是用了这么多spring框架的模块和项目,有些时候还是需要对spring框架的基本知识进行深入了解,这里我讲记录下spring框架中bean的作用域。  <br />     在Spring框架中,可以在6个内置的范围中创建bean,也可以定义自定义范围。 在这六个范围中,只有在使用Web感知的ApplicationContext时才有四个范围可用。<code>singleton</code> 和<code>prototype</code> (单例和多例)作用域 在任何的spring框架IOC容器都可用。 <h2>一.spring框架中Bean的作用域类型讲解</h2> 下面是六个spring框架中默认可用的作用域 <table class="table table-bordered table-hover"> <tbody> <tr> <th>作用域类型</th> <th>简介</th> </tr> <tr> <td><code>singleton</code>(默认单例)</td> <td>spring框架的 IoC 容器中只有单个实例。</td> </tr> <tr> <td><code>prototype</code></td> <td>与单例相反, 多例会在每次使用他的时候创建一个新的实例.</td> </tr> <tr> <td><code>request</code></td> <td>在HTTP request的完整生命周期中,将创建并提供一个实例。 <p>只在web感知的Spring ApplicationContext中有效。</p> </td> </tr> <tr> <td><code>session</code></td> <td>在HTTP session的整个生命周期中,将创建一个单一的实例并使其可用。 <p>只在web感知的Spring ApplicationContext中有效。</p> </td> </tr> <tr> <td><code>application</code></td> <td>在ServletContext的完整生命周期中,将创建并提供一个实例。 <p>只在web感知的Spring ApplicationContext中有效。</p> </td> </tr> <tr> <td><code>websocket</code></td> <td>WebSocket的完整生命周期中将创建并提供一个实例。 <p>Only valid in web-aware Spring <code>ApplicationContext</code>.</p> </td> </tr> </tbody> </table> <h3>1.1 spring框架singleton(单例) scope(作用域)</h3>     <code>singleton</code> 是spring容器中的默认bean作用域。 它告诉容器创建和管理每个容器只有一个bean类的实例。 这个单实例存储在这样的单例bean的缓存中,并且该命名bean的所有后续请求和引用都会返回缓存的实例。<br /> <br /> 下面是一个单例作用域,使用的Java代码配置方式: <pre> <code class="language-java">@Component @Scope("singleton")//这个语句其实是多余的 - 单例是默认范围 public class BeanClass { }</code></pre> 同样,下面是一个基于xml配置的方式: <pre> <code class="language-xml"><!-- 指定单例作用域是多余的 --> <bean id="beanId" class="com.leftso.BeanClass" scope="singleton" /> //或者 <bean id="beanId" class="com.leftso.BeanClass" /></code></pre> <h3>1.2 spring框架prototype(多例)scope(作用域)</h3>     <code>prototype</code> (多例)作用域,每一次请求使用到该bean都会被应用创建一个新的实例。<br /> <br />     你应该知道销毁bean的生命周期方法并不会调用<code>prototype</code> 作用域的beans,只有在初始化回调方法的时候调用。因此作为开发人员,应该负责清理<code>prototype</code> 作用域的beans实例和他们挂着的其他资源。<br /> <br /> Java代码配置的方式的prototype(多例)作用域例子: <pre> <code class="language-java">@Component @Scope("prototype") public class BeanClass { }</code></pre> 下面是xml的方式: <pre> <code class="language-xml"><bean id="beanId" class="com.leftso.BeanClass" scope="singleton" /></code></pre> <br /> <span style="color:#1abc9c"><strong>提示:</strong></span><span style="color:#ff0000"><strong>通常应该使用prototype(多例)在有状态的bean ,singleton(单例)处理无状态的bean</strong></span><br /> <br /> <strong><span style="color:#9b59b6">注意:在request, session, application 和 websocket 作用域中使用beans,必须注册RequestContextListener或者RequestContextFilter</span></strong><br />   <h3>1.3 spring框架request scope(作用域)</h3>     在<code>request</code> 范围中,容器为每个HTTP请求创建一个新的实例。因此,如果服务器正在处理50个请求,然而容器最多可以创建50个单独bean的实例。 任何状态更改为一个实例,其他实例将不可见。 这些实例一旦请求完成就被销毁。<br /> <br /> Java代码配置方式例子: <pre> <code class="language-java">@Component @Scope("request") public class BeanClass { } //或者 @Component @RequestScope public class BeanClass { }</code></pre> xml配置例子: <pre> <code class="language-xml"><bean id="beanId" class="com.leftso.BeanClass" scope="request" /></code></pre> <h3>1.4 spring框架session scope(作用域)</h3>     在<code>session</code> 作用域,容器为每个HTTP会话创建一个新的实例。因此,如果服务器有20个活动会话,并且容器最多可以有20个单独的bean类实例。在单个会话生命周期内的所有HTTP请求将有权访问该会话作用域中相同的单个bean实例。<br />     任何状态更改为一个实例,其他实例将不可见。 一旦会话被销毁/终止在服务器上,这些实例就被销毁了。<br /> <br /> Java代码配置例子: <pre> <code class="language-java">@Component @Scope("session") public class BeanClass { } //or @Component @SessionScope public class BeanClass { }</code></pre> xml配置例子: <pre> <code class="language-xml"><bean id="beanId" class="com.leftso.BeanClass" scope="session" /></code></pre> <h3>1.5 spring框架application scope(作用域)</h3>    在<code>application</code> 作用域中,容器每个Web应用程序运行时创建一个实例 它几乎类似于单一的范围,只有两个不同之处,即: <ol> <li><code>application</code> 范围的bean是每个<code>ServletContext</code> 单例,而<code>singleton</code> 范围的bean是每个ApplicationContext单例。 请注意,单个应用程序可以有多个应用程序上下文。</li> <li><code>application</code> 范围的bean作为一个<code>ServletContext</code> 属性是可见的。</li> </ol> java代码配置例子: <pre> <code class="language-java">@Component @Scope("application") public class BeanClass { } //or @Component @ApplicationScope public class BeanClass { }</code></pre> xml配置例子: <pre> <code class="language-xml"><bean id="beanId" class="com.leftos.BeanClass" scope="application" /></code></pre> <h3>1.6 spring框架websocket  scope(作用域)</h3>     WebSocket协议启用客户端和选择与客户端通信的远程主机之间的双向通信。 WebSocket协议为两个方向上的流量提供单个TCP连接。 这对于同时编辑和多用户游戏的多用户应用程序特别有用。<br /> <br />     在这种类型的Web应用程序中,HTTP仅用于初始握手。 服务器可以响应HTTP状态101(切换协议),如果它同意 - 握手请求。 如果握手成功,则TCP套接字保持打开状态,客户端和服务器都可以使用它来相互发送消息。<br /> <br /> java代码配置例子: <pre> <code class="language-java">@Component @Scope("websocket") public class BeanClass { }</code></pre> xml配置例子: <pre> <code class="language-xml"><bean id="beanId" class="com.howtodoinjava.BeanClass" scope="websocket" /></code></pre> <strong><span style="color:#f39c12">请注意,websocket范围的bean通常是单身,比任何单独的WebSocket会话寿命更长。</span></strong><br />   <h3>1.7当前线程作用域</h3>   Spring框架还提供了一个使用SimpleThreadScope类的非默认<code>thread</code> 作用域。 要使用此范围,您必须使用<code>CustomScopeConfigurer</code> 类将其注册到容器。 <pre> <code class="language-xml"><bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleThreadScope"/> </entry> </map> </property> </bean></code></pre> 每个bean的请求都会在同一个线程中返回相同的实例。<br /> <br /> Java配置例子: <pre> <code class="language-java">@Component @Scope("thread") public class BeanClass { }</code></pre> xml配置例子: <pre> <code class="language-xml"><bean id="beanId" class="com.howtodoinjava.BeanClass" scope="thread" /></code></pre> <h2>二.总结</h2>     Spring框架提供了六个bean作用域,每个作用域内的实例有不同的生命周期跨度。 作为开发人员,我们必须明智地选择任何容器管理bean的范围。 另外,当不同范围的豆子互相引用时,我们必须做出明智的选择。
  • Docker部署spring boot 入门

    在本文中,您将学习如何构建用于运行Spring引导应用程序的Docker镜像。我首先给你一个docker的简要概念,然后我们将为我们的spring启动应用程序创建一个docker镜像,并在本地运行它。最后,我们会将码头图像推送到docker 中心库。<h2>前言</h2>     在本文中,您将学习如何构建用于运行Spring引导应用程序的Docker镜像。我首先给你一个docker的简要概念,然后我们将为我们的spring启动应用程序创建一个docker镜像,并在本地运行它。最后,我们会将码头图像推送到docker 中心库。<br />   <h2 style="margin-left:0px; margin-right:0px; text-align:start">快速介绍Docker</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"><a rel="external nofollow" target="_blank" href="https://www.docker.com/" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word">Docker</a>是一个软件平台,它使开发人员能够借助<strong>容器</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">现在什么是容器,码头工人用容器解决什么问题?</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> <ul> <li style="margin-left: 0px; margin-right: 0px;"><em>“它在<strong>我的机器</strong>上运行!!”</em></li> <li style="margin-left: 0px; margin-right: 0px;"><em>“我认为你的<strong>Tomcat版本</strong>已经过时了!</em></li> <li style="margin-left: 0px; margin-right: 0px;"><em>“我不想在运行应用程序之前安装10个不同的库和工具。难道它不能以<strong>包装形式</strong>提供它需要的所有库和工具吗?“</em></li> <li style="margin-left: 0px; margin-right: 0px;"><em>“我们有用不同的语言,工具,系统库和环境编写的应用程序。我们可以在<strong>同一个基础架构</strong>上独立运行它们吗?“</em></li> </ul> <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">Docker通过为应用程序创建一个轻量级的独立可执行程序包来解决这些问题,该程序包包括运行代码,运行时,库,工具,环境和配置所需的一切。</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>码头图像</strong>。而一个正在运行的Docker镜像实例称为<strong>码头容器</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">现在,这些容器图像可以在任何环境中的任何地方共享,发货和运行。无论他们运行的环境如何,他们的表现都完全一样。</span></span></span></p> <img alt="docker简介" class="img-thumbnail" src="/resources/assist/images/blog/105bde037060487da104f2b687d37257.jpg" /> <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">很酷,不是吗?那么,让我们现在学习如何在Docker容器中运行Spring Boot应用程序。但在此之前,请继续在您的平台上<a rel="external nofollow" target="_blank" href="https://www.docker.com/community-edition/" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word">安装Docker社区版</a>。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">使用Docker的Spring Boot:Docker化Spring Boot应用程序</h2> <h3 style="margin-left:0px; margin-right:0px; text-align:start">1.将应用程序下载到Dockerize</h3> 在本文中,我们将dockerize一个基于web套接字的群组聊天应用程序,这个应用程序是用spring引导的。<br /> <br />   <h3 style="margin-left:0px; margin-right:0px; text-align:start">2.使用Dockerfile定义Docker镜像</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>Dockerfile</code>。</span></span></span></p> <pre> <code class="language-html">$ cd spring-boot-websocket-chat-demo $ touch Dockerfile</code></pre> Dockerfile是我们定义Docker镜像并指定运行应用程序所需的所有配置的地方。以下是我们的Spring boot应用程序的Dockerfile - <pre> <code class="language-html"># Start with a base image containing Java runtime FROM openjdk:8-jdk-alpine # Add Maintainer Info LABEL maintainer="xxx@gmail.com" # Add a volume pointing to /tmp VOLUME /tmp # Make port 8080 available to the world outside this container EXPOSE 8080 # The application's jar file ARG JAR_FILE=target/websocket-demo-0.0.1-SNAPSHOT.jar # Add the application's jar to the container ADD ${JAR_FILE} websocket-demo.jar # Run the jar file ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/websocket-demo.jar"]</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">Dockerfile非常简单且具有声明性。让我们通过Dockerfile的每一行来理解细节。</span></span></span></p> <ul> <li style="margin-left: 0px; margin-right: 0px;"><strong>FROM</strong>:泊坞窗图像可以使用Docker注册表中可用的其他图像作为其基础或父图像。在上面的例子中,我们使用<a rel="external nofollow" target="_blank" href="https://hub.docker.com/_/openjdk/" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word"><code>openjdk:8-jdk-alpine</code></a>图像作为我们的基础图像。这是一个使用Alpine Linux的非常轻量级的OpenJDK 8运行时映像。(它非常适合小型Java微服务。)</li> <li style="margin-left: 0px; margin-right: 0px;"><strong>LABEL</strong>:该<code>LABEL</code>指令用于向图像添加元数据。在上面的Dockerfile中,我们<code>maintainer</code>通过<code>LABEL</code>指令添加了一些关于图像的信息。</li> <li style="margin-left: 0px; margin-right: 0px;"><strong>VOLUME</strong>:卷是一种机制,用于在主机操作系统上保留容器生成的数据,并使用容器共享主机操作系统中的目录。</li> <li style="margin-left: 0px; margin-right: 0px;">该<code>VOLUME</code>指令使用指定的路径在容器上创建一个安装点。运行容器时,可以指定给定安装点将映射到的Hot OS上的目录。之后,容器写入挂载路径的任何内容都将写入主机操作系统上的映射目录。</li> <li style="margin-left: 0px; margin-right: 0px;">卷最常见的用例之一是将容器生成的日志文件存储在主机操作系统上。例如,假设您的应用程序将日志文件写入某个位置<code>/var/log/app.log</code>。</li> <li style="margin-left: 0px; margin-right: 0px;">您可以在Dockerfile中安装一个<code>VOLUME</code>带有路径的路径<code>/var/log</code>,然后在运行该容器时指定该安装点将映射到的主机操作系统上的目录。之后,您将能够从主机操作系统上的映射目录访问日志。</li> <li style="margin-left: 0px; margin-right: 0px;">在上面<code>Dockerfile</code>,我们创建了一个带有路径的挂载点,<code>/tmp</code>因为这是默认情况下Spring引导应用程序为Tomcat创建工作目录的地方。尽管这个spring引导应用程序并不需要它,因为谁在乎tomcat目录。但是如果你想存储诸如tomcat访问日志等东西,那么VOLUMES非常有用。</li> <li style="margin-left: 0px; margin-right: 0px;">您可以从<a rel="external nofollow" target="_blank" href="https://docs.docker.com/storage/volumes/" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word">官方文档中</a>了解更多关于卷的<a rel="external nofollow" target="_blank" href="https://docs.docker.com/storage/volumes/" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word">信息</a>。</li> <li style="margin-left: 0px; margin-right: 0px;"><strong>EXPOSE</strong>:顾名思义,这条指令允许你将某个端口暴露给外部世界。</li> <li style="margin-left: 0px; margin-right: 0px;"><strong>ARG</strong>:<code>ARG</code>指令定义了一个默认值的变量。您可以通过在构建时传递它来覆盖该变量的默认值。</li> <li style="margin-left: 0px; margin-right: 0px;"> <pre> <code class="language-html">ARG <name>[=<default value>]</code></pre> </li> <li style="margin-left: 0px; margin-right: 0px;">一旦定义,该变量可以被其后的指令使用。</li> <li style="margin-left: 0px; margin-right: 0px;"><strong>ADD</strong>:该<code>ADD</code>指令用于将新文件和目录复制到泊坞窗图像。</li> <li style="margin-left: 0px; margin-right: 0px;"><strong>入口点</strong>:这是配置应用程序在容器内执行的方式。</li> </ul>   <h3 style="margin-left:0px; margin-right:0px; text-align:start">3.构建Docker镜像</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>Dockerfile</code>,让我们为我们的应用程序构建一个docker镜像。从项目的根目录键入以下命令来构建docker 镜像 -</span></span></span></p> <pre> <code class="language-html">$ docker build -t spring-boot-websocket-chat-demo .</code></pre> 您可以使用以下命令查看系统上所有泊docker 镜像的列表 - <pre> <code class="language-html">$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE spring-boot-websocket-chat-demo latest 30ad8958ac67 22 hours ago 126MB openjdk 8-jdk-alpine 224765a6bdbe 3 months ago 102MB</code></pre> 这应该显示我们新建的docker镜像。 <h3 style="margin-left:0px; margin-right:0px; text-align:start">4.运行docker镜像</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">一旦你有一个docker镜像,你可以使用<code>docker run</code>像这样的命令来运行它-</span></span></span></p> <pre> <code class="language-html">$ docker run -p 5000:8080 spring-boot-websocket-chat-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">在该<code>run</code>命令中,我们指定<code>8080</code>容器上的端口应映射到<code>5000</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>http://localhost:5000</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>CTRL + C</code>会停止它。现在让我们看看如何在后台运行容器。</span></span></span></p> <h4 style="margin-left:0px; margin-right:0px; text-align:start">在分离模式下在后台运行Docker镜像。</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>-d</code>选项在<code>docker run</code>后台运行容器 -</span></span></span></p> <pre> <code class="language-html">$ docker run -d -p 5000:8080 spring-boot-websocket-chat-demo 1c3528715862a8a8efb712c85bc8ab61f3419c04eb6dc613af76c89846d316e0</code></pre> 上述命令在后台启动容器并为您提供容器ID。您可以使用以下命令查看系统中运行的所有容器的列表 - <pre> <code class="language-html">$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1c3528715862 spring-boot-websocket-chat-demo "java -Djava.securit…" About a minute ago Up About a minute 8080/tcp, 0.0.0.0:4000->80/tcp vigorous_stallman</code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">5.将docker镜像推送到docker hub中心库</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">现在让我们将码头图像推送到docker中央库,以便其他人可以下载并使用我们的镜像。</span></span></span></p> <ul style="margin-left:30px; margin-right:0px"> <li> <p style="margin-left:0px; margin-right:0px"><strong>用您的Docker ID登录</strong></p> <pre> <code class="language-html">$ docker login Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username (callicoder): callicoder Password: Login Succeeded</code></pre> </li> </ul> <pre style="margin-left:0px; margin-right:0px; text-align:left"> <span style="background-color:#f6f8fa"><span style="font-family:monospace,monospace"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-bash"> </code></span></span></span></pre> <ul style="margin-left:30px; margin-right:0px"> <li> <p style="margin-left:0px; margin-right:0px"><strong>标记镜像</strong></p> <p style="margin-left:0px; margin-right:0px">要将本地图像推送到docker注册中心,您需要将本地图像与docker注册中心的存储库相关联。docker注册表上的存储库的表示法是<code>username/repository:tag</code>。</p> <p style="margin-left:0px; margin-right:0px">要标记图像,我们使用<code>docker tag</code>命令 -</p> <pre> <code class="language-html">$ docker tag image username/repository:tag</code></pre> </li> </ul> <br /> 例如,以下是我们如何标记Spring boot应用程序的本地映像 - <pre> <code class="language-html">$ docker tag spring-boot-websocket-chat-demo callicoder/spring-boot-websocket-chat-demo:0.0.1-SNAPSHOT</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:left"><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>callicoder</code>在上述命令中将我的用户名替换为docker id。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:left"><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>docker image ls</code>终端以查看新标记的图像 -</span></span></span><br />  </p> <pre> <code class="language-html">$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE callicoder/spring-boot-websocket-chat-demo 0.0.1-SNAPSHOT 30ad8958ac67 23 hours ago 126MB spring-boot-websocket-chat-demo latest 30ad8958ac67 23 hours ago 126MB openjdk 8-jdk-alpine 224765a6bdbe 3 months ago 102MB</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:left"><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>将图像推送到docker集线器</strong></span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:left"><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>docker push</code>像这样使用该命令将标记的图像推送到docker集线器 -</span></span></span></p> <pre> <code class="language-html">$ docker push callicoder/spring-boot-websocket-chat-demo:0.0.1-SNAPSHOT</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:left"><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>callicoder</code>用您的码头ID 替换用户名。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:left"><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">就这样!镜像现在发布在码头集线器的以下链接 - https://hub.docker.com/r/callicoder/spring-boot-websocket-chat-demo/</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start">6.从docker集线器中拉出镜像并运行它</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">将图像发布到Docker集线器后,任何人都可以拉出该镜像并在其环境中运行它。输入以下命令以拉出并运行我们刚刚推送到docker中心的计算机上的映像 -</span></span></span></p> <pre> <code class="language-html">$ docker run -p 5000:8080 callicoder/spring-boot-websocket-chat-demo:0.0.1-SNAPSHOT</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>docker run</code>命令在本地不可用,则该命令将从Docker集线器中提取图像,然后运行它。</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">您看到与其他人分享您的镜像是多么容易。人们不需要安装任何软件来运行你的应用程序。他们只需要将镜像拖放到docker上即可运行。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">使用dockerfile-maven-plugin自动化Docker镜像创建和发布</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">您可以自动执行从Docker镜像构建到docker集线器发布的所有功能<code>dockerfile-maven-plugin</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>pom.xml</code>使用以下配置将插件添加到文件中 -</span></span></span></p> <pre> <code class="language-xml"><plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>1.4.0</version> <configuration> <!-- replace `callicoder` with your docker id--> <repository>callicoder/spring-boot-websocket-chat-demo</repository> <tag>${project.version}</tag> <buildArgs> <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE> </buildArgs> </configuration> <executions> <execution> <id>default</id> <phase>install</phase> <goals> <goal>build</goal> <goal>push</goal> </goals> </execution> </executions> </plugin></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>callicoder</code>泊坞窗ID 替换用户名<code><repository></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>JAR_FILE</code>参数<code><buildArgs></code>。记得我们已经向<code>ARG JAR_FILE</code>Dockerfile 添加了指令吗?这里传递的参数将覆盖该值。</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>docker-file-maven</code>插件构建docker镜像-</span></span></span></p> <pre> <code class="language-html">$ mvn package dockerfile: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">上面的命令首先以jar文件的形式打包应用程序,然后构建docker镜像。</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>dockerfile:push</code>命令将docker映像推送到docker注册表-</span></span></span></p> <pre> <code class="language-html">$ mvn dockerfile:push</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>dockerfile:build</code>和<code>dockerfile:push</code>目标落实到<code>install</code>使用Maven构建生命周期阶段<code><executions></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>mvn install</code>时,<code>build</code>和<code>push</code>目标<code>dockerfile-maven-plugin</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>dockerfile-maven-plugin</code>使用存储在任何配置文件中的认证信息<code>~/.dockercfg</code>或<code>~/.docker/config.json</code>到泊坞窗图像推送到码头工人的个人资料。这些配置文件是通过登录docker时创建的<code>docker login</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">您也可以在maven <code>settings.xml</code>或<code>pom.xml</code>文件中指定验证细节。查看<a rel="external nofollow" target="_blank" href="https://github.com/spotify/dockerfile-maven#authenticating-with-maven-settingsxml" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word">该插件</a>的<a rel="external nofollow" target="_blank" href="https://github.com/spotify/dockerfile-maven#authenticating-with-maven-settingsxml" style="box-sizing:border-box; color:#419be8; text-decoration:none; word-wrap:break-word">官方自述文件以</a>获取更多信息。</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">我们在本文中停靠的应用程序非常简单。它不使用任何数据库或与其他服务进行通信。<br /> <br /> 附件中含上述spring boot项目下载</span></span></span></p>
  • HttpSession持久化/集群共享实现

    HttpSession什么时候创建什么时候销毁,session生命周期,javax.servlet.http.HttpSession接口表示一个会话,我们可以把一个会话内需要共享的数据保存到HttSession对象中。HttpSession持久化/集群共享实现<h2>一.Java web中什么是HttpSession</h2> javax.servlet.http.HttpSession接口表示一个会话,我们可以把一个会话内需要共享的数据保存到HttSession对象中 <h2>二.HttpSession由谁创建</h2>     session在访问tomcat<strong>服务器</strong>HttpServletRequest的getSession(true)/getSession()的时候<strong>创建</strong>,tomcat的ManagerBase类提供创建sessionid的方法:<strong>随机数+时间+jvmid</strong>;,session默认存储在服务器的内存中。<br />     sessionid是一个会话的key,浏览器第一次访问服务器会在服务器端生成一个session,有一个sessionid和它对应。tomcat生成的sessionid叫做jsessionid。 <h2>三.HttpSession什么时候销毁</h2> <ul> <li>超时;</li> <li>程序调用HttpSession.invalidate();</li> <li>程序关闭;</li> </ul> <h2>四.HttpSession持久化问题</h2>     所有支持Servlet规范的容器都自带session管理,于是大多数人都使用<code>HttpSession</code>接口存放状态信息。事实上, servlet的session会使得应用服务器水平扩展变的非常困难,在应用编写时应该完全弃用<code>HttpSession原生</code>接口。<br /> <br /> session在web应用中主要有以下几个功能实现: <ul> <li>session ID生成</li> <li>session过期管理</li> <li>session更新</li> </ul> <h3>4.1redis替代<code>HttpSession</code>接口方案设计</h3> <p><strong>生成session id:</strong><br /> 当用户登陆时,服务器生成一个全局唯一的字符串<code>SESSION:日期:20位随机字符串</code>做为redis中hash数据结构的key名,然后将该标识做为cookie返回给客户端。 之后该用户的后续请求都会带上此cookie, 我们编写一个filter, 其作用为读取请求中的标识,从redis中取出该标识对应的数据,然后放到<code>HttpServletRequest</code>对象中以供后续使用。</p> <p><strong>session过期:</strong><br /> 使用redis自带的过期功能为session id设置过期时间,轻松实现session过期。<br /> <br /> <strong>session追踪:</strong><br /> 我们可以将每个用户的session id记录下来,如保存到数据库中,或者依然放在redis里,这样就可以查到某个注册用户所有session id, 轻松实现踢出登陆功能。<br /> <br /> <strong>session更新:</strong><br /> 通过AOP, 在每个请求完后之后,检查在请求处理过程中有没有更新session信息,如果有则将新数据刷新到Redis中。<br /> <br /> <strong>方案特点:</strong><br /> 将session转移到redis中后,只要做好redis的运维工作,我们的应用服务器已经是完全无状态的了,水平扩展时只需要添加机器而不需要改动任何一行代码。</p> <h3>4.2 spring session成熟方案替换<code>HttpSession</code></h3> <strong>spring session介绍:</strong><br /> Spring session为管理用户的会话信息提供了一个API和实现。它还提供了无缝的集成: <ul> <li>HttpSession——允许在应用程序容器中替换HttpSession(即Tomcat)中立的方式。附加功能,包括</li> <li>集群会话——Spring session使得支持集群会话而不被绑定到应用程序容器的特定解决方案变得微不足道。</li> <li>多个浏览器会话——Spring session支持在一个浏览器实例中管理多个用户会话(也就是说,多个经过身份验证的帐户类似于谷歌)。</li> <li>RESTful api——Spring session允许在头文件中提供与RESTful api一起工作的会话id</li> </ul> WebSocket——提供在接收WebSocket消息时保持HttpSession存活的能力<br /> <br /> <strong>spring session主要得实现方式有:</strong> <ul> <li>Redis实现(逻辑同4.1)</li> <li>JDBC实现</li> <li>Mongo实现</li> <li>Hazelcast实现</li> <li>Pivotal GemFire实现</li> </ul> <br />  
  • Linux yum nginx安装和nginx入门配置方法

    Linux(centos) nginx安装和nginx入门配置方法,centos yum 安装nginxLinux(centos) nginx安装和nginx入门配置方法<br /> <br /> 1.切换至root用户 <pre> <code>$su -</code></pre> 2.执行在线安装命令 <pre> <code class="language-html">#yum install nginx -y</code></pre> 如果上面提示以下内容 <pre> <code class="language-html">[root@template ~]# yum install nginx -y Loaded plugins: fastestmirror, security Setting up Install Process Loading mirror speeds from cached hostfile * base: mirrors.cqu.edu.cn * extras: mirror.bit.edu.cn * updates: mirrors.tuna.tsinghua.edu.cn No package nginx available. Error: Nothing to do [root@template ~]# yum install nginx -y Loaded plugins: fastestmirror, security Setting up Install Process Loading mirror speeds from cached hostfile * base: mirrors.cqu.edu.cn * extras: mirror.bit.edu.cn * updates: mirrors.tuna.tsinghua.edu.cn No package nginx available. </code></pre> 则需要配置nginx的源,如果直接安装则忽略这里的配置源步骤。<br /> <br /> <strong>配置yum的nginx源</strong><br /> <code>/etc/yum.repos.d/</code>目录下创建一个源配置文件<code>nginx.repo</code><br /> 命令: <pre> <code>cd /etc/yum.repos.d/</code></pre>   <pre> <code>vim nginx.repo</code></pre> <br /> nginx.repo内容输入: <pre> <code class="language-html">[nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/6/$basearch/ gpgcheck=0 enabled=1</code></pre> 一般情况centos7会自带源的,只有部分centos6没有。所以配置源。配置完成后再执行安装命令 <pre> <code>yum install nginx -y</code></pre> <br /> <br /> 3.安装完成,启动nginx命令 <pre> <code class="language-html">#nginx</code></pre> 4.启动后在浏览器输入刚才安装好nginx的服务器IP地址<br /> <img alt="nginx" class="img-thumbnail" src="/resources/assist/images/blog/d26591d7-3032-44ee-8352-3d55fdcbe4de.png" style="height:375px; width:900px" /><br /> 出现上面的页面标识安装和启动成功<br /> 5.nginx停止 <pre> <code class="language-html">#nginx -s stop</code></pre> <br /> 6.nginx通用配置文件nginx.conf配置说明<br />   <pre> <code class="language-html">#指定nginx进程运行用户及用户组(默认nginx)(动静分离时候需要。注意权限否则静态文件可能访问不到。) user nginx; #nginx要开启的进程数,建议按照cpu数目来指定,一般跟cpu核数相同或为它的倍数 #推荐最多开启8个,8个以上性能提升不会再提升了,而且稳定性变得更低,所以8个进程够用了 worker_processes 8; #为每个进程分配cpu,上例中将8个进程分配到8个cpu worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000; #worker_processes 4;#4个进程配置 #worker_cpu_affinity 0001 0010 0100 1000 error_log /var/log/nginx/error.log warn;#错误日志存放路径,以及日志级别默认warn pid /var/run/nginx.pid;#默认存放nginx进程id文件 #当一个nginx进程打开的最多文件描述符数目,理论值应该是系统的最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n的值保持一致 #[注]文件资源限制的配置可以在/etc/security/limits.conf设置,针对root/user等各个用户或者*代表所有用户来设置。 worker_rlimit_nofile 65535; events { #使用epoll的I/O模型,用这个模型来高效处理异步事件,epoll是linux平台下的高效模式 use epoll; #每个进程允许的最多连接数,理论上每台nginx服务器的最大连接数为worker_processes*worker_connections #定义nginx每个进程的最大连接数为51200,一般网上都配置65535,根据张宴大神的建议51200即可 worker_connections 51200;#默认1024 } http { include /etc/nginx/mime.types;#实现对配置文件所包含的文件的设定 default_type application/octet-stream;#设置默认类型为二进制流 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; #http连接超时时间,默认是60s,功能是使客户端到服务器端的连接在设定的时间内持续有效,当出现对服务器的后继请求时,该功能避免了建立或者重新建立连接。切记这个参数也不能设置过大!否则会导致许多无效的http连接占据着nginx的连接数,终nginx崩溃! keepalive_timeout 65; #客户端请求头部的缓冲区大小,这个可以根据你的系统分页大小来设置,一般一个请求的头部大小不会超过1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE取得。 client_header_buffer_size 4K; #############下面部分为动静分离需要nginx代理文件时候使用,仅做Tomcat分发则不需要启用############ #下面这个参数将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。 #open_file_cache max=102400 inactive=20s; #下面这个是指多长时间检查一次缓存的有效信息。 #open_file_cache_valid 30s; #open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive时间内一次没被使用,它将被移除。 #open_file_cache_min_uses 1; ############上面面部分为动静分离需要nginx代理文件时候使用,仅做Tomcat分发则不需要启用############ #隐藏响应头中的有关操作系统和web server(Nginx)版本号的信息,这样对于安全性是有好处的。 server_tokens off; #gzip on; include /etc/nginx/conf.d/*.conf; }</code></pre> <br /> 7.新增一个nginx解析<br /> 切换目录至配置目录 <pre> <code>#cd /etc/nginx/conf.d/</code></pre> 新建一个解析配置文件test.conf <pre> <code class="language-html">#server {###配置虚拟机 # listen 80;#配置监听端口 # server_name zhljc.com www.zhljc.com srv.gift www.srv.gift 127.0.0.1; #配置访问域名,可以有多个[127.0.0.1/localhost表示任何] # location / {#对所有地址进行负载均衡 # root html;##定义服务器的默认网站根目录位置 # index index.html index.htm;##定义首页索引文件的名称 # # #以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上 # proxy_set_header Host $host; # proxy_set_header X-Real-IP $remote_addr; # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # # #websocket # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection "upgrade"; # # proxy_pass http://localhost:8080/bamboo; # }#end location # if ($host = 'www.xqlee.net'){ # rewrite ^/(.*)$ http://xqlee.net/$1 permanent; # } # #}#end server #upstream tomcats-webservice {#设定负载均衡的服务器列表,注意多个负载时候这里的名字必须唯一 #upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。 #负载方式,默认 #1.轮询[每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除] #2.weight 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 #3.ip_hash 每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 #server localhost:8000 weight=1 max_fails=3 fail_timeout=60s; #第一台机器 #server localhost:8080 weight=1 max_fails=3 fail_timeout=60s; #第二台机器 #ip_hash; #server localhost:8000; #}#end upstream </code></pre> <br /> <br />  
  • Spring框架与线程安全

    Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”。但其实,Spring并没有保证这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码。<p>Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”。但其实,Spring并没有保证这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码。</p> <p>Spring对每个bean提供了一个scope属性来表示该bean的作用域。它是bean的生命周期。例如,一个scope为singleton的bean,在第一次被注入时,会创建为一个单例对象,该对象会一直被复用到应用结束。</p> <ul> <li> <p>singleton:默认的scope,每个scope为singleton的bean都会被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)。</p> </li> <li> <p>prototype:bean被定义为在每次注入时都会创建一个新的对象。</p> </li> <li> <p>request:bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象。</p> </li> <li> <p>session:bean被定义为在一个session的生命周期内创建一个单例对象。</p> </li> <li> <p>application:bean被定义为在ServletContext的生命周期中复用一个单例对象。</p> </li> <li> <p>websocket:bean被定义为在websocket的生命周期中复用一个单例对象。</p> </li> </ul> <p>我们交由Spring管理的大多数对象其实都是一些无状态的对象,这种不会因为多线程而导致状态被破坏的对象很适合Spring的默认scope,每个单例的无状态对象都是线程安全的(也可以说只要是无状态的对象,不管单例多例都是线程安全的,不过单例毕竟节省了不断创建对象与GC的开销)。</p> <p>无状态的对象即是自身没有状态的对象,自然也就不会因为多个线程的交替调度而破坏自身状态导致线程安全问题。无状态对象包括我们经常使用的DO、DTO、VO这些只作为数据的实体模型的贫血对象,还有Service、DAO和Controller,这些对象并没有自己的状态,它们只是用来执行某些操作的。例如,每个DAO提供的函数都只是对数据库的CRUD,而且每个数据库Connection都作为函数的局部变量(局部变量是在用户栈中的,而且用户栈本身就是线程私有的内存区域,所以不存在线程安全问题),用完即关(或交还给连接池)。</p> <p>有人可能会认为,我使用request作用域不就可以避免每个请求之间的安全问题了吗?这是完全错误的,因为Controller默认是单例的,一个HTTP请求是会被多个线程执行的,这就又回到了线程的安全问题。当然,你也可以把Controller的scope改成prototype,实际上Struts2就是这么做的,但有一点要注意,Spring MVC对请求的拦截粒度是基于每个方法的,而Struts2是基于每个类的,所以把Controller设为多例将会频繁的创建与回收对象,严重影响到了性能。</p> <p>通过阅读上文其实已经说的很清楚了,Spring根本就没有对bean的多线程安全问题做出任何保证与措施。对于每个bean的线程安全问题,根本原因是每个bean自身的设计。不要在bean中声明任何有状态的实例变量或类变量,如果必须如此,那么就使用ThreadLocal把变量变为线程私有的,如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用synchronized、lock、CAS等这些实现线程同步的方法了。<br /> 下面将通过解析ThreadLocal的源码来了解它的实现与作用,ThreadLocal是一个很好用的工具类,它在某些情况下解决了线程安全问题(在变量不需要被多个线程共享时)。</p> <h2>ThreadLocal</h2> <p>ThreadLocal是一个为线程提供线程局部变量的工具类。它的思想也十分简单,就是为线程提供一个线程私有的变量副本,这样多个线程都可以随意更改自己线程局部的变量,不会影响到其他线程。不过需要注意的是,ThreadLocal提供的只是一个浅拷贝,如果变量是一个引用类型,那么就要考虑它内部的状态是否会被改变,想要解决这个问题可以通过重写ThreadLocal的initialValue()函数来自己实现深拷贝,建议在使用ThreadLocal时一开始就重写该函数。</p> <p>ThreadLocal与像synchronized这样的锁机制是不同的。首先,它们的应用场景与实现思路就不一样,锁更强调的是如何同步多个线程去正确地共享一个变量,ThreadLocal则是为了解决同一个变量如何不被多个线程共享。从性能开销的角度上来讲,如果锁机制是用时间换空间的话,那么ThreadLocal就是用空间换时间。</p> <p>ThreadLocal中含有一个叫做ThreadLocalMap的内部类,该类为一个采用线性探测法实现的HashMap。它的key为ThreadLocal对象而且还使用了WeakReference,ThreadLocalMap正是用来存储变量副本的。</p> <pre> <code class="language-java">/** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } .... }</code></pre> <p>ThreadLocal中只含有三个成员变量,这三个变量都是与ThreadLocalMap的hash策略相关的。<br />  </p> <pre> <code class="language-java">/** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */ private final int threadLocalHashCode = nextHashCode(); /** * The next hash code to be given out. Updated atomically. Starts at * zero. */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */ private static final int HASH_INCREMENT = 0x61c88647; /** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }</code></pre> 唯一的实例变量threadLocalHashCode是用来进行寻址的hashcode,它由函数nextHashCode()生成,该函数简单地通过一个增量HASH_INCREMENT来生成hashcode。至于为什么这个增量为0x61c88647,主要是因为ThreadLocalMap的初始大小为16,每次扩容都会为原来的2倍,这样它的容量永远为2的n次方,该增量选为0x61c88647也是为了尽可能均匀地分布,减少碰撞冲突。 <pre> <code class="language-java">/** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }</code></pre> <p>要获得当前线程私有的变量副本需要调用get()函数。首先,它会调用getMap()函数去获得当前线程的ThreadLocalMap,这个函数需要接收当前线程的实例作为参数。如果得到的ThreadLocalMap为null,那么就去调用setInitialValue()函数来进行初始化,如果不为null,就通过map来获得变量副本并返回。</p> <p>setInitialValue()函数会去先调用initialValue()函数来生成初始值,该函数默认返回null,我们可以通过重写这个函数来返回我们想要在ThreadLocal中维护的变量。之后,去调用getMap()函数获得ThreadLocalMap,如果该map已经存在,那么就用新获得value去覆盖旧值,否则就调用createMap()函数来创建新的map。<br />  </p> <pre> <code class="language-java">/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } /** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } protected T initialValue() { return null; }</code></pre> ThreadLocal的set()与remove()函数要比get()的实现还要简单,都只是通过getMap()来获得ThreadLocalMap然后对其进行操作。 <pre> <code class="language-java">/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } /** * Removes the current thread's value for this thread-local * variable. If this thread-local variable is subsequently * {@linkplain #get read} by the current thread, its value will be * reinitialized by invoking its {@link #initialValue} method, * unless its value is {@linkplain #set set} by the current thread * in the interim. This may result in multiple invocations of the * {@code initialValue} method in the current thread. * * @since 1.5 */ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }</code></pre> getMap()函数与createMap()函数的实现也十分简单,但是通过观察这两个函数可以发现一个秘密:ThreadLocalMap是存放在Thread中的。 <pre> <code class="language-java">/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } // Thread中的源码 /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;</code></pre> <p>仔细想想其实就能够理解这种设计的思想。有一种普遍的方法是通过一个全局的线程安全的Map来存储各个线程的变量副本,但是这种做法已经完全违背了ThreadLocal的本意,设计ThreadLocal的初衷就是为了避免多个线程去并发访问同一个对象,尽管它是线程安全的。而在每个Thread中存放与它关联的ThreadLocalMap是完全符合ThreadLocal的思想的,当想要对线程局部变量进行操作时,只需要把Thread作为key来获得Thread中的ThreadLocalMap即可。这种设计相比采用一个全局Map的方法会多占用很多内存空间,但也因此不需要额外的采取锁等线程同步方法而节省了时间上的消耗。</p> <h2>ThreadLocal中的内存泄漏</h2> <p>我们要考虑一种会发生内存泄漏的情况,如果ThreadLocal被设置为null后,而且没有任何强引用指向它,根据垃圾回收的可达性分析算法,ThreadLocal将会被回收。这样一来,ThreadLocalMap中就会含有key为null的Entry,而且ThreadLocalMap是在Thread中的,只要线程迟迟不结束,这些无法访问到的value会形成内存泄漏。为了解决这个问题,ThreadLocalMap中的getEntry()、set()和remove()函数都会清理key为null的Entry,以下面的getEntry()函数的源码为例。<br />  </p> <pre> <code class="language-java">/** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); } /** * Version of getEntry method for use when key is not found in * its direct hash slot. * * @param key the thread local object * @param i the table index for key's hash code * @param e the entry at table[i] * @return the entry associated with key, or null if no such */ private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; // 清理key为null的Entry while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; }</code></pre> <p>在上文中我们发现了ThreadLocalMap的key是一个弱引用,那么为什么使用弱引用呢?使用强引用key与弱引用key的差别如下:</p> <ul> <li> <p><strong>强引用key</strong>:ThreadLocal被设置为null,由于ThreadLocalMap持有ThreadLocal的强引用,如果不手动删除,那么ThreadLocal将不会回收,产生内存泄漏。</p> </li> <li> <p><strong>弱引用key</strong>:ThreadLocal被设置为null,由于ThreadLocalMap持有ThreadLocal的弱引用,即便不手动删除,ThreadLocal仍会被回收,ThreadLocalMap在之后调用set()、getEntry()和remove()函数时会清除所有key为null的Entry。</p> </li> </ul> <p>但要注意的是,ThreadLocalMap仅仅含有这些被动措施来补救内存泄漏问题。如果你在之后没有调用ThreadLocalMap的set()、getEntry()和remove()函数的话,那么仍然会存在内存泄漏问题。<br /> 在使用线程池的情况下,如果不及时进行清理,内存泄漏问题事小,甚至还会产生程序逻辑上的问题。所以,<strong>为了安全地使用ThreadLocal,必须要像每次使用完锁就解锁一样,在每次使用完ThreadLocal后都要调用remove()来清理无用的Entry</strong>。</p>
  • 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 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.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>