大家天天开心!
文章目录
前言
我们在最开始学JavaWeb的时候,是需要配置web.xml文件,来找到Servlet相应的服务,然后还有以注解的方式,这里要清楚注解的方式是@interface.然后我想带着大家了解一下Tomcat到底替我们干了多少事,我们就知道Tomcat有多牛逼了。
提示:以下是本篇文章正文内容,下面案例可供参考
一、对我的这个Tomcat简介:
该Tomcat(或其他Servlet容器)是通过反射获取@WebServlet
注解信息并实例化Servlet的。
对于这个写的Tomcat服务所完成的:
-
反射运用正确:你使用
Class.forName()
加载类,并用getAnnotation(WebServlet.class)
获取注解对象,这是标准做法。 -
成功提取注解信息:你通过
annotation.urlPatterns()
拿到了配置的URL模式,这正是Servlet容器建立映射关系的关键。 -
实例化Servlet:使用
aClass.newInstance()
(注:较新JDK版本推荐使用getDeclaredConstructor().newInstance()
)创建了Servlet实例,模拟了容器初始化Servlet的过程。
二、完整详细代码:
java
package com.hspedu.servlet.annotation;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsServer;
import javax.servlet.annotation.WebServlet;
import java.util.HashMap;
/**
* @author 韩顺平
* @version 1.0
* 模拟一把Tomcat是如果通过 @WebServlet(urlPatterns = {"/ok1", "/ok2"})
* 来装载一个Servlet的
*
* 说明:这代码主要的目的,就是打破 注解的神秘感
*/
public class TestAnnotationServlet {
private static final HashMap<String, Object> hm = new HashMap<>();
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//1. 首先要得到扫描的包 路径 io, 进而得到类的全路径
String classAllPath = "com.hspedu.servlet.annotation.OkServlet";
//2. 得到 OkServlet的Class对象
Class<?> aClass = Class.forName(classAllPath);
//3. 通过class对象,得到Annotation
WebServlet annotation = aClass.getAnnotation(WebServlet.class);
System.out.println(annotation);
String[] strings = annotation.urlPatterns();
for (String url : strings) {
System.out.println("url= " + url);
}
//如果匹配url,如果是第一次,tomcat就会创建一个OkServlet实例,放入到hashmap
Object instance = aClass.newInstance();
System.out.println("instance= " + instance);//OkServlet
//简单的模拟,没有深入.
hm.put("OkServlet", instance);
System.out.println(hm);
}
}
你的代码 | 完整的 Tomcat |
---|---|
手动指定 OkServlet 的全类名进行加载。 |
自动扫描 整个 classpath 或指定包下的所有类,寻找带有 @WebServlet 等注解的类。 |
使用 HashMap<String, Object> 存储实例。 |
使用复杂的上下文和生命周期管理 。HashMap 的 value 通常是 Servlet 实例,key 则是其配置名或 URL 模式。 |
没有 HTTP 服务器功能,无法接收和处理实际请求。 | 包含一个 HTTP 服务器(基于 Socket),监听端口,解析 HTTP 协议,并将请求路由到对应的 Servlet。 |
实例化后直接放入 Map,没有初始化过程。 | 在 Servlet 实例化后,会回调其 init(ServletConfig) 方法,完成初始化。 |
没有请求处理和分发逻辑。 | 接收到 HTTP 请求后,根据 URL 查找映射到哪个 Servlet ,然后调用该 Servlet 的 service 方法 ,进而分发到 doGet 或 doPost 等方法。 |
下面是代码的详细解释:
java
String classAllPath = "com.hspedu.servlet.annotation.OkServlet";
Class<?> aClass = Class.forName(classAllPath);
-
-
这行代码手动指定了要加载的 Servlet 类的全限定名。在实际的 Tomcat 中,这个过程是自动的 。Tomcat 会扫描整个 Web 应用(如 WAR 包的
WEB-INF/classes
和WEB-INF/lib
中的 JAR 包),找到所有带有@WebServlet
注解的类。 -
Class.forName()
方法利用 Java 的反射机制 ,根据类名动态地加载这个类到 JVM 中,并返回它的Class
对象。这是所有后续操作的基础。
-
java
WebServlet annotation = aClass.getAnnotation(WebServlet.class);
System.out.println(annotation);
-
通过
Class
对象的getAnnotation()
方法,并传入注解的类型(WebServlet.class
),可以获取到该类上的特定注解对象。 -
如果这个类上确实标注了
@WebServlet
,那么这里返回的就是一个包含了所有注解属性值的WebServlet
注解实例。如果不存在,则返回null
。 -
打印
annotation
可以看到注解的具体信息,如@javax.servlet.annotation.WebServlet(urlPatterns={"/ok1", "/ok2"}, ...)
。
java
String[] strings = annotation.urlPatterns();
for (String url : strings) {
System.out.println("url= " + url);
}
-
这是整个模拟的关键目的 。从注解对象中,你可以调用其方法(如
urlPatterns()
)来获取配置的属性值。 -
Tomcat 在启动时就是这样做的 :它读取所有 Servlet 类上的
@WebServlet
注解,建立了一个 URL 路径到 Servlet 类的映射表。
java
Object instance = aClass.newInstance(); // 注意:较新JDK推荐getDeclaredConstructor().newInstance()
System.out.println("instance= " + instance);
-
通过
Class
对象的newInstance()
方法(注:较新版本的 JDK 已弃用此方法,推荐使用getDeclaredConstructor().newInstance()
),可以创建该 Servlet 类的一个新实例。 -
Tomcat 正是在这个时候创建 Servlet 实例的(通常在容器启动或首次请求时,取决于配置)。
-
打印出的
instance
应该是类似com.hspedu.servlet.annotation.OkServlet@xxx
的形式,证明实例化成功了。
java
hm.put("OkServlet", instance);
System.out.println(hm);
-
这里用一个
HashMap
来模拟 Tomcat 内部用于管理 Servlet 实例的容器。 -
在真实的 Tomcat 中,维护了一个更为复杂但功能类似的结构,用来根据 URL 查找并获取对应的 Servlet 实例来处理请求。
总结
最核心的"发现"和"装载"机制:
-
发现 (Discovery):通过扫描或配置,找到所有需要管理的 Servlet 类。
-
解析 (Parsing) :读取类上的元信息(这里是
@WebServlet
注解),特别是 URL 映射。 -
装载 (Loading & Instantiation) :使用反射机制动态地创建这些 Servlet 的实例。
-
管理 (Management):将实例存储起来,建立 URL 到实例的映射关系,以备请求时调用。
如果要让这个模拟更接近真实的 Tomcat,接下来可以考虑:
-
实现一个简单的 HTTP 服务器 :使用
ServerSocket
监听端口(如 8080)。 -
解析 HTTP 请求 :从 Socket 连接中读取数据,解析出请求的 URL 路径。
-
实现请求分发 :根据解析出的路径,从你的
HashMap
里找到对应的 Servlet 实例,然后通过反射调用其service
方法。