Servlet知识上篇

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.youkuaiyun.com/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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值