1. 简介
Jetty 提供了一个Web服务器和javax.servlet容器,以及对HTTP/2,WebSocket,OSGi,JMX,JNDI,JAAS和许多其他集成的支持。这些组件是开源的,可用于商业用途和发行。
Jetty有一个口号:不要把应用部署到Jetty上,要把Jetty部署到你的应用里。这句话的意思是把应用打成一个war包部署到Jetty上,不如将Jetty作为应用的一个组件,它可以实例化并像POJO一样。换种说法,在嵌入式模块中运行Jetty意味着把HTTP模块放到你的应用里,这种方法比把你的应用放到一个HTTP服务器里要好。
2. 简单使用
添加 jetty-server 依赖
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.19.v20190610</version>
</dependency>
继承 AbstractHandler
public class HelloHandler extends AbstractHandler {
@Override
public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {
// 设置类型,指定编码utf8
httpServletResponse.setContentType("text/html; charset=utf-8");
// 设置响应状态吗
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
// 写响应数据
httpServletResponse.getWriter().write("<h1>HelloHandler</h1>");
// 标记请求已处理,handler链
request.setHandled(true);
}
}
创建Server并启动
public class JettySimpleTest {
public static void main(String[] args) throws Exception {
// 创建服务器
Server server = new Server(8001);
// 设置handler
server.setHandler(new HelloHandler());
// 启动应用服务并等待请求
server.start();
// 阻塞Jetty server的线程池,直到线程池停止
server.join();
}
}
访问 localhost:8001
3. 嵌入 web 应用(WebAppContext)
WebAppContext是ServletContextHandler 的扩展,使用标准的web应用组件和web.xml,通过web.xml和注解配置servlet,filter和其它特性。
3.1 hello-war
这里会创建三个项目,然后打成war包。
添加依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
Hello1Servlet
public class Hello1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置响应内容类型
resp.setContentType("text/html");
// 实际的逻辑是在这里
PrintWriter out = resp.getWriter();
out.println("<h1> hello-war-1 </h1>");
out.println("<h2> HelloServlet </h2>");
}
}
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Hello1Servlet</display-name>
<servlet>
<servlet-name>Hello1Servlet</servlet-name>
<servlet-class>com.shpun.Hello1Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Hello1Servlet</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
</web-app>
index.jsp
<html>
<body>
<h2> hello-war-1 index.jsp </h2>
<h2>Hello World!</h2>
</body>
</html>
hello-war-1 项目结构
hello-war-2 和 hello-war-3 项目结构,和hello-war-1一样。
3.2 jetty-server
添加依赖
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.19.v20190610</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.4.19.v20190610</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>9.4.19.v20190610</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
<version>9.4.19.v20190610</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>9.4.19.v20190610</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<version>9.4.19.v20190610</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>9.4.19.v20190610</version>
<type>jar</type>
</dependency>
JettyServer,获取war包地址,使用 WebAppContext 封装。然后再添加到 Server 中。
public class JettyServer {
private final Server server;
public JettyServer() throws Exception {
ClassLoader classLoader = getClass().getClassLoader();
File tempDir = new File("E:\\IDEA_workspace\\jetty-test\\work\\jetty");
// war包文件路径
File helloWar1 = new File("E:\\IDEA_workspace\\jetty-test\\hello-war-1\\target\\hello-war-1.war");
File helloWar2 = new File("E:\\IDEA_workspace\\jetty-test\\hello-war-2\\target\\hello-war-2.war");
File helloWar3 = new File("E:\\IDEA_workspace\\jetty-test\\hello-war-3\\target\\hello-war-3.war");
// Web应用程序上下文处理程序。封装工作目录,war包地址,上下文,类加载器
WebAppContext helloWar1WebAppContext = buildWebApContext(tempDir, helloWar1,"/hello1", classLoader);
WebAppContext helloWar2WebAppContext = buildWebApContext(tempDir, helloWar2,"/hello2", classLoader);
WebAppContext helloWar3WebAppContext = buildWebApContext(tempDir, helloWar3,"/hello3", classLoader);
// 将三个 WebAppContext 添加到一个 HandlerCollection 中。
// HandlerCollection 是一个包含多个处理器的集合,按照顺序依次处理
HandlerCollection handlerCollection = new HandlerCollection();
handlerCollection.addHandler(helloWar1WebAppContext);
handlerCollection.addHandler(helloWar2WebAppContext);
handlerCollection.addHandler(helloWar3WebAppContext);
// 将 HandlerCollection 压缩
GzipHandler gzipHandler = new GzipHandler();
gzipHandler.setIncludedMethods("GET", "POST", "PUT", "DELETE");
gzipHandler.setHandler(handlerCollection);
// HandlerCollection会依次调用每一个Handler,即使请求已经被处理了
ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection();
contextHandlerCollection.addHandler(gzipHandler);
// HandlerList顺序执行handler
HandlerList handlerList = new HandlerList();
handlerList.addHandler(contextHandlerCollection);
// 初始化 Server
QueuedThreadPool threadPool = new QueuedThreadPool(30);
threadPool.setName("Web Server");
server = new Server(threadPool);
server.setHandler(handlerList);
// HTTP 配置,这是设置请求和返回头的大小
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.setRequestHeaderSize(16384);
httpConfiguration.setResponseHeaderSize(16384);
// 配置连接器
// Connector是Jetty中可以直接接受客户端连接的抽象,一个Connector监听Jetty服务器的一个端口
ServerConnector serverConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
// 多网卡时使用
//serverConnector.setHost("192.168.1.2");
serverConnector.setPort(8001);
// 设置连接的最大空闲时间
serverConnector.setIdleTimeout(30000L * 2);
server.addConnector(serverConnector);
// 解析jsp方式1:启用基于注释的配置,以确保正确初始化了jsp容器
Configuration.ClassList classList = Configuration.ClassList.setServerDefault(server);
classList.addBefore(JettyWebXmlConfiguration.class.getName(), AnnotationConfiguration.class.getName());
}
private void start() throws Exception {
server.start();
}
public static void main( String[] args ) throws Exception {
JettyServer jettyServer = new JettyServer();
jettyServer.start();
}
/**
* 构建 WebAppContext
* @param tempDir
* @param war
* @param contextPath
* @param classLoader
* @return
* @throws IOException
*/
private WebAppContext buildWebApContext(File tempDir, File war, String contextPath, ClassLoader classLoader) throws IOException {
WebAppContext webappContext = new WebAppContext(war.getPath(), contextPath);
webappContext.setContextPath(contextPath);
webappContext.setDisplayName(contextPath);
File workDir = new File(tempDir, war.getName());
webappContext.setTempDirectory(workDir);
webappContext.setClassLoader(new WebAppClassLoader(classLoader, webappContext));
// 解析jsp方式2:
//webappContext.addBean(new JspStarter(webappContext));
//webappContext.addServlet(JettyJspServlet.class, "*.jsp");
return webappContext;
}
}
Jetty解析jsp目前发现有两种方式
一种是在 Server 启动前,添加
Configuration.ClassList classList = Configuration.ClassList.setServerDefault(server);
classList.addBefore(JettyWebXmlConfiguration.class.getName(), AnnotationConfiguration.class.getName());
一种是在创建 WebAppContext 时添加
webappContext.addBean(new JspStarter(webappContext));
webappContext.addServlet(JettyJspServlet.class, "*.jsp");
还需要 JspStarter
public class JspStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller {
JettyJasperInitializer sci;
ServletContextHandler context;
public JspStarter(ServletContextHandler context) {
this.sci = new JettyJasperInitializer();
this.context = context;
this.context.setAttribute("org.apache.tomcat.JarScanner", new StandardJarScanner());
}
@Override
protected void doStart() throws Exception {
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
try {
sci.onStartup(null, context.getServletContext());
super.doStart();
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
}
测试,三个war包都可正常部署访问
参考:
Jetty 官方文档
Jetty 官方文档 - 嵌入Jetty
Jetty篇教程 之Jetty 嵌入式服务器
idea下,Jetty采用main方法启动web项目
内嵌式jetty服务器支持jsp
jetty介绍之handler
Jetty Connector学习笔记