浅入Tomcat Embed

大老赖不赖 23 2022-11-15

SpringBoot Web Starter

如果您单单引入一个Springboot 的启动器

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>2.7.5</version>
</dependency>

您可能就会看到一堆依赖
其中就包括有Tomcat的
image-1668514221425
框框里面的就是SpringBoot Web Starter一并带来的Tomcat了

精简SpringBoot Web Starter

pom.xml配置

目标:通过基本的SpringBoot Starter集成一个Tomcat,只保证基本的Servlet能使用

<properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <resource.delimiter>@</resource.delimiter>

        <!--        定义版本-->
        <spring.version>5.3.23</spring.version>
        <spring.boot.version>2.7.5</spring.boot.version>
        <apache.tomcat.version>9.0.67</apache.tomcat.version>
    </properties>
<dependencies>
        <!--        引入基本的Starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
        <!--        Tomcat,自带原生Servlet api-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${apache.tomcat.version}</version>
        </dependency>
        <!--        ServletComponentScan-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>

效果图
image-1668514410307

Servlet测试

启动类:
注意得加上@ServletComponentScan

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

Servlet:

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/")
public class HelloWorld extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.getWriter().println("Spring!");
    }
}

结果:
image-1668514992220

尝试自己封装一个Tomcat

Tomcat框架图:image

依赖

仅支持最基础的Servlet

<properties>
  <maven.compiler.source>19</maven.compiler.source>
  <maven.compiler.target>19</maven.compiler.target>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <tomcat.version>10.1.2</tomcat.version>
</properties>
<dependencies>
  <dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>${tomcat.version}</version>
  </dependency>
</dependencies>

下面开始封装

最速封装

public class Main {
    public static void main(String[] args) throws Exception {
        Tomcat.main(args);
    }
}

自己配置的封装

404版本:

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;

import java.io.File;
import java.io.IOException;

/**
 * @author LaiOrigin
 */
public class Main {
    public static void main(String[] args) throws Exception {
        //webapp的目录
        File file = new File("docBase");
        //如果不使用完整路径,启动就可能失败
        String path = file.getAbsolutePath();
        Tomcat tomcat = new Tomcat();
        //设置连接器
        Connector connector = new Connector("HTTP/1.1");
        //设置您的监听地址
        connector.setProperty("address", "localhost");
        connector.setPort(8080);
        String charSet = "UTF-8";
        if (!charSet.equals(connector.getURIEncoding())) {
            connector.setURIEncoding(charSet);
        }
        //给Tomcat加上监听器
        tomcat.setConnector(connector);
        tomcat.addContext("", path);
        tomcat.start();
        //一直监听,不然直接退出了
        tomcat.getServer().await();
    }
}

尝试添加一个Servlet

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;

import java.io.File;
import java.io.IOException;

/**
 * @author LaiOrigin
 */
public class Main {
    public static void main(String[] args) throws Exception {
        //webapp的目录
        File file = new File("docBase");
        //如果不使用完整路径,启动就可能失败
        String path = file.getAbsolutePath();
        Tomcat tomcat = new Tomcat();
        //设置连接器
        Connector connector = new Connector("HTTP/1.1");
        //设置您的监听地址
        connector.setProperty("address", "localhost");
        connector.setPort(8080);
        String charSet = "UTF-8";
        if (!charSet.equals(connector.getURIEncoding())) {
            connector.setURIEncoding(charSet);
        }
        //给Tomcat加上监听器
        tomcat.setConnector(connector);
        Context context = tomcat.addContext("", path);
        tomcat.addServlet("", "SimpleServlet", new SimpleServlet());
        context.addServletMappingDecoded("/","SimpleServlet");
        tomcat.start();
        //一直监听,不然直接退出了
        tomcat.getServer().await();
    }
}

/**
 * 一个简单的Servlet
 */
class SimpleServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.getWriter().println("Hello,Embed Tomcat!");
    }
}

效果图:
image-1668516543540
但是如果不手动添加Servlet使用注解@WebServlet("/")的话,就会报404

添加Servlet 注解支持

想办法让我的Tomcat读取ClassPath

...
//加上
Context context = tomcat.addWebapp("", path);
String webClassesFolder = Thread.currentThread().getContextClassLoader().getResource("").getPath();
File additionWebInfClassesFolder = new File(webClassesFolder);
WebResourceRoot resourceRoot = new StandardRoot(context);
WebResourceSet resourceSet;
if (additionWebInfClassesFolder.exists()) {
   resourceSet = new DirResourceSet(resourceRoot, Constants.WEB_INF_CLASSES, webClassesFolder, "/");
 } else {
    resourceSet = new EmptyResourceSet(resourceRoot);
}
resourceRoot.addPreResources(resourceSet);
context.setResources(resourceRoot);
...

支持jsp

<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-el</artifactId>
  <version>${tomcat.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-jasper</artifactId>
  <version>${tomcat.version}</version>
</dependency>
<%= 1 + 1%>

image-1668557402142
image-1668557372261

参考
嵌入式 Tomcat 作为嵌入 Java 应用程序的库, 你可以在 mvnrepository 下载 发行版Jar 以及源码
嵌入式tomcat的使用