Servlet知识上篇

来源:互联网 发布:mac系统windows虚拟机 编辑:程序博客网 时间:2024/06/06 19:09

1.Servlet的时序图

      
  由上图可以看出来,一个完整的servlet的时序是:加载servlet Class文件,创建servlet对象---------->调用servlet的init()方法------>调用service()方法。

2.Servlet的生命周期的三个阶段

    Servlet生命周期具体包含三个阶段,初始化阶段、运行阶段、销毁阶段
   1)初始化阶段(init()方法只是其中的一部分)

    (1) Servlet容器加载servlet类,把servlet类的.class文件中的数据读到内存中。

    (2) 然后Servlet容器创建一个ServletConfig对象。ServletConfig对象包含了Servlet的初始化配置信息。

    (3)Servlet容器创建一个servlet对象。

    (4) Servlet容器调用servlet对象的init方法进行初始化。init方法有一个带形参的方法,如下图。说明ServletConfig对象是在调用init方法之前

      创建的。

   
2)运行阶段
  

(1)servlet容器接收到一个请求时,servlet容器会针对这个请求创建servletRequestservletResponse对象。

(2)然后调用service方法。并把这两个参数传递给service方法。Service方法通过servletRequest对象获得请求的信息。并处理该请求。再通过  servletResponse对象生成这个请求的响应结果。

 (3)Servlet接口中定义了一个service方法,HttpServlet对该方法进行了实现,实现方式就是将ServletRequestServletResponse转换为HttpServletRequestHttpServletResponse,转换完毕后,会调用HttpServlet类中自己定义的service方法,在该service方法中,首先获得到请求的方法名,然后根据方法名调用对应的doXXX方法,比如说请求方法为GET,那么就去调用doGet方法;请求方法为POST,那么就去调用doPost方法。下面代码是httpServlet的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 / 1000 * 1000)) {                    // 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会先调用service方法,然后通过解析请求时get还是post,然后再调用对应的doXxx()方法。所以顺序是先service,然后才是doXxx方法。所以,一般来说不再重写HttpServlet的service方法,只重写doGet和doPost方法。GenericServlet 是不存在doXxx方法的。如下图:


3).销毁阶段

Web应用被终止时,servlet容器会先调用servlet对象的destrory方法,然后再销毁servlet对象,同时也会销毁与servlet对象相关联的servletConfig对象。我们可以在destroy方法的实现中,释放servlet所占用的资源,如关闭数据库连接,关闭文件输入输出流等。

在这里该注意的地方:

servlet生命周期中,servlet的初始化(第一个客户端访问)和和销毁阶段(如关闭服务器)只会发生一次,而service方法执行的次数则取决于servlet被客户端访问的次数

3.Servlet、GenericServlet、HttpServlet

   Servlet是一个接口,而GenericServlet实现了servlet这个接口,并且也实现了ServletConfig, java.io.Serializable这两个接口。而HttpServlet继承了GenericServlet这个抽象类。这是三者之间的关系。HttpServlet比GenericServlet多了一些方法:如doXXX()方法

   区别是:

    (1)HttpServlet是基于http协议的,而其余两者没有特别针对哪个协议。

  (2)Servlet和GenerticServlet在执行阶段创建的是ServletRequest和ServletResponse对象,而HttpServlet创建的是          HttpServletRequest和HttpServletResponse对象。如下源代码:

<span style="font-weight: normal;"><span style="font-size:14px;"> public void service(ServletRequest req, ServletResponse res)        throws ServletException, IOException    {        HttpServletRequest        request;        HttpServletResponse        response;                try {            request = (HttpServletRequest) req;            response = (HttpServletResponse) res;        } catch (ClassCastException e) {            throw new ServletException("non-HTTP request or response");        }        service(request, response);    }}</span></span>
HttpServletRequest、 HttpServletResponse分别是ServletRequest和ServletResponse的子类 。它们比父类多了一些针对http协议的方法。如:getHeader(String name), getMethod() , getSession() 。

4.ServletContext和ServletConfig

ServletContext对象:servlet容器在启动时会加载web应用,并为每个web应用创建唯一的servlet context对象,可以把ServletContext看成是一个Web应用的服务器端组件的共享内存,在ServletContext中可以存放共享数据。ServletContext对象是真正的一个全局对象,凡是web容器中的Servlet都可以访问。
servletConfig对象:用于封装servlet的配置信息。从一个servlet被实例化后,对任何客户端在任何时候访问有效,但仅对servlet自身有效,一个servlet的ServletConfig对象不能被另一个servlet访问。
5.HttpServletRequest和HttpServletResponse
   ServletRequest常用方法:
      getRequestURL方法返回客户端发出请求时的完整URL。
  getRequestURI方法返回请求行中的资源名部分。
  getQueryString 方法返回请求行中的参数部分。
  getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
  getRemoteAddr方法返回发出请求的客户机的IP地址。
  getRemoteHost方法返回发出请求的客户机的完整主机名。
  getRemotePort方法返回客户机所使用的网络端口号。
  getLocalAddr方法返回WEB服务器的IP地址。
  getLocalName方法返回WEB服务器的主机名。

     获得客户机请求头

      getHeader(string name)方法:String  ; getHeaders(String name)方法:Enumeration 

    getParameter()getAttribute(string  name,string  value)区别

    getParameter()客户端与服务器端交互:获取客户端的表单或者url返回的是一个string对象

   getAttribute(string  name,string  value)服务器端交互先通过setAttribute()设置属性,在通过getAttribute()获取属性返回的是一个object对象

   HttpServletResponse对象

    设置响应头,发送状态码,向浏览器发送实体内容
    设置响应头的方法:
     response.setContentType("text/html;charset=utf-8");//向浏览器设发送置编码方式
     response.setContentType(“text/html“);//页面的设置文本类型

       //浏览器不缓存的三种方法

       response.setDateHeader("Expires", 0);

       response.setHeader("Cache-Control", "no-cache");

       response.setHeader("pragma","no-cache");

    发送状态码:
    response.sendError()
   向浏览器发送实体内容:

     Response.getOutputStream()  字节输出流对象

     Response.getWriter()   字符的输出流对象

 4.Servlet的多线程问题

Servlet默认是单实例多线程运行方式,的模式,如果servlet中定义的全局变量是可写的,就会发生同步问题所以Servlet的全局变量最好是只读的,局部变量可以可读可写的。单实例是指Servlet对象是在servlet初始化阶段才创建的,而初始化init()方法只进行一次,所以Servlet对象是单实例的。当有客户端请求Servlet时,Servlet容器为这个客户端创建一个线程,去执行service方法。所以针对一个单实例多线程的程序,数据是不安全的。

5.forward和redirect

request.getRequestDispatcher(/test4.jsp).forward(request,response);请求转发

response.sendRedirect("www.baidu.com");重定向

forward和redirect的区别

1.从地址栏显示来说
forward
是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.所以redirect等于客户端向服务器端发出两次request,同时也接受两次response
2.
从数据共享来说
forward:
转发页面和转发到的页面可以共享request里面的数据.假如从a页面 到 b页面 在到c页面 如果用在a页面中:

request.setAttribute("a","aa");

<jsp:forward page="b.jsp"></jsp:forward>

b页面中:<jsp:forward page="c.jsp"></jsp:forward>跳转 在c页面用request.getAttribute("a")

就可以得到a页面的值,或者用request.getParameter("name")也可以得到。

但是要用response.sendRedirect("b.jsp");跳转的话就得不到页面的值和内容。

redirect:不能共享数据.
redirect
不仅可以重定向到当前应用程序的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源.forward,方法只能在同一个Web应用程序内的资源之间转发请求.forward 是服务器内部的一种操作.redirect 是服务器通知客户端,让客户端重新发起请求.所以,你可以说 redirect 是一种间接的请求但是你不能说"一个请求是属于forward还是redirect "
3.
从运用地方来说
forward:
一般用于用户登陆的时候,根据角色转发到相应的模块.redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
4.
从效率来说
forward:
.
redirect:
.

6.解决servlet的乱码问题

乱码分为request乱码和resposne乱码(本节是看的一个网友的博客写的很好,特意搬过来,网友的博客的地址是:
http://blog.csdn.net/xiazdong/article/details/7217022)
request乱码

request请求分为post和get,对于不同的请求方式有不同的解决乱码的方案;

 

1.post请求乱码

 

错误原因:

 

解决方案:

 

2.get请求乱码

 

Response乱码

在网上很有效的解决方法是添加:

response.setCharacterEncoding("UTF-8");

解决不了,后来又搜到一条解决方法是:

respnse.setHeader("content-type","text/html;charset=UTF-8");

两句都填上,后来终于解决了这个问题;

其实我们应该思考一下本质;

  

问题1:

 

我们这里先来说明一下错误的原因,下图是显示乱码的流程图:

 

response.setContentType("text/html;charset=UTF-8"); 目的是为了控制浏览器的行为,即控制浏览器用UTF-8进行解码;

response.setCharacterEncoding("UTF-8"); 的目的是用于response.getWriter()输出的字符流的乱码问题,如果是response.getOutputStream()是不需要此种解决方案的;因为这句话的意思是为了将response对象中的数据以UTF-8解码后发向浏览器;

 

解决方案流程图:

 

 

问题2

问题代码如下:

[java] view plain copy
  1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  2.         PrintWriter out = response.getWriter();  
  3.         String data = "博客";  
  4.         out.println(data);    
  5.     }  


浏览器输出: ??

原因:"博客"首先被封装在response对象中,因为IE和WEB服务器之间不能传输文本,然后就通过ISO-8859-1进行编码,但是ISO-8859-1中没有“博客”的编码,因此输出“??”表示没有编码;

 

错误代码流程图:

 

 

而解决方案是:response.setCharacterEncoding("GB2312"); 设置response使用的码表

 

解决方案流程图:

 

 

补充:通过<meta>标签模拟response头;

<meta http-equiv="content-type" content="text/html"/> 等价于 response.setContentType("text/html");


1 0
原创粉丝点击