一文精通Servlet
题记
Servlet是一种Web组件,他与平台无关,属于一种中间件。运行在Tomcat服务器/Java程序/Servlet容器之中。负责与客户端进行操作。
Servlet本身就是一组接口。这里注意接口的特性:接口里面所有的方法都要进行重写。
Servlet有两大功能:
- 创建并且返回动态的HTML页面,也就是说在Javaweb中我们能够在客户端看到的页面是通过Servlet从服务器发送到客户端的。
- 与数据库进行通信。
本质上,Servlet就是Java包中的一组接口,也就是说想要实现Servlet的开发,只需要写出Servlet所对应每个页面的实现类即可。而这里需要注意的是传统的Servlet(也就是直接implements Servlet)是需要重写他的全部方法的,因为它本质上是一个接口,而接口里的所有方法都必须要被重写。我们一般所使用的doGet或者doPost方法,其实实现的是Servlet的另外一种封装——HttpServlet。
亲手仿写一个山寨版的HttpServlet
当一个类,实现Servlet接口之后,需要重写的方法如下:
public class DemoServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
其中,各个方法所对应的是:
-
public void init(ServletConfig servletConfig) throws ServletException:对应的是初始化的方法,不常用。
-
public ServletConfig getServletConfig():获得Servlet的配置,不常用。
-
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException:这是Servlet中最重要的方法,这也是Servlet中的主方法。所有对于逻辑上的实现,都是在这个方法中写出来的。
-
public String getServletInfo():不常用。
-
public void destroy():销毁Servlet,不常用。
(从中便可以看出,Servlet是有他自己的生命周期的。而关于这点在之后的文章中会有讲到。)
众所周知,Servlet是一个JavaWeb中的基石,可以说每一个页面甚至是一个页面中的一个操作可能都会对应一个Servlet。但是从上述介绍中能够看出,每一个Servlet比较重要(需要重写的)方法都是他的主方法——service,其他的方法相对来讲并不是非常的重要。但是由于接口的特性,每一次都需要重写Serviet中的所有方法,这显然是不够程序员的。因此,Java提供了一套Servlet层次结构,由上至下分别是:
- HttpServlet
- GenericServlet
- Servlet
这三者的关系是由上至下,上者分别是下者的进一步封装或者使用。其实我们在实际开发的过程中,我们只需要直接继承HTTP Servlet就可以。具体实现方式:
public class TestServlet extends HttpServlet(){}
但是理解里面具体的逻辑还是非常有必要地。现在就通过亲手仿写一个HTTP Servlet实现来理解这个层次结构:
GenericServlet
Servlet是最底层的实现,也就是上两种的基石,很明显,如果想要实现Servlet首先需要进行实现Servlet的所有方法。
所以GenericServlet他其实本质上就是一个中间层,用于实现其所有的接口,并且向上层HttpServlet去返回一系列的方法和接口。
因此GenericServlet源代码其实最核心的功能就是实现了Servlet接口的实现类
public class MyGenericServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
HttpServlet
HttpServlet是我们在实际开发中最常用的一个方法,一般我们在写Servlet的时候都是直接继承HttpServlet。
public class MyDemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPut(req, resp);
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doDelete(req, resp);
}
}
我们知道HTTP请求分为好几种,其中最常用的就是以下四种:
- GET
- POST
- PUT
- DELETE
这四种分别对应着数据库GRUD四种操作:读取、保存、修改、删除。
所以这也对应着HttpServlet的四种方法,同样,也对应了Servlet本身第二种功能:与数据库进行数据交互。
PUT和DELETE平时用的频率其实也并不高,最高的还是GET和POST两种。那么两种都有什么区别呢?
通俗的来讲:
- GET请求:只要通过浏览器中的url发送的数据打开的页面都是属于GET请求。
- POST请求:只有通过表单来进行跳转或者需要通过表单来发送数据的请求都是POST请求。对应前端。
所以也就是说:这两种请求在HttpServlet中是区分开的,但是Servlet中,都是进入service方法进行处理的。而HttpServlet的本质,其实就是拿到跳转的类型,并且进入Servlet的实现类GenericServlet中的Service中在进行一层业务逻辑区分。那么也就是说:GenericServlet和HttpServlet的本质区别就是:
-
GenericServlet就是实现了Servlet的实现类,自身屏蔽了Servlet其他的不常用的方法,比如init初始化等。
-
HttpServlet就是在GenericServlet对应的主要方法中对HTTP的几种请求进行了业务逻辑处理,使其进入不同的业务逻辑方法。
那么我们来自己写一个山寨版的HttpServlet:
/**
* 继承了MyGenericServlet的所有方法,所以父类中的方法在这里都不需要再去实现,只需要去重写需要的代码就可以
* 知识点:注意继承父类和实现接口两者的区别。
*/
public class MyHttpServlet extends MyGenericServlet {
// 由于是继承父类,所以只重写对自己有用的方法。
// 这样就把其中其他四个不需要的方法给屏蔽掉了,我们只需要重写这一个方法就可以了,实现了GenericServlet的本质。
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 通过强转将servletRequest类型转为HttpServletRequest类型。
/*
* 源代码中:public interface HttpServletRequest extends javax.servlet.ServletRequest
* 也就是说,ServletRequest是HttpServletRequest的父类,所以可以直接强转。
* 转类型的目的是因为父类中没有获取请求类型的方法,也就是说父类的方法具有局限性。
* */
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse;
// 获取请求类型
String method = httpServletRequest.getMethod();
// 用switch语句来做分发
switch (method){
case "GET":
// 这里有个基础知识点,为什么要用this?
/*
* this指代本类,也就是说用this就是调用的本类中的成员变量以及成员方法,而不是传入的形参或者是局部变量。
* */
this.doGet(httpServletRequest,httpServletResponse);
break;
case "POST":
this.doPost(httpServletRequest,httpServletResponse);
break;
}
}
// 自身的方法写两个
// 这里注意传入的方法是自己强转之后的HttpServlet类型的方法
// 在这里抛出子类中的异常
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException{
}
}
(注意我在代码中写的注释,里面有很多知识点)
然后我们再用我们自己写的来实现一下HttpServlet
public class HelloServlet extends MyHttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 在这里出现了报错,需要抛出异常,异常需要在父类上面也同样抛出异常
response.getWriter().write("GET");
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException{
response.getWriter().write("POST");
}
}
复盘三种Servlet的层次关系
从中我们来复盘一下三个实现类的层次关系:
- 顶层Servlet:是最基本的Servlet,属于一个接口。其实本质上Servlet就是一组接口。通过基础知识我们了解到接口所有的方法都需要进行重写和实现。而我们只需要实现其中一个service方法就可以,所以出现了GenericServlet。
- 中间层GenericServlet:就是实现了Servlet接口的一个实现类,作用就是屏蔽掉了四个不常用的Servlet生命周期的方法。继承了该类之后我们只需要重写其中最常用的service方法即可。
- 底层HttpServlet(最常用):将HTTP请求的类型进行了具体逻辑划分,使其进入不同的方法当中。也就是说我们在日常开发之中继承该方法,并且重写该方法的doGET或者doPOST等方法即可。
写在最后
如果该文章对大家有帮助的话,请大家点个赞或者留个评论。我是苏木,谢谢大家捧场哈~