前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Servlet的源码分析

Servlet的源码分析

作者头像
时间静止不是简史
发布2020-07-24 17:06:24
8840
发布2020-07-24 17:06:24
举报
文章被收录于专栏:Java探索之路Java探索之路

Servlet的源码

  • 一 .源码的正确打开方式
  • 二 .Servlet接口
    • 接口结构
    • 方法解析
  • 三. ServletConfig接口
    • 接口结构
    • 方法解析
    • ServletConfig 与ServletContext
  • 四. GenericServlet抽象类
    • 类结构
    • 方法解析
  • 五. 抽象类HttpServlet
    • 类结构
    • 方法解析
    • 整体总结
  • 六. 读源码需要了解的知识
    • 接口
    • 抽象类与抽象方法
    • 继承
    • 多态
    • 方法的重写/覆盖(注意区分重载)
    • 方法重载

一 .源码的正确打开方式

通过对源码的分析 , 掌握其运行原理 ,使我们能够更加熟练地理解和使用相关技术( Servlet)

  1. 新建创建一个maven项目 ,类型为war 项目
  2. 在中央仓库找找Servlet的开发规范 中央仓库https://mvnrepository.com/ 坐标如下:
代码语言:javascript
复制
<dependencies>
	<!-- servlet开发规范 -->
		<!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
  1. 创建一个servlet类 ,打开进入HttpServlet
在这里插入图片描述
在这里插入图片描述

4 .使用 ctrl+o 即可查看该类的所有属性与方法 注 : 每种图标代表不同的方法 ,像是第一个红色方框加SF 代表 private 类型的最终静态常量 ,其他类型如图所示

在这里插入图片描述
在这里插入图片描述
  1. Servlet的总结构图如下
在这里插入图片描述
在这里插入图片描述

二 .Servlet接口

1 .在HttpServlet中, ctrl + t 打开其继承结构 ,点击其父类

在这里插入图片描述
在这里插入图片描述
  1. 进入Servlet接口
在这里插入图片描述
在这里插入图片描述

接口结构

  1. 在接口中查看该接口所定义的方法 绿色圆圈右上为A代表 ,该方法是public修饰接口类
在这里插入图片描述
在这里插入图片描述

方法解析

代码语言:javascript
复制
public interface Servlet {

//负责初始化Servlet 对象。容器一旦创建好Servlet 对象后,就调用此方法来初始化Servlet 对象
public void init(ServletConfig config) throws ServletException;

//负责处理客户的请求并返回响应。当容器接收到客户端要求访问特定的servlet 请求时,就会调用Servlet 的service 方法
public void service(ServletRequest req, ServletResponse res) throws ServletException,
IOException;

//Destroy()方法负责释放Servlet 对象占用的资源,当servlet 对象结束生命周期时,servlet 容器调用此方法来销毁servlet 对象.
public void destroy();

//说明:Init(),service(),destroy() 这三个方法是Servlet 生命周期中的最重要的三个方法。
//返回一个字符串,在该字符串中包含servlet 的创建者,版本和版权等信息
public String getServletInfo();

//GetServletConfig: 返回一个ServletConfig 对象,该对象中包含了Servlet 初始化参数信息
public ServletConfig getServletConfig();
}

注 : init 方法接收一个ServletConfig 参数, 由容器传入.ServletConfig 就是Servlet 的配置,在 web.xml 中定义Servlet 时通过init-param 标签配置的参数由ServletConfig 保存

三. ServletConfig接口

接口结构

在这里插入图片描述
在这里插入图片描述

方法解析

代码语言:javascript
复制
public interface ServletConfig {

//用于获取Servlet 名,web.xml 中定义的servlet-name
String getServletName();


//获取Servlet 上下文对象(非常重要)
ServletContext getServletContext();


//获取init-param 中的配置参数
String getInitParameter(String var1);


//获取配置的所有init-param 名字集合
Enumeration<String> getInitParameterNames();
}

注 : ServletConfig 是Servlet 级别,而ServletContext 是全局的

ServletConfig 与ServletContext

代码语言:javascript
复制
/**
	 *ServletConfig 获取的是局部变量的方法
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		String value1 = config.getInitParameter("key1");
		
	}
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
	/**
	 *ServletConfig 获取的是局部变量的方法
	 *getServletContext 获取的是全局的变量
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		String value1 = config.getInitParameter("key1");
		String value= config.getServletContext().getInitParameter("key");
	}
在这里插入图片描述
在这里插入图片描述

四. GenericServlet抽象类

GenericServlet类实现了Servlet接口和ServletConfig接口,GenericServlet类的主要身份是Servlet,此外,它还运用装饰设计模式,为自己附加了ServletConfig装饰身份( private transient ServletConfig config;装饰者设计模式参见《设计模式之装饰者模式》)。在具体实现中,GenericServlet类包装了一个ServletConfig接口的实例,通过该实例来实现ServletConfig接口中的方法。

类结构

在这里插入图片描述
在这里插入图片描述

方法解析

代码语言:javascript
复制
// 抽象类GenericServlet 实现了Servlet 接口的同时, 也实现了ServletConfig 接口和Serializable 这两个接口
public abstract class GenericServlet
implements Servlet, ServletConfig, java.io.Serializable
{


//私有变量,保存init()传入的ServletConfig 对象的引用
private transient ServletConfig config;


//无参的构造方法
public GenericServlet() { }


------------------------------------
以下方法实现了servlet 接口中的5 个方法
实现Servlet 接口方法开始
------------------------------------
/*
实现接口Servlet 中的带参数的init(ServletConfig Config) 方法, 将传递的
ServletConfig 对象的引用保存到私有成员变量中,
使得GenericServlet 对象和一个ServletConfig 对象关联.
同时它也调用了自身的不带参数的init()方法
**/
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init(); //调用了无参的init()方法
}


//无参的init()方法
public void init() throws ServletException {
}


//空实现了destroy 方法
public void destroy() { }
//实现了接口中的getServletConfig 方法,返回ServletConfig 对象
public ServletConfig getServletConfig()
{
return config;
}



//该方法实现接口<Servlet>中的ServletInfo,默认返回空字符串
public String getServletInfo() {
return "";
}


//唯一没有实现的抽象方法service(),仅仅在此声明。交由子类去实现具体的应用
//在后来的HttpServlet 抽象类中,针对当前基于Http 协议的Web 开发,HttpServlet抽象类具体实现了这个方法
//若有其他的协议,直接继承本类后实现相关协议即可,具有很强的扩展性
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException,IOException;
------------------------------------
实现Servlet 接口方法结束
------------------------------------


---------------------------------------------
以下四个方法实现了接口ServletConfig 中的方法
实现ServletConfig 接口开始
---------------------------------------------
// 该方法实现了接口<ServletConfig> 中的getServletContext 方法, 用于返回
servleConfig 对象中所包含的servletContext 方法
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}


//获取初始化参数
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}


//实现了接口<ServletConfig>中的方法,用于返回在web.xml 文件中
//为servlet 所配置的全部的初始化参数的值
public Enumeration getInitParameterNames() {
return getServletConfig().getInitParameterNames();


//获取在web.xml 文件中注册的当前的这个servlet 名称。没有在web.xml 中注册的
//servlet,该方法直接放回该servlet 的类名。
//法实现了接口<ServleConfig>中的getServletName 方法
public String getServletName() {
return config.getServletName();
}


---------------------------------------------
实现ServletConfig 接口结束
---------------------------------------------
public void log(String msg) {
getServletContext().log(getServletName() + ": "+ msg);
}
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
}

五. 抽象类HttpServlet

设计成抽象类的原因是不允许他被实例化 因为它处理的是http 的请求, 至于处理什么样的请求 ,如何处理请求需要由我们自己编写的Servlet 来设计

类结构

在这里插入图片描述
在这里插入图片描述

方法解析

代码语言:javascript
复制
public abstract class HttpServlet extends GenericServlet
implements java.io.Serializable
{
private static final String METHOD_GET = "GET";
private static final String METHOD_POST = "POST";
......
/**
* Does nothing, because this is an abstract class.
* 抽象类HttpServlet 有一个构造函数,但是空的,什么都没有
*/
public HttpServlet() { }


/*分别执行doGet,doPost,doOpitions,doHead,doPut,doTrace 方法
在请求响应服务方法service()中,根据请求类型,分贝调用这些doXXXX 方法
所以自己写的Servlet 只需要根据请求类型覆盖响应的doXXX 方法即可。
*/
//doXXXX 方法开始
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}


protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
.......
}


protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//todo
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//todo
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//todo
}
protected void doDelete(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
//todo
}


//doXXXX 方法结束
//重载的service(args0,args1)方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {


// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}


//实现父类的service(ServletRequest req,ServletResponse res)方法
// 通过参数的向下转型, 然后调用重载的
service(HttpservletRequest,HttpServletResponse)方法
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req; //向下转型
response = (HttpServletResponse) res; //参数向下转型
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response); //调用重载的service()方法
}
......//其他方法
}

注 : HttpServlet 是基于Http 协议实现的Servlet 基类,我们在写Servlet 的时候直接继承它就行 了.SpringMVC 中的DispatchServlet 就是继承了HttpServlet.HttpServlet 重写了service 方法, 而service 方法首先将ServletRequest 和ServletResponse 转成HttpServletRequest 和 HttpServletResponse,然后根据Http 不同类型的请求,再路由到不同的处理方法进行处理

整体总结

Web 容器加载Servlet 并将其实例化后,Servlet 生命周期开始,容器运行其init()方法进行Servlet 的初始化;请求到达时调用Servlet 的service 方法,service 方法会调用与请求对应的doGet 或doPost 等方法; 当服务器关闭会项目被卸载时服务器会将Servlet 实例销毁,此时会调用Servlet 的destroy 方法。

六. 读源码需要了解的知识

接口

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须能干掉坏人;如果你是坏人,则必须欺负好人。接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。 面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

抽象类与抽象方法

抽象方法

使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

抽象类

包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。

普通类 , 抽象类和接口区别

  1. 普通类:具体实现
  2. 抽象类:具体实现,规范(抽象方法)
  3. 接口:规范!

继承

继承让我们更加容易实现类的扩展 , 存在继承关系的两个类就是父类和子类。而且子类实现父类的方法或接口不是必须的 从英文字面意思理解,extends的意思是“扩展”。子类是父类的扩展。现实世界中的继承无处不在。比如,我们定义了人类,再定义Boy类就只需要扩展人类即可。实现了代码的重用,不用再重新发明轮子(don’t reinvent wheels)。java中普通的类只能单继承 ,但是接口可以多继承 ,中间用逗号隔开

多态

多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人的“休息”方法,张三是睡觉,李四是旅游,gg老师是敲代码,数学教授是做数学题; 同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。

多态的要点: 1. 多态是方法的多态,不是属性的多态(多态与属性无关)。 2. 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。 3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

方法的重写/覆盖(注意区分重载)

子类通过重写父类的方法,可以用自身的行为替换父类的行为。方法的重写是实现多态的必要条件。 发生在继承类中(方法名、形参列表相同)

方法的重写需要符合下面的三个要点:

1.“==”: 方法名、形参列表相同。

2.“≤”:返回值类型和声明异常类型,子类小于等于父类。

3.“≥”: 访问权限,子类大于等于父类。

代码语言:javascript
复制
public class TestOverride {
    public static void main(String[] args) {
        Vehicle v1 = new Vehicle();
        Vehicle v2 = new Horse();
        Vehicle v3 = new Plane();
        v1.run();
        v2.run();
        v3.run();
        v2.stop();
        v3.stop();
    }
}
 
class Vehicle { // 交通工具类
    public void run() {
        System.out.println("跑....");
    }
    public void stop() {
        System.out.println("停止不动");
    }
}
class Horse extends Vehicle { // 马也是交通工具
    public void run() { // 重写父类方法
        System.out.println("四蹄翻飞,嘚嘚嘚...");
    }
}
 
class Plane extends Vehicle {
    public void run() { // 重写父类方法
        System.out.println("天上飞!");
    }
    public void stop() {
        System.out.println("空中不能停,坠毁了!");
    }
}  
在这里插入图片描述
在这里插入图片描述

方法重载

在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。发生在继承类中

格式特点:

  1. 方法名相同。
  2. 方法的参数表必须不同 如果参数个数不同,就不管它的参数类型了 如果参数个数相同,那么参数的类型必须不同
  3. 方法的返回类型、修饰符可以相同,也可不同
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Servlet的源码
  • 一 .源码的正确打开方式
  • 二 .Servlet接口
    • 接口结构
      • 方法解析
      • 三. ServletConfig接口
        • 接口结构
          • 方法解析
            • ServletConfig 与ServletContext
            • 四. GenericServlet抽象类
              • 类结构
                • 方法解析
                • 五. 抽象类HttpServlet
                  • 类结构
                    • 方法解析
                      • 整体总结
                      • 六. 读源码需要了解的知识
                        • 接口
                          • 抽象类与抽象方法
                            • 继承
                              • 多态
                                • 方法的重写/覆盖(注意区分重载)
                                  • 方法重载
                                  相关产品与服务
                                  容器服务
                                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                  领券
                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档