从零手写实现 tomcat-07-war 如何解析处理三方的 war 包?

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 这是一个关于构建Web应用和理解类加载器的文章摘要。作者探讨了如何解析和处理WAR包,使用Netty权威指南系列(BIO, NIO, AIO)作为背景阅读。文章通过一个简单的Web项目实例,展示了项目的目录结构,包括`pom.xml`, `web.xml`和`IndexServlet`。作者还介绍了自定义的`WebAppClassLoader`,它扩展了`URLClassLoader`,用于根据类路径加载非当前项目类。最后提到了一个名为mini-cat的开源项目,它是简易版Tomcat实现,可在GitHub上找到。

拓展阅读

Netty 权威指南-01-BIO 案例

Netty 权威指南-02-NIO 案例

Netty 权威指南-03-AIO 案例

Netty 权威指南-04-为什么选择 Netty?Netty 入门教程

前言

到目前为止,我们处理的都是自己的 servlet 等。

但是 tomcat 这种做一个 web 容器,坑定要能解析处理其他的 war 包。

这个要如何实现呢?

1-war 包什么样的?

源码

直接用一个 web 简单的项目。

https://github.com/houbb/servlet-webxml

项目目录

mvn clean
tree /f

D:.
│
└─src
    └─main
        ├─java
        │  └─com
        │      └─github
        │          └─houbb
        │              └─servlet
        │                  └─webxml
        │                          IndexServlet.java
        │
        └─webapp
            │  index.html
            │
            └─WEB-INF
                    web.xml

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.github.houbb</groupId>
    <artifactId>servlet-webxml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <plugin.tomcat.version>2.2</plugin.tomcat.version>
    </properties>

    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>9.0.0.M8</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>servlet</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>${plugin.tomcat.version}</version>
                <configuration>
                    <port>8080</port>
                    <path>/</path>
                    <uriEncoding>${project.build.sourceEncoding}</uriEncoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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.html</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>index</servlet-name>
        <servlet-class>com.github.houbb.servlet.webxml.IndexServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>index</servlet-name>
        <url-pattern>/index</url-pattern>
    </servlet-mapping>

</web-app>

index.html

<!DOCTYPE html>
<html>
<body>
Hello Servlet!
</body>
</html>

servlet

package com.github.houbb.servlet.webxml;

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

/**
 * @author binbin.hou
 * @since 0.1.0
 */
public class IndexServlet extends HttpServlet {
   

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
   
        resp.setContentType("text/html");

        // 实际的逻辑是在这里
        PrintWriter out = resp.getWriter();
        out.println("<h1>servlet index</h1>");
    }

}

目录结构

打包成 war,然后解压:

mvn clean install

其实比较重要的就是 web.xml 作为一切的入口。

对应的 war

D:.
│  index.html
│
├─META-INF
│  │  MANIFEST.MF
│  │
│  └─maven
│      └─com.github.houbb
│          └─servlet-webxml
│                  pom.properties
│                  pom.xml
│
└─WEB-INF
    │  web.xml
    │
    └─classes
        └─com
            └─github
                └─houbb
                    └─servlet
                        └─webxml
                                IndexServlet.class

如何根据类路径加载类信息?类不是当前项目的

JVM-09-classloader

核心实现

package com.github.houbb.minicat.support.classloader;


import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * https://www.liaoxuefeng.com/wiki/1545956031987744/1545956487069728
 *
 * 每一个 dir 的 classLoader 独立。
 */
public class WebAppClassLoader extends URLClassLoader {
   

    private Path classPath;
    private Path[] libJars;

    public WebAppClassLoader(Path classPath, Path libPath) throws IOException {
   
        super(createUrls(classPath, libPath), ClassLoader.getSystemClassLoader());
//        super("WebAppClassLoader", createUrls(classPath, libPath), ClassLoader.getSystemClassLoader());
//
        this.classPath = classPath.toAbsolutePath().normalize();
        if(libPath.toFile().exists()) {
   
            this.libJars = Files.list(libPath).filter(p -> p.toString().endsWith(".jar")).map(p -> p.toAbsolutePath().normalize()).sorted().toArray(Path[]::new);
        }
    }

    static URL[] createUrls(Path classPath, Path libPath) throws IOException {
   
        List<URL> urls = new ArrayList<>();
        urls.add(toDirURL(classPath));

        //lib 可能不存在
        if(libPath.toFile().exists()) {
   
            Files.list(libPath).filter(p -> p.toString().endsWith(".jar")).sorted().forEach(p -> {
   
                urls.add(toJarURL(p));
            });
        }

        return urls.toArray(new URL[0]);
    }

    static URL toDirURL(Path p) {
   
        try {
   
            if (Files.isDirectory(p)) {
   
                String abs = toAbsPath(p);
                if (!abs.endsWith("/")) {
   
                    abs = abs + "/";
                }
                return URI.create("file://" + abs).toURL();
            }
            throw new IOException("Path is not a directory: " + p);
        } catch (IOException e) {
   
            throw new UncheckedIOException(e);
        }
    }

    //D:\github\minicat\src\test\webapps\servlet\WEB-INF\classes
    //D:\github\minicat\src\test\webapps\WEB-INF\classes

    static URL toJarURL(Path p) {
   
        try {
   
            if (Files.isRegularFile(p)) {
   
                String abs = toAbsPath(p);
                return URI.create("file://" + abs).toURL();
            }
            throw new IOException("Path is not a jar file: " + p);
        } catch (IOException e) {
   
            throw new UncheckedIOException(e);
        }
    }

    static String toAbsPath(Path p) throws IOException {
   
        return p.toAbsolutePath().normalize().toString().replace('\\', '/');
    }

}

开源地址

 /\_/\  
( o.o ) 
 > ^ <

mini-cat 是简易版本的 tomcat 实现。别称【嗅虎】(心有猛虎,轻嗅蔷薇。)

开源地址:https://github.com/houbb/minicat

相关文章
|
3月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
2月前
|
人工智能 前端开发 Java
【Tomcat源码分析】启动过程深度解析 (二)
本文深入探讨了Tomcat启动Web应用的过程,重点解析了其加载ServletContextListener及Servlet的机制。文章从Bootstrap反射调用Catalina的start方法开始,逐步介绍了StandardServer、StandardService、StandardEngine、StandardHost、StandardContext和StandardWrapper的启动流程。每个组件通过Lifecycle接口协调启动,子容器逐层启动,直至整个服务器完全启动。此外,还详细分析了Pipeline及其Valve组件的作用,展示了Tomcat内部组件间的协作机制。
【Tomcat源码分析】启动过程深度解析 (二)
|
5月前
|
Ubuntu 前端开发 JavaScript
技术笔记:Ubuntu:一个部署好的tomcat应用(war包)怎么用Nginx实现动静分离?
技术笔记:Ubuntu:一个部署好的tomcat应用(war包)怎么用Nginx实现动静分离?
http数据包抓包解析
http数据包抓包解析
|
2月前
|
网络协议 网络虚拟化
接收网络包的过程——从硬件网卡解析到IP
【9月更文挑战第18天】这段内容详细描述了网络包接收过程中机制。当网络包触发中断后,内核处理完这批网络包,会进入主动轮询模式,持续处理后续到来的包,直至处理间隙返回其他任务,从而减少中断次数,提高处理效率。此机制涉及网卡驱动初始化时注册轮询函数,通过软中断触发后续处理,并逐步深入内核网络协议栈,最终到达TCP层。整个接收流程分为多个层次,包括DMA技术存入Ring Buffer、中断通知CPU、软中断处理、以及进入内核网络协议栈等多个步骤。
http数据包抓包解析课程笔记
http数据包抓包解析课程笔记
|
3月前
|
应用服务中间件 Docker 容器
在服务器中使用Docker安装Tomcat、同时实现目录挂载、并且部署War包到服务器
这篇文章介绍了在Docker中安装Tomcat的过程,包括搜索Tomcat镜像、拉取镜像、目录挂载的准备、创建并挂载容器,以及如何进入容器和进行测试。文中还说明了如何将WAR包部署到Tomcat服务器并访问部署的应用。
在服务器中使用Docker安装Tomcat、同时实现目录挂载、并且部署War包到服务器
|
3月前
|
XML 存储 JavaScript
xml介绍与解析,及xml库包使用
xml介绍与解析,及xml库包使用
37 0
|
3月前
|
机器学习/深度学习 计算机视觉 Python
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
103 0
|
4月前
|
Java 应用服务中间件 Shell

推荐镜像

更多