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,所以说明找到&#…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
