Java-Servlet解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Java-Servlet解析

前言


从事Javaweb项目开发有一段时间了,一直不理解它是怎么一回事,后来查询资料发现这里面涉及到几个东西,分别是tomcat、JavaEE中13个规范之一的servlet、以及springMVC。于是就去学习了一下,发现这里里面都是围绕这servlet进行的操作。于是就有了今天的这个总结。


Servlet


定义


Servlet(Server  Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。

狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

关于web服务器有很多种,其中常用的有tomcat、Netty,还有微软的IIS(Internet Information Services),

本文中用到的web服务器为tomcat


内部解析


既然说到这个servlet是一个规范,一般说到规范都是接口,就好比我们在项目开发过程中,前端开发工程师和后端的开发工程师都是根据接口文档去进行开发的,双方都遵守这个接口文档,那么开发过程就会减少很多不必要的麻烦。所以下图就是Servlet在Java中的结构展示。


package javax.servlet;
public interface Servlet {
    void init(javax.servlet.ServletConfig servletConfig) throws javax.servlet.ServletException;
    javax.servlet.ServletConfig getServletConfig();
    void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws javax.servlet.ServletException, java.io.IOException;
    java.lang.String getServletInfo();
    void destroy();
}

下面是servlet接口内部方法的展示一共是有5个方法


第一个init(javax.servlet.ServletConfig servletConfig)

这个方法在servlet对象初始化的时候执行,只执行一次。

第二个getServletConfig()

这个方法获取的是在web.xml中配置的参数比如:

第三个service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse)

这个service方法就不得了了,这个方法就是我们具体要用到的请求接口方法了,通俗点讲就是,在web项目中将我们的业务代码可以放到这个service方法,当我们在项目中配置好了servlet的映射以后去调用请求,tomcat调用的就是这个方法。后面会具体演示,在实际开发中现在已经不是直接去实现了servlet接口了,而是重写HttpServlet这个类中的doGet和doPost方法。

第四个getServletInfo()

当Servlet容器(如Tomcat)需要提供有关Servlet的信息时,它会调用getServletInfo()方法。通常情况下,这些信息用于管理和监视Servlet的部署和执行过程。例如,当管理员查看部署在Servlet容器中的应用程序时,可能会显示Servlet的名称、描述、版本号等信息。

第五个destroy()、

当Servlet容器(如Tomcat)需要关闭或卸载Servlet时,它会调用Servlet的destroy()方法。这通常发生在以下情况下:

服务器关闭:当服务器关闭或重新启动时,Servlet容器会通知所有正在运行的Servlet执行销毁操作,以确保它们可以正确地释放资源。

Servlet被卸载:当应用程序的Servlet被从Servlet容器中卸载时,容器会先调用destroy()方法,然后才会将Servlet从容器中移除。

在destroy()方法中,Servlet可以执行一些清理任务,如释放打开的文件、关闭数据库连接、取消注册的监听器等。这是一个用于资源清理和释放的重要时机,以确保应用程序的正常关闭和资源的正确释放。


总结servlet接口


以上这些内容是JavaEE的servlet接口中定义的方法,这些方法中init和destroy都是只会执行一次,也就是说当tomcat程序启动,servlet中的这两个方法只会被调用一次,分别是创建servlet对象和销毁servlet时,那么大家肯定会问,既然只调用一次,哪是谁调用的呢?答案就是web容器去调用的,在web项目中我们写的代码最后都是由tomcat去执行的,但是tomcat怎么知道要调用你哪些类的哪些方法呢?为了解决这个问题,就有了这个servlet这个规范了,tomcat只去关注去实现了servlet接口的这些类,其它的类它不会去调用。


实际应用的servlet


上面的小总结说了,servlet是由tomcat去调用的,也就是说只要我们自己写的代码去实现了servlet的接口,并且在web.xml中配置了,那么当tomcat启动时它就会把我们自己写的servlet去给创建对象。

但是我们在实际应用的时候自己写的servlet类并没有去直接实现的servlet而是去继承的抽象类HttpServlet,这里大家就有疑问了,问什么不直接去实现servlet接口,却要去继承一个抽象类呢?这里就需要给大家说一下咱们一开始放出来的servlet的一个结构图(类图关系)了,


GenericServlet类和HttpServlet类

GenericServlet类和HttpServlet类是Java Servlet API中的两个重要类,用于实现Servlet的基本功能。


GenericServlet类:


GenericServlet是一个抽象类,实现了Servlet接口。

它提供了Servlet的基本实现,可以被继承并用于创建自定义的Servlet。

GenericServlet适用于所有类型的协议,不仅限于HTTP协议。

它简化了Servlet的编写,通过提供默认的空实现,使得开发者只需重写感兴趣的方法即可。

GenericServlet中最重要的方法是service(),用于处理客户端请求。默认情况下,它会委托给doGet(), doPost()等方法,可以在子类中进行重写以实现特定的请求处理逻辑。

除了service()方法,GenericServlet还提供了其他有用的方法,如getServletConfig()和getServletInfo(),用于获取Servlet的配置信息和描述信息。

HttpServlet类:


HttpServlet是GenericServlet的子类,专门用于处理基于HTTP协议的请求。

它扩展了GenericServlet,提供了更方便处理HTTP请求的方法和功能。

HttpServlet通过重写doGet()、doPost()、doPut()等方法,可以根据请求类型执行相应的处理逻辑。

它还提供了一系列的钩子方法(如doHead()、doDelete()、doOptions()等),用于处理特定的HTTP请求方法。

HttpServlet还提供了一些用于处理HTTP请求的辅助方法,如获取请求参数、处理会话和Cookie等。

总结来说,GenericServlet类是一个通用的Servlet基类,适用于各种协议。而HttpServlet类是在GenericServlet基础上专门为HTTP协议提供了更方便的处理方法和功能。一般情况下,开发基于HTTP协议的Servlet时,应该使用HttpServlet类作为基类,以便更好地处理HTTP请求和提供相应的功能。

web项目中基本上都是使用的http协议,所以我们自己写的servlet类都是继承HttpServlet类的,因为这个类帮我们做了相应的http协议解析。


HttpServlet中的设计模式


既然使用http协议进行通信,那么http协议的请求是不同类型,比如post、get、put等,那这些类型请求servlet是如何区分呢?因为在servlet接口中只有一个service方法会被tomcat调用呀!所以这里就用到了设计模式中行为型的模板方法了。


首先看一下模板方法的定义

模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern), 指在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。

简单说, 模板方法模式定义一个操作中的算法的骨架,而将- -些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。


逐步解析


在HttpServlet中定义好了相应不同类型请求的方法


  • doGet,如果 servlet 支持 HTTP GET 请求
  • doPost,对于 HTTP POST 请求
  • doPut,对于 HTTP PUT 请求
  • doDelete,对于 HTTP 删除请求


只要继承了HttpServlet重写以上不同的方法就可以在有相应类型的请求时调用对应的方法。那大家肯定会想,这么多方法它是怎么知道我请求这个servlet时发出的是什么请求呢?这里就要说一下了,这个HttpServlet中已经解析了http请求,也就是说它解析了这个请求是什么类型,当它解析到这个请求是post类型时就会调用doPost方法,它是怎么调用的呢?这里就说到了模板方法的核心了,定义算法骨架。在HttpServlet中有一个方法叫做service方法,这个方法并不是实现的servlet接口的那个service方法,这两个有区别。

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) {
                    // 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);
        }
    }

所以当我们自己写的类继承了HttpServlet后去重新其中的doPost或者doGet等方法后会在HttpServlet中自定义的service方法中进行分发

可以看一下这个service方法中,有一个service方法调用,这是在调用HttpServlet中自定义的service方法进行后续的请求分发。


SpringMVC应用


说到这里就浅浅的说一下,SpringMVC的实现就是基于Servlet实现的,在配置mvc项目时用springMVC是需要在web.xml中进行配置的,大家可以看一下这个配置是什么。

再继续往下看,我们看一下SpringMVC中的核心类DispatcherServlet,可以看到,这个类其实也是实现了Servlet接口的。也就是说SpringMVC的实现是在基于Servlet开发的。

相关文章
|
9天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
34 2
|
13天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
65 6
|
20天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
5天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
6天前
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
|
11天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
21 4
|
14天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
18天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
17天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
39 2
|
17天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。

推荐镜像

更多
下一篇
无影云桌面