SpringBoot中的Jetty使用及分析

前言

和 Tomcat 类似,Jetty 也是一个 Web 应用服务器,相对于 Tomcat,Jetty 更加轻量、更加简易、更加灵活。今天通过代码来简单分析下 SpringBoot 中是如何启动 Jetty 的。

Jetty简介

使用

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.util.ClassUtils;

public class TestJetty {

  public static void main(String[] args) throws Exception {
    WebAppContext context = new WebAppContext();
    InetSocketAddress address = new InetSocketAddress((InetAddress) null, getPort());
    configureWebAppContext(context);
    Server server = createServer(address);
    server.setHandler(context);
    server.start();
  }

  private static void configureWebAppContext(WebAppContext context) {
    context.setTempDirectory(getTempDirectory());
    context.setClassLoader(ClassUtils.getDefaultClassLoader());
    context.setContextPath(getContextPath());
    configureDocumentRoot(context);
    addDefaultServlet(context);
    Configuration configuration = new AbstractConfiguration() {
      @Override
      public void configure(WebAppContext context) throws Exception {
        ServletContext ctx = context.getServletContext();
        Dynamic registration = ctx.addServlet("myServlet", new MyServlet());
        registration.setLoadOnStartup(1);
        registration.addMapping("/myServlet");
      }
    };
    Configuration[] configurations = new Configuration[]{configuration};
    context.setConfigurations(configurations);
    context.setThrowUnavailableOnStartupException(true);
  }

  private static void configureDocumentRoot(WebAppContext handler) {
    File docBase = createTempDir("jetty-docbase");
    try {
      List<Resource> resources = new ArrayList<>();
      Resource rootResource = (docBase.isDirectory() ? Resource
          .newResource(docBase.getCanonicalFile())
          : JarResource.newJarResource(Resource.newResource(docBase)));
      resources.add(rootResource);
      handler.setBaseResource(new ResourceCollection(resources.toArray(new Resource[0])));
    } catch (Exception ex) {
      throw new IllegalStateException(ex);
    }
  }

  private static Server createServer(InetSocketAddress address) {
    Server server = new Server();
    server.setConnectors(new Connector[]{createConnector(address, server)});
    return server;
  }

  private static AbstractConnector createConnector(InetSocketAddress address, Server server) {
    ServerConnector connector = new ServerConnector(server, -1, -1);
    connector.setHost(address.getHostString());
    connector.setPort(address.getPort());
    for (ConnectionFactory connectionFactory : connector.getConnectionFactories()) {
      if (connectionFactory instanceof HttpConfiguration.ConnectionFactory) {
        ((HttpConfiguration.ConnectionFactory) connectionFactory).getHttpConfiguration()
            .setSendServerVersion(false);
      }
    }
    return connector;
  }

  private static class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
      resp.getWriter().println("hello Jetty");
      resp.getWriter().flush();
    }
  }

  private static void addDefaultServlet(WebAppContext context) {
    ServletHolder holder = new ServletHolder();
    holder.setName("default");
    holder.setClassName("org.eclipse.jetty.servlet.DefaultServlet");
    holder.setInitParameter("dirAllowed", "false");
    holder.setInitOrder(1);
    context.getServletHandler().addServletWithMapping(holder, "/");
    context.getServletHandler().getServletMapping("/").setDefault(true);
  }

  private static File createTempDir(String prefix) {
    try {
      File tempDir = File.createTempFile(prefix + ".", "." + getPort());
      tempDir.delete();
      tempDir.mkdir();
      tempDir.deleteOnExit();
      return tempDir;
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return null;
  }


  private static File getTempDirectory() {
    String temp = System.getProperty("java.io.tmpdir");
    return (temp != null) ? new File(temp) : null;
  }

  private static String getContextPath() {
    return "/testjetty";
  }

  private static int getPort() {
    return 8989;
  }
}

定义一个 WebAppContext,启动 8989 监听端口,代码启动之后,我们可以通过以下地址来访问。
http://localhost:8989/testjetty/myServlet

分析

上述代码完全是参考 SpringBoot 内创建并启动 Jetty 的过程,具体流程如下

  1. SpringBoot 默认使用的 ApplicationContext 实现类为 AnnotationConfigServletWebServerApplicationContext,具体判断逻辑为 ApplicationContextFactory 的 DEFAULT。
  2. AnnotationConfigServletWebServerApplicationContext 继承 ServletWebServerApplicationContext 的 onRefresh() 方法,通过 JettyServletWebServerFactory 的 getWebServer() 方法来创建 WebServer,在这个过程中就会创建 Server 对象并启动。
posted @ 2024-04-13 12:54  strongmore  阅读(498)  评论(0编辑  收藏  举报