JavaWeb-GenericServlet源码分析(适配器/模板方法)
文章目录
- 类直接实现Servlet接口的弊端
- Servlet接口的方法
- 适配器设计模式
- 适配器对象的改造
- 关于init方法的ServletConfig对象来源
- 使用模板方法设计模式改造init方法
- GenericServlet内置抽象类
- ServletConfig接口
- ServletConfig接口简介
- 测试
- 再谈GenericServlet抽象类
类直接实现Servlet接口的弊端
Servlet接口的方法

上面是jakarta.servlet.Servlet接口中的方法
- init(): 初始化Servlet对象的相关信息
- service(): 业务的核心方法, 也是Tomcat调用该对象实现逻辑的入口
- destroy(): 销毁Servlet对象的信息
- getServletConfig(): 获取
ServletConfig对象(这个下面再说) - getServletInfo(): 获取一些无用的信息(作者, 版本号之类的)
适配器设计模式
为什么类不能直接实现Servlet接口呢, 是因为对于一个Servlet对象来说, 除了service方法, 其他的对象都是不经常用的, 如果我任意一个类都去实现Servlet接口中的所有方法, 那最后的结果就是代码十分的冗余…
适配器设计模式可以类比为手机不能直接插在220V电源上, 需要一个适配器的充电头进行转换…
所以我们创建一个适配器(也就是一个抽象类), 实现这个Servlet接口中的大部分方法, 只把一些需要子类重定义的方法抽象出来, 这样就可以使得代码的冗余度大大降低, 这其实就是适配器设计模式的核心
我们创建一个适配器的抽象类代码如下
import jakarta.servlet.*;import java.io.IOException;/*** 适配器设计模式* 因为我们并不是所有的Servlet对象实现的时候并不需要所有的Servlet方法* 需要我们添加一个适配器, 实现里面的大部分方法即可(除了Service)*/
public abstract class ServletAdapt implements Servlet{@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;@Overridepublic String getServletInfo() {return "";}@Overridepublic void destroy() {}
}
下面我们创建一个子类实现这个适配器中的抽象方法
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;import java.io.IOException;public class UserServlet extends ServletAdapt{@Overridepublic void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {// 重写适配器的service方法对外提供服务即可System.out.println("UserServlet service method called");}
}
适配器对象的改造
我们官方都把适配器对象称之为GenericServlet(标题的由来)
关于init方法的ServletConfig对象来源

思考这个ServletConfig对象是谁传过来的, 我们大致说一下底层的Tomcat伪代码
class Tomcat{public static void main(String[] args) {// 首先通过反射机制拿到相关类对象Class clazz = Class.forName("全限定类名");// 构造出该类的一个实例Object object = clazz.getConstructor().newInstance();// 向下转型成为Servlet对象Servlet servletImp = (Servlet) object; // 创建一个ServletConfig对象ServletConfig servletConfig = new ServletConfigImp();// 初始化这个类的实例servletImp.init(servletConfig);// 使用service方法提供服务servletImp.service(ServletRequest request, ServletResponse response);}
}
所以我们的ServletConfig对象, 是 Tomcat服务器创建出来的
我们想查看以下这个实现ServletConfig接口的对象的信息(改造init方法)

在web.xml文件中添加映射信息如下

在浏览器中输入URL访问…

上面我们说了, 这个对象是 Tomcat 服务器实现的, 我们现在找到上次下载的关于 Tomcat 服务器的源码找到这个类进行分析

可以发现这个类实现了jakarta.servlet.ServletConfig 接口, 这同时也证明了, 我们的Tomcat 服务器实现了 Servlet规范
使用模板方法设计模式改造init方法
关于模板方法设计模式, 核心总结就是下面的一句话
- 对拓展开放, 对修改关闭
假如, 我们想要这个 ServletConfig 对象那应该怎么办呢
init方法的其中一个参数就是ServletConfig, 所以我们可以创建一个实例变量接住这个临时变量…

但是假如子类继承了这个适配器, 并且尝试对其中的init方法进行重写, 那我们的config 对象不就有变为空了吗,
所以一个简单的策略是直接把init方法使用final修饰, 此时不允许子类进行重写…

但是还有一个问题, 假设我们的子类真的想要重写init方法, 但是又不想config对象变成空, 那我们就可以采用模板方法设计模式


这样就会在不破坏原有代码的基础上进行代码能力的扩展, 这就是模板方法设计模式

可以看到, 在源代码基本功能不变的基础上, 重写的方法正常执行了…
GenericServlet内置抽象类
其实我们上面的分析出来的内容, 实际上内置的类已经实现了相关方法了
GenericServlet实现了Servlet接口 & ServletConfig接口
下面是我们的GenericServlet的源码解析

可以看到里面init方法正是使用了模板方法设计模式进行的设计, 十分巧妙…
完整源码
package jakarta.servlet;import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {private static final long serialVersionUID = 1L;private transient ServletConfig config;public GenericServlet() {}public void destroy() {}public String getInitParameter(String name) {return this.getServletConfig().getInitParameter(name);}public Enumeration<String> getInitParameterNames() {return this.getServletConfig().getInitParameterNames();}public ServletConfig getServletConfig() {return this.config;}public ServletContext getServletContext() {return this.getServletConfig().getServletContext();}public String getServletInfo() {return "";}public void init(ServletConfig config) throws ServletException {this.config = config;this.init();}public void init() throws ServletException {}public void log(String message) {ServletContext var10000 = this.getServletContext();String var10001 = this.getServletName();var10000.log(var10001 + ": " + message);}public void log(String message, Throwable t) {this.getServletContext().log(this.getServletName() + ": " + message, t);}public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;public String getServletName() {return this.config.getServletName();}
}
我们先介绍其中的一部分方法, 等下学习完ServletConfig之后把全部的方法介绍一下…


ServletConfig接口
ServletConfig接口简介
我们上面学习Servlet接口的时候, 知道有一个方法getServletConfig, 这个方法的作用就是返回一个ServletConfig对象其实也就是上面我们分析的Tomcat服务器创建传入的那个对象
- 一个
Servlet对象有且仅有唯一一个ServletConfig对象 - 保存的是该
Servlet对象在web.xml中配置的servlet标签一些信息
下面是ServletConfig接口中的常见的方法

- getInitParameter(String name): 返回一个name对应的参数值
- getInitParameterNames(): 返回一个集合保存所有的name值
- getServletContext(): 返回一个ServletContext对象
- getServletName(): 返回配置的servlet-name
测试
我们在web.xml中配置的相关信息如下
<servlet><servlet-name>user</servlet-name><servlet-class>com.qnn.servlet.UserServlet</servlet-class><!--下面配置的相关参数信息, 可以通过ServletConfig对象中的方法拿到--><init-param><param-name>userName</param-name><param-value>qiannian</param-value></init-param><init-param><param-name>account</param-name><param-value>root</param-value></init-param><init-param><param-name>password</param-name><param-value>123456</param-value></init-param></servlet>
关于我们Servlet对象实现的service()方法的内容如下
@Override// 测试一下ServletConfig对象中保存的相关信息public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {// 改变输出的方法, 并获取一个输出流对象response.setContentType("text/html");PrintWriter out = response.getWriter();// 通过Servlet接口中的方法获得一个ServletConfig对象ServletConfig servletConfig = this.getServletConfig();// 利用ServletConfig对象中的相关的方法来输出我们当前的Servlet对象在web.xml中配置的一些信息// 这里有一个小点就是, 这个集合类其中的元素不可以使用foreach来进行循环的遍历...out.print("<h3>" + servletConfig.getServletName() + "</h3>");out.print("<br/>");Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();while (initParameterNames.hasMoreElements()) {String initParameterName = initParameterNames.nextElement();out.print("<br/>");out.print("<h3>" + initParameterName + " : " + servletConfig.getInitParameter(initParameterName) + "</h3>");}}
结果如下

再谈GenericServlet抽象类
有了上面的基础, 我们想要理解GenericServlet抽象类中的内容就更容易了…具体不再说了…
相关文章:
JavaWeb-GenericServlet源码分析(适配器/模板方法)
文章目录 类直接实现Servlet接口的弊端Servlet接口的方法适配器设计模式 适配器对象的改造关于init方法的ServletConfig对象来源使用模板方法设计模式改造init方法 GenericServlet内置抽象类ServletConfig接口ServletConfig接口简介测试再谈GenericServlet抽象类 类直接实现Ser…...
微机原理与汇编语言试题四
一、单项选择 1.(单选题)()指向的内存单元的值被CPU做为指令执行。 A. DS:SI B. CS:IP C. SS:SP D. ES:DI 正确答案:B 2.(单选题)当RESET信号进入高电平状态时,将使8086的()寄存器初始化为0FFFFH A. SS B. DS C. ES D. CS 正确答案:D 3.(单选题)堆栈段寄存器是( …...
[java基础-JVM篇]1_JVM自动内存管理
JVM内存管理涉及但不限于类加载、对象分配、垃圾回收等,本篇主要记录运行时数据区域与对象相关内容。 内容主要来源《深入理解Java虚拟机:JVM高级特性与最佳实践》与官方文档,理解与表述错漏之处恳请各位大佬指正。 目录 运行时数据区域 栈 栈…...
安宝特科技 | Vuzix Z100智能眼镜+AugmentOS:重新定义AI可穿戴设备的未来——从操作系统到硬件生态,如何掀起无感智能革命?
一、AugmentOS:AI可穿戴的“操作系统革命” 2025年2月3日,Vuzix与AI人机交互团队Mentra联合推出的AugmentOS,被业内视为智能眼镜领域的“iOS时刻”。这款全球首个专为智能眼镜设计的通用操作系统,通过三大突破重新定义了AI可穿戴…...
Unity FBXExport导出的FBX无法在Blender打开
将FBX转换为obj: Convert 3D models online - free and secure...
UE5销毁Actor,移动Actor,简单的空气墙的制作
1.销毁Actor 1.Actor中存在Destory()函数和Destoryed()函数 Destory()函数是成员函数,它会立即标记 Actor 为销毁状态,并且会从场景中移除该 Actor。它会触发生命周期中的销毁过程,调用 Destroy() 后,Actor 立即进入销毁过程。具体…...
【python】提取word\pdf格式内容到txt文件
一、使用pdfminer提取 import os import re from pdfminer.high_level import extract_text import docx2txt import jiebadef read_pdf(file_path):"""读取 PDF 文件内容:param file_path: PDF 文件路径:return: 文件内容文本"""try:text ext…...
002简单MaterialApp主题和Scaffold脚手架
002最简单的MaterialApp主题和Scaffold脚手架使用导航栏_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1RZ421p7BL?spm_id_from333.788.videopod.episodes&vd_source68aea1c1d33b45ca3285a52d4ef7365f&p1501.MaterialApp纯净的 /*MaterialApp 是主题,自带方向设…...
jdk21下载、安装(Windows、Linux、macOS)
Windows 系统 1. 下载安装 访问 Oracle 官方 JDK 下载页面 或 OpenJDK 下载页面,根据自己的系统选择合适的 Windows 版本进行下载(通常选择 .msi 安装包)。 2. 配置环境变量 右键点击 “此电脑”,选择 “属性”。 在左侧导航栏…...
Baklib知识中台引领服务智能化
智能中枢系统架构解析 Baklib 知识中台的智能中枢系统采用分层解耦设计,通过数据接入层、知识处理层与服务输出层的三级架构实现全链路智能化管理。在数据接入层,系统支持多源异构数据的实时采集与标准化清洗,涵盖结构化数据(如客…...
Spring源码分析の循环依赖
文章目录 前言一、循环依赖问题二、循环依赖的解决三、整体流程分析 前言 常见的可能存在循环依赖的情况如下: 两个bean中互相持有对方作为自己的属性。 类似于: 两个bean中互相持有对方作为自己的属性,且在构造时就需要传入:…...
检查SSH安全配置-关于“MaxStartups参数”
官方文档介绍 在《检查SSH安全配置-sshd服务端未认证连接最大并发量配置》中我们简略地阐述了“MaxStartups参数”在SSH安全配置中的意义。但是,并未对该参数做详细说明。 为啥没有详细说明呢?因为俺也没弄明白! 我们先看一下sshd_config的…...
某查”平台请求头反爬技术解析与应对
一、请求头反爬技术概述 请求头(HTTP Header)是 HTTP 协议中用于在客户端和服务器之间传递信息的一部分。它包含了请求的来源、用户代理、内容类型等关键信息。许多网站通过检查请求头中的特定字段来判断请求是否来自合法的浏览器,从而防止爬…...
MOE结构解读和deepseek的MoE结构
不管dense还是MoE(Mixture of Experts)都是基于transformer的。 下面回顾下解码器块的主要架构: 注意力机制-层归一化&残差连接-FFN前馈神经网络-层归一化&残差连接。 dense模型是沿用了这个一架构,将post-norm换为pre-no…...
LLM+多智能体协作:基于CrewAI与DeepSeek的邮件自动化实践
文章目录 引言理解 Flows(工作流)与 Crews(协作组)一、环境准备与工具安装1.1 Python环境搭建1.2 创建并激活虚拟环境1.3 安装核心依赖库(crewai、litellm) 二、本地DeepSeek R1大模型部署2.1 Ollama框架安…...
基于C++“简单且有效”的“数据库连接池”
前言 数据库连接池在开发中应该是很常用的一个组件,他可以很好的节省连接数据库的时间开销;本文基使用C实现了一个简单的数据库连接池,代码量只有400行只有,但是压力测试效果很好;欢迎收藏 关注,本人将会…...
为什么要将PDF转换为CSV?CSV是Excel吗?
在企业和数据管理的日常工作中,PDF文件和CSV文件承担着各自的任务。PDF通常用于传输和展示静态的文档,而CSV因其简洁、易操作的特性,广泛应用于数据存储和交换。如果需要从PDF中提取、分析或处理数据,转换为CSV格式可能是一个高效…...
Redis 集群的三种模式:一主一从、一主多从和多主多从
本文记述了博主在学习 Redis 在大型项目下的使用方式,包括如何设置Redis主从节点,应对突发状况如何处理。在了解了Redis的集群搭建和相关的主从复制以及哨兵模式的知识以后,进而想要了解 Redis 集群如何使用,如何正确使用…...
面试题——简述Vue 3的服务器端渲染(SSR)是如何工作的?
面试题——简述Vue3的服务器端渲染(SSR)是如何工作的? 服务器端渲染(SSR)已经成为了一个热门话题。Vue 3,作为一款流行的前端框架,也提供了强大的SSR支持。那么,Vue 3的SSR究竟是如…...
2.25DFS和BFS刷题
洛谷P1101单词方阵:用sta存字符串,for找到‘y的位置,然后dfs对字符串用for进行一个一个的判断,不符合就return,下面再用for进行book标记,能执行下面的for说明上面没有return,所以说明找到&#…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
