• 精通Servlet研究,HttpServlet的实现追究


    JSP的本质就是Servlet,或者说(是吧,开始说的是java,汗)JavaWeb开发的本质也就是Servlet+JDBC.任何性质的框架技术最底层的依然是基于他们2个.因此如果自己想写一套如SSH那样的框架技术,Java最底层的东西是必须掌握的.

    Servlet被称为"服务器端小程序."是运行在服务器端的程序,用于处理以及响应客户端的请求.

    HTTP 目前支持 7 种请求方法: GET  POST   PUT  DELETE  TARCE,HEAD  OPTIONS .前四个都是客户端请求数据这类相关的方法.

    • GET      请求获取由Request-URI所标识的资源。
    • POST Request-URI所标识的资源后附加新的数据。
    • PUT 请求服务器存储一个资源,并用Request-URI作为其标识。
    • DELETE 请求服务器删除由Request-URI所标识的资源。
    •  
    • TRACE 请求服务器回送收到的请求信息,主要用语测试或诊断。
    • HEAD 请求获取由Request-URI所标识的资源的响应消息报头。
    • OPTIONS 请求查询服务器的性能,或查询与资源相关的选项和需求。

    我在这里只是列出HTTP的请求方法,具体的HTTP协议请参考这篇文章:http://my.oschina.net/zhaoqian/blog/90315

    Servlet是个特殊的类,这个类必须继承HttpServlet.Servlet提供了相对于HTTP数据请求的四个方法的请求(其他三个也提供了,但我懒得写了,很少用到).

    • doGet:用于响应上面绿色字体的GET请求.
    • doPost:用于响应客户端的POST请求.
    • doPut:用于响应客户端的PUT请求.
    • doDelete:用于响应客户端的Delete请求.

    事实上,HTTP这个大好青年被浪费了,大部分我们就是使用GET/POST请求,至于PUT/DELETE都不知道被遗弃到哪里去了.但最近几年兴起的一种REST模型的WEB服务,让HTTP协议发出了第二春.具体详见:http://my.oschina.net/zhaoqian/blog/90321

    而诸如Struts2,SpringMVC这类的框架,也就是使用GET/POST,但工作时间久后,越发的感觉到,很多新技术,没必要去追,所需要的是,搞清楚追底层的,以不变的底层去应对万变的新技术.

    Servlet的示例代码:

    01 package org.credo.test;
    02  
    03 import java.io.IOException;
    04  
    05 import javax.servlet.ServletConfig;
    06 import javax.servlet.ServletException;
    07 import javax.servlet.annotation.WebServlet;
    08 import javax.servlet.http.HttpServlet;
    09 import javax.servlet.http.HttpServletRequest;
    10 import javax.servlet.http.HttpServletResponse;
    11  
    12 /**
    13  * <P>Project: Credo's Base</P>
    14  * <P>Description:Servlet3.0的第一个测试. </P>
    15  * <P>Copyright (c) 2012 LionCredo.All Rights Reserved.</P>
    16  * @author <a href="zhaoqianjava@qq.com">LionCredo</a>
    17  */
    18 @WebServlet(name="firstServlet",urlPatterns="/firstServlet")
    19 public class ServletTest1 extends HttpServlet{
    20  
    21     private static final long serialVersionUID = -2149324298582445679L;
    22      
    23     @Override
    24     public void init(ServletConfig config) throws ServletException {
    25         //初始化Servlet的时候,可以在这里完成某些初始化的方法.
    26         super.init(config);
    27     }
    28      
    29     @Override
    30     protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
    31         //大部分时候,对于所有请求都是一样的话,可以重写HttpServlet的Service方法来替代doGet/doPost这些方法.
    32         super.service(arg0, arg1);
    33     }
    34      
    35     @Override
    36     public void destroy() {
    37         // 在销毁Servlet之前,需要完成某些资源的回收,比如关闭数据库链接等,可以写在这里.
    38         super.destroy();
    39     }
    40 }

    HttpServlet

    public abstract class javax.servlet.http

    Extends: GenericServlet 
    Implements: java.io.Serializable 

    提供将要被子类化以创建适用于 Web 站点的 HTTP servlet 的抽象类。HttpServlet 的子类至少必须重写一个方法,该方法通常是以下这些方法之一:

     •doGet,如果 servlet 支持 HTTP GET 请求 
    •doPost,用于 HTTP POST 请求 
    •doPut,用于 HTTP PUT 请求 
    •doDelete,用于 HTTP DELETE 请求 
    •init 和 destroy,用于管理 servlet 的生命周期内保存的资源 
    •getServletInfo,servlet 使用它提供有关其自身的信息 

    几乎没有理由重写 service 方法。service 通过将标准 HTTP 请求分发给每个 HTTP 请求类型的处理程序方法(上面列出的 doXXX 方法)来处理它们。 
    同样,几乎没有理由重写 doOptions 和 doTrace 方法。 
    servlet 通常运行在多线程服务器上,因此应该意识到 servlet 必须处理并发请求并小心地同步对共享资源的访问。共享资源包括内存数据(比如实例或类变量)和外部对象(比如文件、数据库连接和网络连接)。有关在 Java 程序中处理多个线程的更多信息,请参见 Java Tutorial on Multithreaded Programming。 

    上面是查阅官方的API所得的说明,大概可以看看,虽说不官方推荐重写Service方法,但实际上javaweb开发中,很少有web开发完全严格的使用Http协议.所以说大好青年浪费不是没道理的,但商业的实现手段就是商业的实现手段,技术永远是其次的.当然,REST这个嘛,第二春....再详细的方法就看HttpServlet的源码或者API.

    ===========================================

    一个Servlet继承抽象类HttpServlet,而HttpServlet继承GenericServlet,

    abstract  GenericServlet

    Implements: Servlet, ServletConfig, java.io.Serializable
    Extended by: HttpServlet
    定义一般的、与协议无关的 servlet。要编写用于 Web 上的 HTTP servlet,请改为扩展 javax.servlet.http.HttpServlet。 
    GenericServlet 实现 Servlet 和 ServletConfig 接口。servlet 可以直接扩展 GenericServlet,尽管扩展特定于协议的子类(比如 HttpServlet)更为常见。 
    GenericServlet 使编写 servlet 变得更容易。它提供生命周期方法 init 和 destroy 的简单版本,以及 ServletConfig 接口中的方法的简单版本。GenericServlet 还实现 log 方法,在 ServletContext 接口中对此进行了声明。 
    要编写一般的 servlet,只需重写抽象 service 方法即可。 

    剥离了一大堆注释的源码:

    01 package javax.servlet;
    02  
    03 import java.io.IOException;
    04 import java.util.Enumeration;
    05  
    06 public abstract class GenericServlet implements Servlet, ServletConfig,
    07         java.io.Serializable {
    08  
    09     private static final long serialVersionUID = 1L;
    10  
    11     //java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。  
    12     //Java的serialization提供了一种持久化对象实例的机制。
    13     //当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。
    14     //为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
    15     //当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
    16     private transient ServletConfig config;
    17  
    18     public GenericServlet() {
    19         //构造方法
    20     }
    21  
    22     @Override
    23     public void destroy() {
    24         //销毁
    25     }
    26  
    27     @Override
    28     public String getInitParameter(String name) {
    29         return getServletConfig().getInitParameter(name);
    30     }
    31  
    32     @Override
    33     public Enumeration<String> getInitParameterNames() {
    34         return getServletConfig().getInitParameterNames();
    35     }
    36  
    37     @Override
    38     public ServletConfig getServletConfig() {
    39         return config;
    40     }
    41  
    42      */
    43     @Override
    44     public ServletContext getServletContext() {
    45         return getServletConfig().getServletContext();
    46     }
    47  
    48     @Override
    49     public String getServletInfo() {
    50         return "";
    51     }
    52  
    53     @Override
    54     public void init(ServletConfig config) throws ServletException {
    55         this.config = config;
    56         this.init();
    57     }
    58  
    59     public void init() throws ServletException {
    60         // NOOP by default
    61     }
    62  
    63     public void log(String msg) {
    64         getServletContext().log(getServletName() + ": " + msg);
    65     }
    66  
    67     public void log(String message, Throwable t) {
    68         getServletContext().log(getServletName() + ": " + message, t);
    69     }
    70  
    71     @Override
    72     public abstract void service(ServletRequest req, ServletResponse res)
    73             throws ServletException, IOException;
    74  
    75     @Override
    76     public String getServletName() {
    77         return config.getServletName();
    78     }
    79 }

    可以看到,HttpServlet,是基于Http协议扩展了GenericServlet的抽象类.

    而 GenericServlet是实现了Servlet,ServletConfig,以及必须的Serializable序列化.下面主要看实现的2个接口,Servlet,ServletConfig都定义了那些接口.

    ===========================================================

    public interface javax.servlet

    Implemented by: FacesServlet, GenericServlet, JspPage 
    定义所有 servlet 都必须实现的方法。  
    servlet 是运行在 Web 服务器中的小型 Java 程序。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。  

    要实现此接口,可以编写一个扩展 javax.servlet.GenericServlet 的一般 servlet,或者编写一个扩展 javax.servlet.http.HttpServlet 的 HTTP servlet。  

    此接口定义了初始化 servlet 的方法、为请求提供服务的方法和从服务器移除 servlet 的方法。这些方法称为生命周期方法,它们是按以下顺序调用的:  
    1.构造 servlet,然后使用 init 方法将其初始化。  
    2.处理来自客户端的对 service 方法的所有调用。  
    3.从服务中取出 servlet,然后使用 destroy 方法销毁它,最后进行垃圾回收并终止它。  

    除了生命周期方法之外,此接口还提供了 getServletConfig 方法和 getServletInfo 方法,servlet 可使用前一种方法获得任何启动信息,而后一种方法允许 servlet 返回有关其自身的基本信息,比如作者、版本和版权。  

    可以看源码,API,都可以发现servlet是一个接口.定义了五个方法.

    01 package javax.servlet;
    02  
    03 import java.io.IOException;
    04  
    05 public interface Servlet {
    06  
    07     public void init(ServletConfig config) throws ServletException;
    08  
    09     public ServletConfig getServletConfig();
    10      
    11     public void service(ServletRequest req, ServletResponse res)
    12             throws ServletException, IOException;
    13  
    14     public String getServletInfo();
    15      
    16     public void destroy();
    17 }

     public interface javax.servlet  ServletConfig 

    Implemented by: GenericServlet 
     servlet 容器使用的 servlet 配置对象,该对象在初始化期间将信息传递给 servlet。  

    看上图Servlet接口中有 getServletConfig.从这个接口的定义可以结合起来.

    01 package javax.servlet;
    02  
    03 import java.util.Enumeration;
    04  
    05 public interface ServletConfig {
    06  
    07     public String getServletName();
    08  
    09     public ServletContext getServletContext();
    10  
    11     public String getInitParameter(String name);
    12  
    13     public Enumeration<String> getInitParameterNames();
    14 }

    ==========================================================================

    可以看到,正常下我们使用的的 Servlet extend httpServlet,

    然后实质上的httpServlet extends GenericServlet ,

    GenericServlet实现implements 

    1.接口Servlet, 2.接口ServletConfig,以及 3.java.io.Serializable.

    具体的接口定义和抽象类的实现,可以看看源代码,不过话说回来,我现在的目的也就是搞清楚,而非是自己写,那样精力消耗太大.毕竟源码是轻易获得的,想看明白就行了.

    =====================================================================================================

    HttpServletRequest , HttpServletResponse 

    下一个将粗略说下HttpServletRequest , HttpServletResponse .这两个写下就知道是什么情况了.

    HttpServletRequest , HttpServletResponse,在Servlet里是什么意思,一目了然.

    Web基于HTTP的开发中,这两货是天天用的东西.两者都是接口.

    HttpServletRequest, HttpServletResponse对象简介:

    客户端对于Servlet的每次访问请求,Servlet容器(如Tomcat)都会创建一个封装HTTP请求的对象和一个代表HTTP响应的对象,当调用Servlet的doGet或doPost方法时,这两个对象会作为参数被传递进去

     

     

    HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,所发出的HTTP请求消息被封装在此对象之中,通过这个对象提供的方法,即可获得客户端发出的请求信息。

    HttpServletRequest对象最基本的应用是获取浏览器传递给Web服务器的请求参数信息

     
    请求参数:
    ▪GET方式下,URL地址后的附加信息
    ▪POST方式下,HTTP请求消息中的实体内容部分

     

     

    具体的示例嘛...:日,不想写了,这些太简单了,随便去网上找的DEMO看啦.

    读取请求参数的方法
    通过HttpServletRequest对象的以下方法读取GET/POST方式下传递的参数
    getParameter
    getParameterValues

    HttpServletResponse对象代表服务器端对客户端的响应,用于封装HTTP响应消息

    输出响应正文(实体内容)输出响应正文的方法

    ▪getWriter
    –返回一个(文本)字符输出流对象
    –专用于输出内容为文本字符的网页文档
    ▪getOutPutStream
    –返回一个字节输出流对象
    –如果要输出二进制格式的响应正文,应该使用该方法

    01 public class Servlet1 extends HttpServlet {
    02    public void doGet(HttpServletRequest request, HttpServletResponse response){
    03         response.setContentType("text/html");
    04         PrintWriter out = response.getWriter();
    05         String userName = request.getParameter("userName");
    06            //通过PrintWriter类型对象out的println方法,输出内容至浏览器
    07         out.println("username:"+userName+"<br/>");
    08         //显式清理缓存和关闭
    09         out.flush();
    10         out.close();   
    11 }

    如上示例.

    HttpServletResponse其它常用方法

    }字符集编码问题
    }重定向
  • 相关阅读:
    HTTP协议
    在项目中使用模块
    将封装模块发布到NPM注册表
    package.json文件常用指令说明
    npm常用命令
    分享wifi热点
    Globals模块常用的方法和属性
    WebStorm2016.1.1永久破解
    Module
    解释器模式(Interpreter Pattern)
  • 原文地址:https://www.cnblogs.com/shihao/p/2810220.html
Copyright © 2020-2023  润新知