Servlet是啥?Servlet是Sun公司提供的一门用于开发动态web资源的技术,按照一种约定俗成的称呼习惯,通常我们也把实现了Servlet接口的java程序,称之为Servlet。但是实际上,真正意义上的Servlet只是一个接口而已,在javax.servlet包中,并没有实现。我们来看看Servlet所在的层级。
很显然,Servlet位于Service之前,HTTP服务器之后,主要的工作就是处理HTTP请求(注意:不是接收HTTP请求!),然后调用对应的Service进行业务逻辑处理,再将处理结果返回。这就是Servlet的处理过程的概括,接下来我们来详细讲讲他的具体过程。
在讲工作流程之前,我们先来看看Servlet接口中定义了哪些方法。
public interface Servlet {
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
String getServletInfo();
void destroy();
}
是的,如你所见,这个接口只有五个方法。其中init、service、destroy方法分别对应了service的三个生命周期。另外两个方法一个是获取配置,一个是获取信息。那么我们先来看看Servlet的生命周期吧。
其实Servlet的生命周期非常好理解,一共就三个,就像我们人的生命周期一样,分为出生、工作、死亡。其中最重要的当然是service部分,因为这也是Servlet实现自己价值的地方,在这里Servlet会将HTTP请求进行相应的处理,具体的处理方法在后面会讲。看完了生命周期我们再来看Servlet工作流程。
当客户请求某一个资源时,HTTP服务器会用一个HttpServletRequest对象把客户的请求信息封装起来,然后调用Servlet容器的service方法,Servlet容器拿到请求后,根据请求的URL和Servlet的映射关系,找到相应的Servlet(如果Servlet还没有被加载,就用反射机制创建这个Servlet,并调用Servlet的init方法来完成初始化)。接着,调用Servlet的service方法来处理请求,把HttpServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。
由此可见,Servlet所做的工作其实非常简单,下面我给出一张时间轴图,更加清晰的展现工作流程和各个对象的实例化的时间。这张图请大家一定要熟记!!!这里最需要注意的是HttpServletRequest和HttpServletResponse对象的实例化时间,他们都是在调用Servlet之前创建的(非常重要!!!后面会用到)。
你可能会觉得疑惑,图里这个HttpServlet是啥?这和Servlet有什么关系?实际上,这个HttpServlet其实是Servlet接口的一个基于HTTP协议的实现类,我们接下来会详细介绍他。不过在此之前,我们先讲讲上面这张图中包含的一个重要的东西——ServletContainer。
Servlet容器用来部署Web应用,提供对于Servlet生命周期的管理。总而言之,Servlet容器就是用来装Servlet的(通俗易懂)。对了,在讲Servlet容器之前,最好还是先讲一下Web服务器。
Web服务器使用HTTP协议来传输数据。最简单的一种情况是,用户在浏览器(中输入一个URL,然后就能获取网页。因此,服务器完成的工作就是发送网页至客户端。传输过程遵循HTTP协议,它指明了请求消息和响应消息的格式。这是传统开发模式下的流程,在前后端分离的模式下,服务器更多的是将客户端请求的数据发送给客户端,而不是发送整个网页。其实,浏览器我们每天都在接触,服务器虽然我们可能接触不到,但是他们每天都在为我们提供服务,我们对他们的功能肯定也不会陌生到哪里去。
接下来看看Servlet容器。Servlet容器也叫做Servlet引擎,是Web服务器的一部分。Servlet没有自己的main方法,不能独立运行,它必须被部署到Servlet容器中,由容器来实例化和调用 Servlet的方法,Servlet容器在Servlet的生命周期内包容和管理Servlet。那么我们为什么一定要用Servlet容器,或者说用了之后有什么好处呢?
这些术语看不懂关系不大,你只需要知道,Servlet容器的出现使我们不用再关心底层的通信和线程安全,我们可以把自己的注意力只放在处理业务逻辑上,简化开发步骤。后面我们就来介绍如何书写自己的Servlet。
首先我们先看最上层的两个接口,一个是ServletConfig接口,一个是我们熟悉的Servlet接口。ServletConfig接口主要负责Servlet的配置信息,常用来在Servlet初始化时进行信息传递。这个不算特别重要,我们初学的时候应该先把注意力放在业务逻辑上。我们再来看Servlet接口,里面就是我们熟悉的五个方法,所以,接下来我们肯定是要依次实现他们。
然后我们来看这个GenericServlet。GenericServlet是个抽象类,所以他无法构造Servlet对象,必须子类实现里面所有的抽象方法才能实例化对象。他的功能是给出了设计Servlet的一些骨架,定义了Servlet生命周期,还有一些得到名字、配置、初始化参数的方法,其设计的是和应用层协议无关的,也就是说你有可能用非HTTP协议实现它。我们只需要记住,这个GenericServlet只是一个骨架,我们还需要另外的具体实现。
接着就是我们最重要的HttpServlet类。HttpServlet是GenericServlet的子类,具有GenericServlet的一切特性,还额外添加了doGet, doPost, doDelete, doPut, doTrace等方法对应处理HTTP协议里的命令的请求响应过程,我们在开发时最重要的就是实现这些方法来处理不同的HTTP请求。我们可以说HttpServlet是基于HTTP协议的Servlet实现类,当然还有基于别的协议的Servlet实现类,因为用的不多所以我也没有去太多了解。一般没有特殊需要,我们自己写的Servlet都扩展自HttpServlet 。最底部的MyServlet就是我们自己书写的Servlet,用来实现我们自己项目的业务逻辑。下面我给出一张继承简图:
是的,大名鼎鼎的Servlet的结构就是这么简单,所以我们学东西的时候不能有畏难情绪。那些你可能完全听不懂的名词或者句子其实仔细想想可能也没有那么难吧。
讲了那么多理论知识,我们终于可以用Servlet做些事情了。因为我们还没有讲Tomcat(一种Web服务器,也是一种Servlet容器)的配置,没有Tomcat我们的Servlet是无法运行的,所以在这里还是看我演示就行。到Tomcat章节我会告诉大家如何配置Tomcat。
在讲实战之前我先来介绍一个调试工具——postman。这是一款专门用来调试API的工具,功能全面、界面美观、操作简便。我们后面调试Servlet时都靠他了,大家可以去Google一下然后安装一个。
话不多说,我们先写一个简单的Servlet:
package controller;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginController", value = "/entrance/login")
public class LoginController extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request.getMethod());
System.out.println(request.getParameter("userName") + request.getParameter("password"));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
我们来看一下这段代码干了什么事,首先我们在注解里面标明了这个Servlet的名字和对应的虚拟路径。(注意:这边只是虚拟路径,请求的参数不需要写在这里)然后我们重写了两个方法,分别是doPost和doGet,这两个方法分别处理请求到“/entrance/login”路径下的post方式请求和get方式请求。这两个方法的参数是一模一样的,都是HttpServletRequest对象和HttpServletResponse对象,这两个对象来自Tomcat中的HTTP服务器,封装了请求和响应。如果忘记了可以去看看上文中的图(Servlet执行流程)。
既然我们已经拿到了HttpServletRequest对象,那么接下来的事就简单了,我们可以调用HttpServletRequest中的各种方法,拿到请求中的任何信息。比如请求头、请求体、请求参数、请求方式、cookie等(总之无论你的请求里有啥,他都能拿得到)。在这里我演示了一下拿请求方式和请求参数。我们用postman工具请求一下他。
显然,控制台打印出了请求方式为POST,请求参数为123和456。经过了这个案例,你应该明白了Servlet是怎么拿到我们的请求中的信息的。哦对了有一点忘记说了,你一定发现了我的doGet方法中又写了一个doPost方法,这是干什么的?很简单,当用get方式请求时,这个doGet方法会调用doPost方法,相当于是将请求又转交给了doPost方法。因此,在这个案例中,你用get方式和post请求这个路径,效果是完全一样的(当然,控制台打印的请求方式肯定是不一样的)。但是,如果你用DELETE、PUT等其他的方式请求,很显然是没用的。如果你需要用到这些方式,你必须要重写doDelete、doPut等方法。下面放一张我分别用post方式和get方式请求之后控制台的结果:
这个结果是不是和你想象的一样,如果一样的话,说明你已经明白了Servlet的基本用法。至于HttpServletRequest的其他方法和HttpServletResponse中的方法,你可以通过Google或者查文档或者阅读源码的方式来学习。我会在Servlet源码分析章节中细讲。
2020年4月23日