使用嵌入式 Jetty 应用服务器托管 Spring Boot Web 应用程序
在本教程中,我将向读者展示如何使用 Spring Boot 创建一个 MVC 应用程序,将其打包成一个自托管的 WAR 文件,并使用嵌入式 Jetty 应用服务器执行。
引言
2018 年 4 月,我写了一篇 快速教程文章,介绍了如何使用 Spring Boot 创建一个 Web 应用程序,并将其打包成 WAR 归档文件。它运行在一个嵌入式的 Tomcat 应用服务器上。本文将使用相同的代码库,但托管在一个嵌入式的 Jetty 应用服务器上。你可能会问,重复同一篇文章有什么意义?意义在于,当你完成本教程后,你可以轻松地将嵌入式 Tomcat 服务器替换为嵌入式 Jetty 服务器。有选择总是比只有一种选择要好。
源代码快速回顾
我没有对 孪生文章 中的实际源代码做任何更改。如果你很懒,不想在这篇教程和另一篇教程之间切换,只需快速浏览本节,然后我们将进入精彩的部分,将嵌入式 Tomcat 服务器替换为嵌入式 Jetty 服务器。
这是主入口的代码
package org.hanbo.boot.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class App extends SpringBootServletInitializer
{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder appBuilder)
{
return appBuilder.sources(App.class);
}
public static void main(String[] args) throws Exception
{
SpringApplication.run(App.class, args);
}
}
主入口设置应用程序作为 Web 应用程序运行。该类继承自 SpringBootServletInitializer
,这使得应用程序可以打包成 WAR 归档文件。嵌入式服务器将使用 WAR 归档文件的结构来运行应用程序。这是托管 Spring MVC 应用程序(包括 JSP 模板等)的理想方法。
每个 MVC 应用程序都有一个控制器。这里是
package org.hanbo.boot.app.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController
{
@RequestMapping(value="/meow", method = RequestMethod.GET)
public ModelAndView hello(
@RequestParam("sayit")
String sayit
)
{
ModelAndView retVal = new ModelAndView();
retVal.setViewName("testme");
retVal.addObject("mymessage", sayit);
return retVal;
}
}
这个控制器所做的就是设置一个网页,该网页会回显一些输入。这里有一个 URL
https://:8080/meow?sayit=This+is+pretty+crazy
只要应用程序正在运行,当你将 URL 粘贴到浏览器并按 Enter 时,你将看到一个页面显示
What did you say?
I said: "This is pretty crazy."
我需要让 Web 应用程序知道在哪里找到 JSP 模板,视图名称是“testme
”。我需要让 Web 应用程序知道视图模板文件的文件扩展名。我在 application.properties
中指定了这些。
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
看起来很熟悉,对吧?提示一下:org.springframework.web.servlet.view.InternalResourceViewResolver
。
最后,视图模板 JSP 文件名为“testme.jsp”
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="en">
<head>
<c:url value="/assets/css/index.css" var="jstlCss" />
<link href="${jstlCss}" rel="stylesheet" />
</head>
<body>
<p>What did you say?</p>
<p>I said: <span class="text-underline">"${mymessage}."</span></p>
<script type="text/javascript" src="/assets/js/test.js">
</script>
</body>
</html>
这个 Web 应用程序还包含一些静态内容,可以在 src/main/resources/static/** 目录中找到。按照约定,Spring Boot 会在这里查找静态内容。
总之,我稍后会描述如何测试这个应用程序。现在是时候展示精彩的部分了。为了使应用程序能够成功运行嵌入式 Jetty 服务器,我需要做的唯一更改是在 Maven POM 文件中。
Jetty 的 Maven POM 文件
Maven POM 文件用于依赖管理、构建和 WAR 打包。默认情况下,它会将嵌入式 Tomcat 服务器打包到 WAR 归档文件中,即 WAR 归档文件中的所有 jar 依赖项。现在,如果我需要用嵌入式 Jetty 服务器替换嵌入式 Tomcat 服务器,我需要首先排除所有 Tomcat 嵌入式服务器 jar,然后添加嵌入式 Jetty 服务器 jar。因为 Jetty 不是 Spring Boot 使用的默认应用程序容器,并且对于这个 Spring 应用程序启用了 Spring MVC、JSP/JSTL。当时,这一切都相当令人不知所措。这就是我记录下来以节省大家麻烦的原因,我也喜欢挑战。现在我将告诉你如何做到这一点。
我做的第一件事是排除 Tomcat 服务器 jar。方法如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
"spring-boot-starter-web
" 工件用于包含 Web 应用程序所需的所有 jar 文件,其中也包括 Tomcat。这就是为什么我需要一个专门的部分来排除 "spring-boot-starter-tomcat" 工件。这不是我自己想出来的。你可以在 Baeldung 提供的一个教程中找到它。接下来是添加自托管的 Jetty 依赖项。这里是:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
同样,这不是我自己想出来的。如果你猜到了,是的,来自 Baeldung 提供的同一个教程。然而,这就是我从 Baeldung 那里得到的所有信息。我刚才做的这两个操作只支持基本的 Web 应用程序功能。如果你只是这样运行应用程序,没有额外的 jar,应用程序就无法工作。它对我的小应用程序没有帮助,因为我需要 JSP/JSTL 支持,并且我需要将应用程序打包成 WAR 文件。所以经过一些研究,我找到了一个有效的解决方案。而且它如此简单,简直让我惊叹。这个解决方案是添加另外三个工件,就像这样:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jstl</artifactId>
</dependency>
最重要的两个工件是最后两个,一旦将它们添加到 POM 文件中,它们就会引入嵌入式 Jetty 服务器所需的所有必要 jar 文件。在继续之前,请仔细查看整个 Maven POM 文件。在其中,你可能会找到将文件打包为 war 的方法,并使用 Spring Boot 构建插件来构建应用程序。现在是运行应用程序的时候了。
测试应用程序
在构建和运行应用程序之前,请进入静态 Web 内容文件夹,并将所有 *.sj 文件重命名为 *.js 文件。
要构建应用程序,首先转到项目的基本目录(POM 文件所在的位置),然后使用以下 Maven 构建命令:
mvn clean install
在命令行提示符下按 Enter 后,它将下载所有依赖项,这需要一些时间,所以请耐心等待。构建成功后,只需一个命令即可运行应用程序。假设你仍在项目的基本目录中,输入命令:
java -jar target\boot-war-1.0.0.war
当命令运行时,它会输出很多无用的信息,查找类似这样的片段:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
2019-02-11 23:06:59.055 INFO 12404 --- [ main] org.hanbo.boot.app.App
: Starting App v1.0.0 on U3DTEST-PC with PID 12404 (C:\Users\
u3dadmin\workspace-mars8\SpringBootJettyWar\target\boot-war-1.0.0.war started by
u3dadmin in C:\Users\u3dadmin\workspace-mars8\SpringBootJettyWar)
2019-02-11 23:06:59.132 INFO 12404 --- [ main] org.hanbo.boot.app.App
: No active profile set, falling back to default profiles: de
fault
2019-02-11 23:06:59.584 INFO 12404 --- [ main] ConfigServletWebServer
ApplicationContext : Refreshing org.springframework.boot.web.servlet.context.Ann
otationConfigServletWebServerApplicationContext@6aceb1a5: startup date [Mon Feb
11 23:06:59 EST 2019]; root of context hierarchy
2019-02-11 23:07:04.389 INFO 12404 --- [ main] o.s.b.w.e.j.JettyServl
etWebServerFactory : Server initialized with port: 8080
....
2019-02-11 23:07:09.103 INFO 12404 --- [ main] o.e.jetty.server.handl
er.ContextHandler : Started o.s.b.w.e.j.JettyEmbeddedWebAppContext@6293abcc{app
lication,/,[org.springframework.boot.web.embedded.jetty.JettyServletWebServerFac
tory$LoaderHidingResource@7995092a],AVAILABLE}
2019-02-11 23:07:09.106 INFO 12404 --- [ main] org.eclipse.jetty.serv
er.Server : Started @16076ms
....
2019-02-11 23:07:10.454 INFO 12404 --- [ main] o.e.j.s.h.ContextHandl
er.application : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-02-11 23:07:10.454 INFO 12404 --- [ main] o.s.web.servlet.Dispat
cherServlet : FrameworkServlet 'dispatcherServlet': initialization starte
d
2019-02-11 23:07:10.479 INFO 12404 --- [ main] o.s.web.servlet.Dispat
cherServlet : FrameworkServlet 'dispatcherServlet': initialization comple
ted in 20 ms
2019-02-11 23:07:10.844 INFO 12404 --- [ main] o.e.jetty.server.Abstr
actConnector : Started ServerConnector@1bd4fdd{HTTP/1.1,[http/1.1]}{0.0.0.
0:8080}
2019-02-11 23:07:10.849 INFO 12404 --- [ main] o.s.b.web.embedded.jet
ty.JettyWebServer : Jetty started on port(s) 8080 (http/1.1) with context path
'/'
2019-02-11 23:07:10.854 INFO 12404 --- [ main] org.hanbo.boot.app.App
: Started App in 15.3 seconds (JVM running for 17.828)
第一个片段是应用程序运行的开始。在最后面某个地方,你可以看到嵌入式 Jetty 服务器的初始化。输出的中间部分有更多关于 Jetty 服务器启动的信息。在最后,我们看到应用程序正在端口 8080 上运行,并准备好接受用户请求。如果你没有看到任何异常输出,并且看到了最后一行,那么你就成功启动了应用程序。
要测试应用程序,只需将以下链接复制并粘贴到浏览器中:
https://:8080/meow?sayit=This+is+pretty+crazy
然后你将看到一个简单的页面显示:
What did you say?
I said: "This is pretty crazy."
或者尝试一些新的东西:
https://:8080/meow?sayit=Hello+World
然后你将看到一个简单的页面显示:
What did you say?
I said: "Hello World."
摘要
这是一个非常直接的 Spring Boot 教程。我采用了一篇去年提交的文章,去掉了嵌入式 Tomcat 服务器的使用,并将其替换为嵌入式 Jetty 服务器。这相当直接,只需要排除 "spring-boot-starter-tomcat
" 工件。然后添加嵌入式 Jetty 服务器的等效工件。要为 Spring MVC 支持 JSP/JSTL,必须至少再添加两个与 Jetty 相关的 JSP/JSTL 工件。一旦你知道如何做到这一点,你就可以使用相同的方法替换 2018 年 Spring Boot 教程的示例项目中的所有嵌入式 Tomcat 服务器。祝你愉快!
历史
- 2019 年 11 月 2 日 - 初稿