从事Javaweb项目开发有一段时间了,一直不理解它是怎么一回事,后来查询资料发现这里面涉及到几个东西,分别是tomcat、JavaEE中13个规范之一的servlet、以及springMVC。于是就去学习了一下,发现这里里面都是围绕这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)
getServletConfig()
service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse)
getServletInfo()
destroy()
、以上这些内容是JavaEE的servlet接口中定义的方法,这些方法中init
和destroy
都是只会执行一次,也就是说当tomcat程序启动,servlet中的这两个方法只会被调用一次,分别是创建servlet对象和销毁servlet时,那么大家肯定会问,既然只调用一次,哪是谁调用的呢?答案就是web容器去调用的
,在web项目中我们写的代码最后都是由tomcat去执行的,但是tomcat怎么知道要调用你哪些类的哪些方法呢?为了解决这个问题,就有了这个servlet这个规范了,tomcat只去关注去实现了servlet接口的这些类,其它的类它不会去调用。
上面的小总结说了,servlet是由tomcat去调用的,也就是说只要我们自己写的代码去实现了servlet的接口,并且在web.xml中配置了,那么当tomcat启动时它就会把我们自己写的servlet去给创建对象。
但是我们在实际应用的时候自己写的servlet类并没有去直接实现的servlet而是去继承的抽象类HttpServlet
,这里大家就有疑问了,问什么不直接去实现servlet接口,却要去继承一个抽象类呢?这里就需要给大家说一下咱们一开始放出来的servlet的一个结构图(类图关系)了,
GenericServlet
类和HttpServlet
类是Java Servlet API中的两个重要类,用于实现Servlet的基本功能。
GenericServlet
类:
GenericServlet
是一个抽象类,实现了Servlet
接口。GenericServlet
适用于所有类型的协议,不仅限于HTTP协议。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协议解析。
既然使用http协议进行通信,那么http协议的请求是不同类型,比如post、get、put等,那这些类型请求servlet是如何区分呢?因为在servlet接口中只有一个service方法会被tomcat调用呀!所以这里就用到了设计模式中行为型的模板方法
了。
在HttpServlet中定义好了相应不同类型请求的方法
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的实现就是基于Servlet
实现的,在配置mvc项目时用springMVC是需要在web.xml中进行配置的,大家可以看一下这个配置是什么。
再继续往下看,我们看一下SpringMVC中的核心类DispatcherServlet
,可以看到,这个类其实也是实现了Servlet
接口的。也就是说SpringMVC的实现是在基于Servlet开发的。