Servlet

来源:互联网 发布:m2数据查询 编辑:程序博客网 时间:2024/06/07 22:41

概述

Servlet是JavaWeb三大组件之一。另外两种是过滤器(Filter)和ner监听器(Listener)

Servle的作用是用来处理用户请求。当客户端发出请求后,由Tomcat去找到可以处理这一请求的Servlet来处理请求。


JavaWeb请求响应原理

当Tomcat接收到请求(http://localhost:8080/hello/oneServlet)后,会找到hello项目中的web.xml文件,然后通过oneServlet这个请求路径,查找处理这个请求的Servlet类型。这刚好与<url-pattern>/oneServlet</url-pattern>匹配,这说明存在一个可以通过这个请求的Servlet。然后再通过<url-pattern>/oneServlet</url-pattern>查找到<servlet-name>one</servlet-name>,然后再通过<servlet-name>one</servle-name>查找到<servlet-class>cn.itcast.servlet.MyOneServlet</servlet-class>。这时Tomcat已经得到了一个Servlet类名(一个字符串而已)。


Tomcat通过以上的Servlet类名去查找Servlet缓存池中是否已经存在了这个Servlet对象。
如果存在,直接获取这个Servlet实例,调用它的service()方法完成请求!如果不存在,那么Tomcat会通过反射来创建Servlet实例,并把Servlet实例存放到Servlet池中,再去调用Servlet的service方法处理请求。

Servlet接口

javax.servlet.Servlet接口中方法如下:

  • void init(ServletConfig servletConfig)
    Tomcat创建Servlet实例后,马上调用init()方法。这个方法只在创建后调用一次!用来做Servlet初始化工作!一个Servlet实例只被创建一次,所以init()方法也只被调用一次!(本方法编写对Servlet的初始化代码)
  • void service(ServletRequest request, ServletResponse response)
    Servlet实例在每次处理请求时都调用service()方法。
  • void destroy()当Tomcat要销毁实例时,会先调用destroy()方法,再销毁它。所谓销毁Servlet其实就是在Servlet缓存池中把Servlet移除!一般只有Tomcat关闭时,才会销毁Servlet实例!
  • ServletConfig getServletConfig()
    这个方法返回
    ServletConfig对象,但我们不能自己去创建ServletConfig对象,所以一般我们会在init()方法中把init()方法的参数保存起来,然后再在本方法中返回它。ServletConfig对象对应web.xml中当前Servlet实例的配置信息。
  • String getServletInfo():这个方法只是返回一个字符串,用来说明当前Servlet基本没用!

Servlet生命周期

javax.servlet.Servlet接口中,有三个方法说明了Servlet的生命周期:

  • void init(ServletConfig):
    创建后马上调用init()完成初始化;这个方法只在创建后调用一次!用来做Servlet初始化工作!一个Servlet实例只被创建一次,所以init()方法也只被调用一次!
  • void service(ServletRequest,ServletResponse):
    每次处理请求时调用service()方法;
  • void destroy():
    当Tomcat要销毁Servlet实例时,先调用destroy()方法,再销毁它。所谓销毁Servlet,其实就是在Servlet缓存池中把Servlet移除!一般只有Tomcat关闭时,才会销毁Servlet实例!

注意:Servlet的实例不由我们创建,Servlet的方法不由我们来调用,这一切都是由Tomcat来完成,也就是说由Tomcat来管理Servlet而我们只需要去编写Servlet实现类,并将其部署到web.xml文件中去!

servlet创建的时间

第一次访问时(请求到达时)创建

Tomcat服务器启动时创建
有些Servlet需要在Tomcat启动时就被创建,而不是第一次访问时被创建,可以<servlet>元素中添加子元素<load-on-startup>
<servlet><servlet-name>One</servlet-name><servlet-class>cn.it.servlet.OneServlet</servlet-class><load-on-startup>0</load-on-startup></servlet><servlet><servlet-name>Two</servlet-name><servlet-class>cn.it.servlet.TwoServlet</servlet-class><load-on-startup>1</load-on-startup></servlet>

所有添加了<load-on-startup>子元素的Servle,都会在Tomcat启动时被创建!当然,只是被创建,但没有处理请求!但我们知道在Servlet生命周期中init()方法会在创建后被调用,所以你可以在init()方法中做一些输出,查看是否在Tomcat启动时调用了它。

<load-on-startup>元素的值是一个序号,Tomcat会使用这个序号给多个Servlet排序!然后在Tomcat启动时会按这个顺序来创建Servlet实例对象


单例的Servlet

因为Servlet实例是由Tomcat来创建的,但Tomcat只会创建一个Servlet实例,所以Servlet就是单例的!这与我们自己写的单例模式不太一样。因为这种单例是通过容器来管理而实现的!

一个实例需要在同一个时间点上处理多个请求!
同步就是安全,但效率太低!
Servlet是线程不安全的!

  •  不写属性;
  •  不写可以存储数据的属性!

第一次惩罚

前面已经说过了Servlet的生命周期。当第一次访问一个Servlet时,Tomcat会创建Servlet实例,然后把Servlet实例存放到Servlet缓存池中,在第二次访问Servlet时,就不会再次创建Servlet了。这说明第一次需要创建,第一次就比较慢!!!这种第一次比较慢的情况被称之为“第一次惩罚”。


6 请求对象和响应对象(ServletRequest和ServletResponse)

Tomcat调用Servletservice()方法时,需要给service()方法传递参数,这两个参数一个是ServletRequest,另一个是ServletResponse。其实Tomcat传递的是HttpServletRequestHttpServletResponse对象。因为我们的请求都是基于HTTP协议的!

HttpServletRequestServletRequest的子接口;HttpServletResponseHttpServletResponse的子接口。

Tomcat会把所有与请求相关的数据封装到HttpServletRequest对象中。
例如请求的是
POST还是GET,所有请求头信息,以及请求参数,这些数据都封装到了HttpServletRequest对象中!!!当我们需要使用这些信息时,可以去调   用HttpServletRequest的相关方法来获取。

HttpServletRequest方法

  • String getParameter(String paramName):获取指定请求参数的值;
  • String getMethod():获取请求方法,例如GETPOST
  • String getHeader(String name):获取指定请求头的值;
  • void setCharacterEncoding(String encoding):设置请求参数编码!只对POST请求中的参数有效。当调用request.setCharacterEncoding(“utf-8”)之后,再通过getParameter()方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用getParameter()方法之前调用!

Tomcat会传递给service()方法一个HttpServletResponse对象,这个对象我们称之为响应对象。它可以完成与响应相关的工作。例如向客户端发送响应状态码,向客户端发送响应头信息,向客户端发送响应正文等等!反正只要是与响应相关的方法,你就去HttpServletResponse对象中去查找。
 void setContentType(String contentType):设置响应内部的类型,所谓响应类型,就是给客户端浏览器用来识别响应内部的类型,如果响应类型是text/html,那么浏览器就会显示它,如果响应类型为appliction/avi,那么浏览器就会弹出下载对象框。所以,响应类型是不能写错的。例如:response.setContentType(“text/html;charaset=utf-8”),这是告诉构造器,响应内部是html代码,使用的编码是UTF-8

void addHeader(String name, String value):向客户端添加响应头信息;

void sendError(int code, String errorMsg):向客户端发送状态码,以及错误消息。例如给客户端发送404response(404, “您要查找的资源不存在!”)

PrintWriter getWriter():获取response的输出流,使用该流可以向客户端响应正文内容。例如response.getWriter().print(“<h1>Hello JavaWeb!</h1>”);

7 ServletConfig

当容器初始化一个Servlet对象时,会向Servlet提供一个ServletConfig对象,Servlet通过该对象来获取初始化参数信息及ServletContext对象。

ServletConfig对象对应web.xml文件中的<servlet>元素。例如你想获取当前Servletweb.xml文件中的配置名,那么可以使用servletConfig.getServletName()方法获取!

Servlet补充


Servlet必须要有一个公有无参构造器,因为Tomcat会调用这个构造器来创建Servlet对象。

HTTP请求的Servlet


1 写一个专门处理HTTP请求的Servlet

因为现在我们的请求都是基于HTTP协议的,所以我们应该专门为HTTP请求写一个Servlet做为通用父类。

对于专注于HTTPServlet,我们需要处理以下几个问题:

service()方法的参数ServletRequestServletResponse,但因为所有的请求都是HTTP请求,所以传递给service()就去的参数其实是HttpServletRequestHttpServletResponse对象。如果子类希望使用的是HttpServletRequest,而不是ServletRequest,那么它需要自己去做强转。我们的MyHttpServlet这个通用父类可以把强转的工作完成!

public class MyHttpServlet extends GenericServlet {public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest httpReq = (HttpServletRequest)req;HttpServletResponse httpRes = (HttpServletResponse)res;service(httpReq, httpRes);}// 子类应该去覆盖这个方法,而不是上面的方法// 这个方法的参数是HTTP的public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}}

MyHttpServlet类中的service(ServletRequest,ServletResponse)方法调用了本类中自己写的方法service(HttpServletRequest,HttpServletResponse)。子类应该去覆盖后者!

3doGet()doPost()

我们知道,在HTTP中请求的方法最为常见的就是GETPOST。往往我们需要在service()方法中去判断客户端的请求方法,然后为GET写一种处理请求的方案,再为POST写一种处理请求的方法。这说明我们需要在service()方法中写if语句来做判断!为什么我们的MyHttpServlet不把这个工作完成了呢?

public class MyHttpServlet extends GenericServlet {public void service[Servlet的生命周期方法,这个方法会被Tomcat调用。而本类的这个方法会调用另一个service() 方法,本方法完成的是把参数强转成HTTP的参数。](ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest httpReq = (HttpServletRequest)req;HttpServletResponse httpRes = (HttpServletResponse)res;service(httpReq, httpRes)[调用HTTP的service()方法];}public void service[本方法会获取请求方式,然后判断请求方式是GET还是POST,如果是GET,那么调用本类的doGet()方法,如果是POST那么调用本类的doPost() 方法。子类应该去覆盖doGet()或doPost()方法](HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String method = request.getMethod();if(method.equals("GET")) {doGet(request, response);} else if(method.equals("POST")) {doPost(request, response);}}public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}}
4 子类不能覆盖这个类的service()方法

子类不能去覆盖MyHttpServlet类的service()方法!如果覆盖了这个方法,那么它就不能去调用service(HttpServletRequest,HttpServletResponse)方法了,那么也就不会通过请求方式去调用doGet()doPost()方法了。

注意,现在已经有两个service()方法了,这两个service()方法的关系一定要搞清楚!!!

doGet()doPost()service()方法的关系是怎样的也一定要搞清楚!

5HttpServlet

没错,Java中已经存在了javax.servlet.http.HttpServlet类。它与我们自己写的MyHttpServlet类很相似。打开源代码看看这个类!它比我们自己的MyHttpServlet复杂一点,但最为常用的特性都已经写在MyHttpServlet中了,如果你会用了MyHttpServlet,那么HttpServlet你也就会用了。

我们以后写的Servlet基本都是从HttpServlet开始派生!当然我们很可能让我们的Servlet更加强大,那么我们会从HttpServlet派生出来一个功能强大的通用父类:BaseServlet!!!但现在我们还不是很清楚HttpServlet缺少了什么,如果知道的话就可以自己写一个BaseServlet了!

7HttpServletdoXXX()的默认实现

HttpServlet中提供了7种不同的请求方式处理的方法:

l doGet():当请求为GET时调用这个方法;

l doPost():当请求为POST时调用这个方法;

l doPut():当请求为PUT时调用这个方法;

l doDelete():当请求为DELETE时调用这个方法;

l doOptions():当请求为OPTIONS时调用这个方法;

l doTrace():当请求为TRACE时调用这个方法;

l doHead():当请求为HEAD时调用这个方法。

8HTTP状态码

HTTP的状态码我们也需要了解一下!

我们现在已经知道的状态码有:200206404416500

下面深入了解一下HTTP状态码:

1XX:信息类(Information),表示收到Web浏览器请求,正在进一步的处理中;

2XX:成功类(Successful),表示用户请求被正确接收,理解和处理例如:200 OK

3XX:重定向类(Redirection),表示请求没有成功,客户必须采取进一步的动作;

4XX:客户端错误(Client Error),表示客户端提交的请求有错误。 例如:404 Not Found,意味着请求中所引用的文档不存在;

5XX:服务器错误(Server Error)表示服务器不能完成对请求的处理:如 500

<servlet><servlet-name>One</servlet-name><servlet-class>cn.it.servlet.OneServlet</servlet-class><load-on-startup>0</load-on-startup></servlet><servlet><servlet-name>Two</servlet-name><servlet-class>cn.it.servlet.TwoServlet</servlet-class><load-on-startup>1</load-on-startup></servlet>

所有添加了<load-on-startup>子元素的Servle,都会在Tomcat启动时被创建!当然,只是被创建,但没有处理请求!但我们知道在Servlet生命周期中init()方法会在创建后被调用,所以你可以在init()方法中做一些输出,查看是否在Tomcat启动时调用了它。

<load-on-startup>元素的值是一个序号,Tomcat会使用这个序号给多个Servlet排序!然后在Tomcat启动时会按这个顺序来创建Servlet实例对象!

0 0
原创粉丝点击