创作缘由
平时使用 tomcat 等 web 服务器不可谓不多,但是一直一知半解。
于是想着自己实现一个简单版本,学习一下 tomcat 的精髓。
系列教程
从零手写实现 apache Tomcat-01-入门介绍
从零手写实现 apache Tomcat-02-web.xml 入门详细介绍
从零手写实现 tomcat-03-基本的 socket 实现
从零手写实现 tomcat-04-请求和响应的抽象
从零手写实现 tomcat-05-servlet 处理支持
从零手写实现 tomcat-06-servlet bio/thread/nio/netty 池化处理
从零手写实现 tomcat-07-war 如何解析处理三方的 war 包?
从零手写实现 tomcat-08-tomcat 如何与 springboot 集成?
从零手写实现 tomcat-09-servlet 处理类
从零手写实现 tomcat-10-static resource 静态资源文件
从零手写实现 tomcat-11-filter 过滤器
从零手写实现 tomcat-12-listener 监听器
前言
到目前为止,我们处理的都是自己的 servlet 等。
但是 tomcat 这种做一个 web 容器,坑定要能解析处理其他的 war 包。
这个要如何实现呢?
1-war 包什么样的?
源码
直接用一个 web 简单的项目。
https://github.com/houbb/servlet-webxml
项目目录
mvn clean
tree /fD:.
│
└─src└─main├─java│ └─com│ └─github│ └─houbb│ └─servlet│ └─webxml│ IndexServlet.java│└─webapp│ index.html│└─WEB-INFweb.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 {@Overrideprotected 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└─webxmlIndexServlet.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\classesstatic 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