前言
从事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开发的。