首页 > 其他分享 >SpringBoot中的Tomcat(内嵌式)使用及分析

SpringBoot中的Tomcat(内嵌式)使用及分析

时间:2024-04-07 22:56:09浏览次数:32  
标签:内嵌式 Tomcat org private static context new import SpringBoot

前言

Tomcat 是我们在项目中使用最多的 Web 应用服务器,今天通过代码来简单分析下 SpringBoot 中是如何启动内嵌式 Tomcat 的。

使用

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
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.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.ParallelWebappClassLoader;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.springframework.util.ClassUtils;

/**
 * 每一个Context都是一个项目,可以有单独的contextPath,每一个Connector都是一个监听端口
 */
public class TestTomcat {

    private static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";

    public static void main(String[] args) throws LifecycleException {
        Tomcat tomcat = new Tomcat();
        File baseDir = createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(DEFAULT_PROTOCOL);
        Connector connector2 = new Connector(DEFAULT_PROTOCOL);
        tomcat.getService().addConnector(connector);
        tomcat.getService().addConnector(connector2);
        customizeConnector(connector);
        customizeConnector2(connector2);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        prepareContext(tomcat.getHost());
        prepareContext2(tomcat.getHost());
        tomcat.start();
    }

    private static void prepareContext(Host host) {
        StandardContext context = new StandardContext();
        context.setName(getContextPath());
        context.setPath(getContextPath());
        File docBase = createTempDir("tomcat-docbase");
        context.setDocBase(docBase.getAbsolutePath());
        context.addLifecycleListener(new FixContextListener());
        context.setParentClassLoader(ClassUtils.getDefaultClassLoader());
        context.setUseRelativeRedirects(false);
        try {
            context.setCreateUploadTargets(true);
        } catch (NoSuchMethodError ex) {
            // Tomcat is < 8.5.39. Continue.
        }
        WebappLoader loader = new WebappLoader(context.getParentClassLoader());
        loader.setLoaderClass(ParallelWebappClassLoader.class.getName());
        loader.setDelegate(true);
        context.setLoader(loader);
        addDefaultServlet(context);
        host.addChild(context);
        configureContext(context);
    }

    private static void prepareContext2(Host host) {
        StandardContext context = new StandardContext();
        context.setName(getContextPath() + "2");
        context.setPath(getContextPath() + "2");
        File docBase = createTempDir("tomcat-docbase2");
        context.setDocBase(docBase.getAbsolutePath());
        context.addLifecycleListener(new FixContextListener());
        context.setParentClassLoader(ClassUtils.getDefaultClassLoader());
        context.setUseRelativeRedirects(false);
        try {
            context.setCreateUploadTargets(true);
        } catch (NoSuchMethodError ex) {
            // Tomcat is < 8.5.39. Continue.
        }
        WebappLoader loader = new WebappLoader(context.getParentClassLoader());
        loader.setLoaderClass(ParallelWebappClassLoader.class.getName());
        loader.setDelegate(true);
        context.setLoader(loader);
        addDefaultServlet(context);
        host.addChild(context);
        configureContext2(context);
    }

    private static void configureContext(Context context) {
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
                Dynamic registration = ctx.addServlet("myServlet", new MyServlet());
                registration.setLoadOnStartup(1);
                registration.addMapping("/myServlet");
            }
        }, new HashSet<>());
    }

    private static void configureContext2(Context context) {
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
                Dynamic registration = ctx.addServlet("myServlet2", new MyServlet());
                registration.setLoadOnStartup(1);
                registration.addMapping("/myServlet2");
            }
        }, new HashSet<>());
    }

    private static class MyServlet extends HttpServlet {

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

    private static void addDefaultServlet(Context context) {
        Wrapper defaultServlet = context.createWrapper();
        defaultServlet.setName("default");
        defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
        defaultServlet.addInitParameter("debug", "0");
        defaultServlet.addInitParameter("listings", "false");
        defaultServlet.setLoadOnStartup(1);
        // Otherwise the default location of a Spring DispatcherServlet cannot be set
        defaultServlet.setOverridable(true);
        context.addChild(defaultServlet);
        context.addServletMappingDecoded("/", "default");
    }

    private static void customizeConnector(Connector connector) {
        int port = Math.max(getPort(), 0);
        connector.setPort(port);
        connector.setURIEncoding(StandardCharsets.UTF_8.name());
        // Don't bind to the socket prematurely if ApplicationContext is slow to start
        connector.setProperty("bindOnInit", "false");
    }

    private static void customizeConnector2(Connector connector) {
        int port = Math.max(getPort2(), 0);
        connector.setPort(port);
        connector.setURIEncoding(StandardCharsets.UTF_8.name());
        // Don't bind to the socket prematurely if ApplicationContext is slow to start
        connector.setProperty("bindOnInit", "false");
    }

    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 String getContextPath() {
        return "/testtomcat";
    }

    private static int getPort() {
        return 8989;
    }

    private static int getPort2() {
        return 8990;
    }
}

定义了两个 Context,类似在 Tomcat 内部署了两个服务,每个项目服务启动了一个监听端口,代码启动之后,我们可以通过以下两个地址来访问。
http://localhost:8989/testtomcat/myServlet,
http://localhost:8990/testtomcat2/myServlet2

分析

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

  1. SpringBoot 默认使用的 ApplicationContext 实现类为 AnnotationConfigServletWebServerApplicationContext,具体判断逻辑为 ApplicationContextFactory 的 DEFAULT。
  2. AnnotationConfigServletWebServerApplicationContext 继承 ServletWebServerApplicationContext 的 onRefresh() 方法,通过 TomcatServletWebServerFactory 的 getWebServer() 方法来创建 WebServer,在这个过程中就会创建 Tomcat 对象并启动。

标签:内嵌式,Tomcat,org,private,static,context,new,import,SpringBoot
From: https://www.cnblogs.com/strongmore/p/18038815

相关文章

  • Springboot计算机毕业设计财务报销微信小程序【附源码】开题+论文+mysql+程序+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着移动互联网技术的飞速发展,微信小程序作为一种新型的应用形态,以其便捷、高效的特点受到了广大用户的青睐。在高等教育领域,财务管理是学校运营中不......
  • Springboot计算机毕业设计博物馆预约小程序【附源码】开题+论文+mysql+程序+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在信息化、数字化日益发展的今天,博物馆作为传承历史文化的重要场所,其管理和服务方式也在不断革新。传统的博物馆参观方式往往受限于开放时间、参观人......
  • @SpringbootApplication注解
    @SpringBootConfiguration是一个组合注解,由@ComponentScan、@EnableAutoConfiguration和@SpringBootConfiguration组成@SpringBootConfiguration与普通@Configuration相比,唯一区别是前者要求整个app中只出现一次@ComponentScanexcludeFilters-用来在组件扫描......
  • windows环境下使用tomcat搭建文件服务器(带权限验证)
    操作系统:Windows11 jdk:jdk1.8tomcat版本:8.5.100 开始准备所需物料。tomcat下载地址:https://tomcat.apache.org/download-80.cgi选择windows64位选择64-bitWindowszip。查看tomcat版本说明支持,tomcat8支持1.7及以上,我这边下载1.8版本。jdk下载路径:https://www.or......
  • Java Tomcat7中使用Quartz2.2实现定时任务项目代码实例(demo)
    ​ 在Java中使用Quartz2.2结合Tomcat7实现定时任务是一种常见的需求,Quartz是一个强大的定时任务库,能够帮助开发者轻松实现复杂的定时任务调度。以下是一个简单的示例,展示如何在JavaWeb应用中集成Quartz来执行定时任务。 详细文档:JavaTomcat7中使用Quartz2.2实现定时任务......
  • SpringBoot 日志显示(truncate...),输出完整日志
    本文地址:https://www.cnblogs.com/hchengmx/p/18119562在查看SpringBoot查看日志中,http的responsebody会显示不全,如下:2024-04-0709:39:53.758|172.17.0.8|DEBUG|[qtp1763344271-7365]|org.springframework.core.log.LogFormatUtils.traceDebug(LogFormatUtils.java:119)|Writi......
  • SpringBoot系列---【JDK版本导致AOP获取参数名为空】
    1.问题描述我使用AOP记录请求入参和响应,异步写入es,在获取请求入参的参数名时候,发现在本地没问题,发到云上测试环境就取不到了。privateMap<String,Object>buildRequestParam(ProceedingJoinPointjoinPoint){MehtodSignaturesignature=(MethodSignature)joinPoint.getSig......
  • SpringBoot集成mqtt启动就不断报已断开连接问题
    踩坑记录,实在是天坑!!!原因一:clientId相同,即clientId重复导致(不过我不是这个问题)我的问题是:项目启动成功后,控制台不停地反复输出:已断开连接,,,加了重连机制后,则不停地输出:重连失败,已连接客户机,,,尼玛,,关键点还在于我能接收到订阅的消息(不影响消息处理),这又是什么情况,明明没断连,确一直......
  • SpringBoot中bean的生命周期
    目录概述使用场景代码演示bean初始化TestSupportBeanPostProcessorImpllog代码概述Bean生命周期管理是SpringBoot中的关键功能之一。它负责管理应用程序中的Java对象,这些对象被称为Beans。SpringBoot通过创建、配置、初始化和销毁这些Beans来确保应用程序的正常运行......
  • 基于SpringBoot的“垃圾分类网站”的设计与实现(源码+数据库+文档+PPT)
    基于SpringBoot的“垃圾分类网站”的设计与实现(源码+数据库+文档+PPT)开发语言:Java数据库:MySQL技术:SpringBoot工具:IDEA/Ecilpse、Navicat、Maven系统展示系统功能结构图系统功能界面图用户登录、用户注册界面图4垃圾图谱界面图管理员登录界面图用户......