嵌入式Tomcat学习-第四天

写在前面

什么是嵌入式 Tomcat?

嵌入式 Tomcat 是将 Apache Tomcat 服务器作为库文件集成到 Java 应用程序中的技术方案。它允许开发者通过编程方式直接启动、配置和管理 Tomcat,无需依赖独立安装的 Tomcat 服务器,适用于构建轻量级、自包含的 Web 应用或微服务。

版本特性(11.0.3)

  • Servlet/JSP 规范支持
    支持 Servlet 6.0 和 JSP 4.0 规范(基于 Jakarta EE 10 平台)。
  • 轻量级嵌入
    通过 tomcat-embed-core 等依赖包实现核心功能集成,无需完整 Tomcat 安装。
  • Java 版本要求
    最低要求 Java 17 及以上版本。
  • 性能优化
    改进的 HTTP/2 支持、连接器性能调优及内存管理优化。

核心优势

  • 简化部署
    应用与服务器一体化,直接打包为可执行 JAR 文件,适合云原生场景。
  • 灵活配置
    通过代码动态配置端口、上下文路径、SSL 等参数,无需 server.xml 文件。
  • 开发便捷
    与 Spring Boot、Micronaut 等框架无缝集成,快速构建独立 Web 服务。
  • 资源占用低
    仅加载必要的组件,启动速度快,适合资源受限环境。

典型应用场景

  • 微服务架构中的独立服务节点
  • 快速原型开发或测试环境
  • 命令行工具集成 Web 接口
  • 需要定制化 Web 容器行为的场景

集成springmvc

什么是springmvc

SpringMVCSpring Framework 的一部分,它实现了 Model-View-Controller (MVC) 设计模式,并用于构建灵活且松耦合的 web 应用程序。SpringMVC 提供了一种分离应用逻辑和用户界面的方法,这有助于开发者管理复杂的应用程序,并且使得应用程序更易于扩展和维护。

核心组件和功能

SpringMVC 的核心组件包括:

  • DispatcherServlet​:作为前端控制器,它负责将请求路由到不同的处理器。
  • HandlerMapping​:确定请求的目标处理器。
  • Controller​:处理请求并返回 ModelAndView 对象,其中包含模型数据和视图名称。
  • ViewResolver​:解析视图名称到具体的视图实现。
  • Model​:在控制器和视图之间传递数据。
  • View​:负责渲染模型数据,通常是 JSPThymeleaf 页面。

工作流程

SpringMVC 的工作流程涉及以下步骤:

  1. DispatcherServlet 接收到 HTTP 请求。
  2. HandlerMapping 确定请求的处理器。
  3. Controller 处理请求并返回 ​ModelAndView​。
  4. ViewResolver 解析视图名称到具体视图。
  5. View 渲染模型数据并返回给客户端。

如何集成到tomcat中

移除上一篇文章中的struts相关内容

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lhstack.tomcat</groupId>
    <artifactId>tomcat-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>11.0.3</version>
        </dependency>

    </dependencies>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>阿里云</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>

</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <welcome-file-list>
        <welcome-file>index</welcome-file>
    </welcome-file-list>
</web-app>

保留如下图

image-dxfi.png

修改main函数如下

package com.lhstack.tomcat;

import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.websocket.server.WsSci;

import java.util.Collections;

public class TomcatApplication {
    public static void main(String[] args) throws Throwable {
        Tomcat tomcat = new Tomcat();
        //2025-02-15新增 新增webapp,指定路径
        Context context = tomcat.addWebapp("", System.getProperty("user.dir") + "/webapp");
        //2025-02-15 添加websocket支持和websocket连接监听器
        context.addServletContainerInitializer(new WsSci(), Collections.singleton(WebSocketListener.class));
        Connector connector = tomcat.getConnector();
        connector.setPort(8080);
        tomcat.start();
        tomcat.getServer().await();
    }
}

添加spring-webmvc依赖

pom.xml

image-ssgm.png

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lhstack.tomcat</groupId>
    <artifactId>tomcat-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>6.2.3</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>11.0.3</version>
        </dependency>

    </dependencies>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>阿里云</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>

</project>

在web.xml添加dispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <welcome-file-list>
        <welcome-file>index</welcome-file>
    </welcome-file-list>
    
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

在resource目录下面创建spring-mvc.xml

spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <mvc:annotation-driven/>
    <context:component-scan base-package="com.lhstack.tomcat.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

添加TestController

image-zsdp.png

package com.lhstack.tomcat.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("test")
public class TestController {

    @GetMapping
    public Map<String,String> test(){
        return Map.of("msg","hello world");
    }
}

添加TestIndexController和修改index.jsp

image-pchz.png

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

Hello This Is Index Jsp
</body>
</html>

image-ndaa.png

package com.lhstack.tomcat.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("jsp")
public class TestIndexController {

    @GetMapping
    public String index(){
        return "WEB-INF/jsp/index.jsp";
    }
}

启动项目并访问

image-hatm.png

image-jwfi.png

image-ogza.png

集成log4j2

pom.xml增加相关依赖

image-ivlw.png

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lhstack.tomcat</groupId>
    <artifactId>tomcat-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>6.2.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.24.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.24.3</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>11.0.3</version>
        </dependency>

    </dependencies>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>阿里云</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>

</project>

在resource目录添加log4j2.xml

image-qlep.png

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="tomcat-demo">
    <Properties>
        <Property name="ConsolePattern">%d{yyyy-MM-dd HH:mm:ss} - ${sys:user.name} [%thread]|-%-5level %logger{100}-[ %msg ]%n%throwable</Property>
    </Properties>
    <Appenders>

        <Console name="DefaultConsole" target="SYSTEM_OUT">
            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${ConsolePattern}"/>
        </Console>

    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="DefaultConsole"/>
        </Root>
    </Loggers>
</Configuration>

修改TestController,增加日志输出

image-ccfr.png

package com.lhstack.tomcat.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("test")
public class TestController {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

    @GetMapping
    public Map<String,String> test(){
        LOGGER.info("test");
        return Map.of("msg","hello world");
    }
}

启动项目并测试

image-qozr.png

image-iacd.png

为log4j2增加异步支持

简介

异步特点
  • 无锁队列设计​:使用 Disruptor 库的环形缓冲区(RingBuffer),避免线程竞争,减少同步开销。
  • 低内存分配​:通过预分配内存和对象复用,减少垃圾回收压力。
  • 非阻塞主线程​:日志事件生成后立即返回,由独立线程处理写入,避免 I/O 阻塞主线程。
  • 高并发优化​:在大量并发请求下,异步模式显著减少日志记录对业务性能的影响。
  • 独立线程池​​:异步日志使用专用后台线程处理写入,与应用线程隔离。
  • 线程数控制​:默认根据 CPU 核心数分配,避免过多线程竞争。
  • 基于 Disruptor 的高性能无锁环形缓冲区,实现真正的非阻塞异步。
  • 支持细粒度控制,可为不同 Logger 配置异步或同步模式。
潜在风险与注意事项
  • 事件丢失风险​:若系统崩溃时 RingBuffer 中未持久化的日志会丢失(可通过 shutdownHook 缓解)。
  • 延迟写入​:异步模式下日志可能不会立即落盘,不适合对实时性要求极高的场景。
  • 依赖 Disruptor​:需引入 disruptor 依赖包以启用 AsyncLogger。
性能调优建议
  • 增大 RingBuffer 容量​:避免生产者(应用线程)与消费者(日志线程)速度不匹配导致的等待。
  • 选择等待策略​:根据场景平衡吞吐量与延迟(如 BlockingWaitStrategy 适合低延迟,SleepingWaitStrategy 适合高吞吐)
适用场景
  • 高并发服务​:如 Web 服务器、微服务等需要快速响应的场景。
  • 性能敏感型应用​:需减少日志记录对主业务线程的影响。
  • 非关键日志​:允许短暂延迟或少量日志丢失的场景(如调试日志)。

pom.xml增加相关依赖

image-ssjj.png

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lhstack.tomcat</groupId>
    <artifactId>tomcat-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/com.lmax/disruptor -->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>4.0.0</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>6.2.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.24.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.24.3</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>11.0.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>11.0.3</version>
        </dependency>

    </dependencies>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>阿里云</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>

</project>

在resources添加log4j2.system.properties

配置log4j2异步支持

image-jwlx.png

log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
log4j2.AsyncQueueFullPolicy=org.apache.logging.log4j.core.async.DiscardingAsyncQueueFullPolicy
log4j2.asyncLoggerRingBufferSize=1024
log4j2.asyncLoggerWaitStrategy=Yield

启动项目,debug查看

在AsyncLogger中debug查看是否进入

image-vrih.png

image-mosd.png

结尾

源码
今天介绍了如何在嵌入式tomcat中集成spring-webmvc,以及如何在项目中添加log4j2日志管理,以及如何集成异步日志达到高性能输入日志,大家可以通过这篇文章完成一个小小的demo,如一个简单的用户管理系统


嵌入式Tomcat学习-第四天
https://blog.lhstack.xyz/archives/tomcat-fourthday
作者
lhstack
发布于
2025年02月18日
许可协议