博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Servlet
阅读量:5846 次
发布时间:2019-06-18

本文共 9073 字,大约阅读时间需要 30 分钟。

 

Servlet

  Servlet是一个运行在Web服务器中的一个Java小程序,它能够接受和处理客户端的请求,并完成对客户端的响应。Servlet是Web应用程序中的核心类,它可以直接处理请求,也可以将请求委托给应用程序中的别的部分进行处理(请求转发、包含),所有的web服务器都内建了一个或者多个servlet,用来处理JSP、HTML页面、图片等等,所以说所有的请求都是要基于servlet的,即使在请求JSP也需要servlet的处理才能够完成,一般我们不需要编写处理JSP、HTML的servlet,因为web容器已经帮我们完成了这个任务。

  可以在tomcat的conf目录下的web.xml文件中查看,有关处理JSP的servlet的配置,下面这段配置是tomcat默认配置(这里省略了一些servlet的配置),这个文件相当于写在了每一个使用tomcat做容器的web应用程序之中, org.apache.jasper.servlet.JspServlet这个类会处理所有访问JSP的请求,并且这个servlet在服务器启动时,就会被初始化.

1   
2 3   
jsp
4 5   
org.apache.jasper.servlet.JspServlet
6 7   
3
8 9   
10 11 12 13   
14 15   
jsp
16 17   
*.jsp
18 19   
*.jspx
20 21   

 

  还有一个默认的servlet,这个servle在服务器启动时就会被创建,如果没有任何servlet处理请求,那么这个servlet会处理,这个servlet匹配一切路径,这个servlet的service方法会给客户端发送状态码404

1   
2 3   
default
4 5   
org.apache.catalina.servlets.DefaultServlet
6 7   
1
8 9   
10 11 12 13   
14 15   
default
16 17   
/
18 19   

 

 

所有的Servlet都实现了javax.servlet.Servlet接口,接口中有如下五个方法:

1 public class HelloServlet implements Servlet { 2     private ServletConfig servletConfig; 3     @Override 4     public void init(ServletConfig servletConfig) throws ServletException { 5         this.servletConfig = servletConfig; 6         System.out.println("init()..."); 7     } 8     @Override 9     public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {10          System.out.println("service()..,.");11     }12     @Override13     public void destroy() {14         System.out.println("destroy()..");15     }16     @Override17     public ServletConfig getServletConfig() {18         return this.servletConfig;19     }20     @Override21     public String getServletInfo() {22         return "";23     }24 }

 

其中有三个方法是servlet的生命周期方法

  • init(ServletConfig config)

    • 这个方法会在servlet被创建之后立即被调用,只会调用一次

    • 服务器会给这个方法传递参数,即 ServletConfig的一个实例

  • service(ServletRequest request, ServletResponse response)

    • 每次处理请求都是调用这个方法,

    • 参数也是由服务器传递

  • destroy();

    • servlet在被销毁之前会调用这个方法

当然上面的这些方法都不是由我们来调用,都是服务器在调用,我们也不会创建servlet对象,只编写servlet,servlet实例也由服务器创建。

GenericServlet

  GenericServlet实现了javax.servlet.Servlet和javax.servlet.ServletConfig接口,我们可以来模仿一下GenericServlet类的实现,重写这两个接口中的方法,其中init()方法中的参数是ServletConfig,我们可以在类中保存这个这个参数,然后使用服务器传递过来的ServletConfig完成ServletConfig接口中的方法,

 

1 import java.io.IOException; 2 import java.util.Enumeration; 3  4 public abstract class MyGenericServlet implements Servlet, ServletConfig { 5     private ServletConfig servletConfig; 6  7     @Override 8     public void init(ServletConfig servletConfig) throws ServletException { 9         this.init();10         this.servletConfig = servletConfig;11     }12 13     public void init() {14         // 在这里去做一些初始化操作15     }16 17     @Override18     public ServletConfig getServletConfig() {19         return this.servletConfig;20     }21 22     @Override23     public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws24             ServletException, IOException;25 26     @Override27     public String getServletInfo() {28         return null;29     }30 31     @Override32     public void destroy() {33         System.out.println("destroy()");34     }35 36     @Override37     public String getServletName() {38         return this.getServletConfig().getServletName();39     }40 41     @Override42     public ServletContext getServletContext() {43         return this.getServletConfig().getServletContext();44     }45 46     @Override47     public String getInitParameter(String s) {48         return this.getServletConfig().getInitParameter(s);49     }50 51     @Override52     public Enumeration
getInitParameterNames() {53 return this.getServletConfig().getInitParameterNames();54 }55 }

 

需要注意的是我们在类中重载了一个init()方法,这个方法是无参数的,并且在有参的init方法中调用了这个方法。

为什么要添加这个方法?假设一个场景,我们继承了GenericServlet类重写了service方法,但是我们还想做一些初始化操作,于是我们重写了有参的init()方法,注意这里就出问题了,我们重写了init方法,但是在父类中的init方法中将ServletConfig保存了下来,现在一重写,我们的私有成员变量servletConfig将的得不到赋值,此时如果我们调用getServletContxt()等等方法都会出错,所以重载了一个无参的init方法就防止了此类错误的发生,我们应该重写无参的init方法,来做一些初始化操作,

 

HttpServlet

  前面的Servlet接口,抽象类GenericServlet都是与具体的应用层协议无关的类,我们在编写web应用程序时,一般都是使用与Http协议相关的这个类HttpServlet,这个类继承于GenericServlet,它重写了GenericServlet中的唯一的抽象方法service(),并且在类中重载了一个service(HttpServletRequest request , HttpServletResponse response)方法,注意这个方法的的两个参数也是与Http协议相关的, HttpServletRequest继承于ServletRequest, HttpServletResponse继承于ServletResponse

  需要注意的是重载的这个方法虽然名字与service(ServletRequest request, ServletResponse response) 方法一样,但是重载的方法不是生命周期方法,也就是说服务器在处理请求时,是不会调用重载的service方法的,只会调用service(ServletRequest request, ServletResponse response) 这个生命周期方法,我们看HttpServlet的原码可以发现:

1 public void service(ServletRequest req,ServletResponse res)throws ServletException,IOException{ 2         HttpServletRequest request; 3         HttpServletResponse response; 4         try{ 5             request=(HttpServletRequest)req; 6             response=(HttpServletResponse)res; 7         }catch(ClassCastException var6){ 8             throw new ServletException("non-HTTP request or response"); 9         }10         this.service(request,response);11 }

 

  在生命周期service方法中,声明Http类型的request和response,然后进行了强转,并调用了重载的service方法,这里强转能够成功说明服务器给service方法传递的实际还是HttpServletRequest和HttpServletResponse,不然不会强转成功。我们再来看重载的service方法,

1 protected void service(HttpServletRequest req,HttpServletResponse resp)throws ServletException,IOException{ 2         String method=req.getMethod(); 3         long lastModified; 4         if(method.equals("GET")){ 5  6         }else if(method.equals("HEAD")){ 7           lastModified=this.getLastModified(req); 8           this.maybeSetLastModified(resp,lastModified); 9           this.doHead(req,resp);10         }else if(method.equals("POST")){11           this.doPost(req,resp);12         }else if(method.equals("PUT")){13           this.doPut(req,resp);14         }else if(method.equals("DELETE")){15           this.doDelete(req,resp);16         }else if(method.equals("OPTIONS")){17           this.doOptions(req,resp);18         }else if(method.equals("TRACE")){19           this.doTrace(req,resp);20         }else{21           String errMsg=lStrings.getString("http.method_not_implemented");22           Object[]errArgs=new Object[]{method};23           errMsg=MessageFormat.format(errMsg,errArgs);24           resp.sendError(501,errMsg); 25 }

 

在重载的service方法中,首先会获取请求的方式,这与Http协议有关,所谓的请求方法指的是Http请求报文中的请求首行第一个字段所表示的方法,Http协议规定的请求方法有:

  • GET

  • 请求读取由URL所标志的信息(资源),一般没有请求体

  • POST

    • 给服务器添加信息,通常用在表单的提交方式中,这种方法会产生请求体

  • OPTION

    • 请求一些选项信息,比如支持的HTTP方法,

  • HEAD

    • 与GET方法类似,不过该请求只会返回页面的头部数据

  • PUT

    • 在指明的URL下存储一个文档

  • DELETE

    • 删除由URL所标识的资源

  • TRACE

    • 用于进行环回检测的请报文的请求方法,通常用于诊断的目的

  • CONNECT

    • 由于代理服务器,HttpServlet中没有这个请求方式对应的方法,所以如果客户端使用这个方法请求服务器将会得到一个501的状态码

上述的方法除了CONNECT,其余的方法在HttpServlet都有相应的处理方法,名字都差不多,doXXX,一系列方法,对应了相应的请求方法。

service方法在获取到请求方法之后,就调用相应的doXXX方法来完成响应,所以我们应该重写一系列的doXXX方法,而不应该重写service方法,这才是最简单的方式。

再来看一下在HttpServlet中的doXXX一系列方法的默认实现,这些方法都不是抽象方法,都是有默认实现的,

1 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {2     String protocol = req.getProtocol();3     String msg = lStrings.getString("http.method_get_not_supported");4     if (protocol.endsWith("1.1")) {5         resp.sendError(405, msg);6     } else {7         resp.sendError(400, msg);8     }9 }

 

上面是doGet方法的源码,实现很简单,先获取协议名,若是http1.1版本发送状态码405,并附带一个信息”http.method_get_not_supported”,若是其他版本如http1.0发送状态码400,可见如果我们不重写doGet等一系列方法将会得到一个405或者400的错误页面

但是等等,我们如何使用浏览器访问servlet?这是一个重要的问题。

在web.xml中配置servlet

  前面说到了我们可以重写HttpServlet的一系列doXXX方法来完成对客户端请求的响应,但是忘记了一个重要的问题,在浏览器的地址栏输入什么来访问servlet?这么多servlet,如何访问特定的一个servlet我们可以在web.xml中对servlet进行配置,每个web项目的WEB-INF都有一个web.xml文件,我们在文件中添加如下配置:

  

1 
2 3   
HelloServlet
4 5   
yu.servlet.HelloServlet
6 7   
1
8 9   
10 11 12 13   
14 15   
HelloServlet
16 17   
/HelloServlet
18 19   

 

  分别进行解释,这段配置分为了两个部分,一个servlet标签,一个 servlet-mapping标签,一个servlet对应web.xml中的这两段配置:

  servlet标签中:

  <servlet-name>HelloServlet</servlet-name>,这段配置是表示给servlet取个名字 <servlet-class>yu.servlet.HelloServlet</servlet-class> ,指定servlet的类名(带包名)

  <load-on-startup>1</load-on-startup> ,在服务器启动时就创建这个servlet的实例,如果没有这段配置,那么服务器会在第一次收到请求这个servlet的请求时,才会创建这个类的实例

  servlet-mapping标签中:

  <servlet-name>HelloServlet</servlet-name>,必须与servlet标签中配置的name一致,

  <url-pattern>/HelloServlet</url-pattern>,这个就是我们在浏览器中可以输入的标识,可以为一个servlet配置多个 url-pattern

  这段配置将一个servlet类与一个 url-pattern关联起来,客户端在发出请求时,服务器将解析请求报文中请求首行的URL,若发现与某个servlet的 url-pattern一致,就调用该servlet的service方法完成响应,服务器可以解析xml文件找出匹配的servlet的配置,在通过配置中的 servlet-class反射创建对象,并调用其service方法。

  另外一个配置的servlet的方式是使用注解,这种方式比较简单,但是有一定的缺陷,比如无法控制过滤器的执行顺序?可以查看注解类,查看可以配置的信息,

  @WebServlet(name = "AServlet",urlPatterns = {"/AServlet"})

转载于:https://www.cnblogs.com/yusiming/p/9748332.html

你可能感兴趣的文章
python进阶08 MySQL基础补充
查看>>
一篇文章学会页面传值的10种方法(下)
查看>>
查看war包编译时使用的jdk版本
查看>>
控制kvm-qcow2增长空间-(一)
查看>>
Network | CIDR
查看>>
delphi中AnsiString、WideString区别
查看>>
七款使用命令行的PNG图像处理工具
查看>>
docker学习笔记(一)
查看>>
Linux入门
查看>>
开篇——Latex效果测试
查看>>
BZOJ2795&2890&3647[Poi2012]A Horrible Poem——hash
查看>>
Android Activity 生命周期 图+代码
查看>>
jQuery.Validate验证库详解
查看>>
调用DLL中的过程和函数
查看>>
JavaScript简介
查看>>
在Android Studio中修改应用包名
查看>>
abcdocker 的博客
查看>>
vs2010 安装记
查看>>
使用介质设备安装 AIX 以通过 HMC 安装分区
查看>>
web-----------HTTP协议
查看>>