HttpServlet背后的那些事!

阅读本篇文章大约花费您4分钟!


  我们自己新建一个Servlet类的时候,我们通常会继承自HttpServlet类,并且编译器也是默认帮我们继承了HttpServlet类,为什么我们要继承HttpSerlvet类呢?初学Web的同学一定知道Servlet类,并且熟知里面的五个方法和Serlvet的生命周期,为什么到具体使用Serlvet的时候却和HttpServlet关系深厚,看起来和Serlvet类好像没什么关系?

  今天我们就来看一下这其中到底是怎么回事。我们需要了解三个类,分别是Servlet,GenericServlet,HttpServlet。

Servlet接口

  Servlet中一共有五个方法,其中包含三个生命周期方法:

  • init(ServletConfig config) 初始化方法【生命周期方法】
  • service()方法 具体的操作方法 【生命周期方法】
  • destroy()方法 销毁方法 【生命周期方法】
  • getServletInfo() 返回Serlvet描述信息 【非生命周期方法】
  • getServletConfig() 返回servlet配置对象 【非生命周期方法】

实现serlvet接口我们必须重写这五个方法,显然这是不友好的,所以有一个GenericServlet类对Servlet进行了封装。

GenericServlet类

  GenericServlet类是一个抽象类,实现Servlet接口,其中主要完成了以下工作:

  1. 将init(ServletConfig config)方法中的config对象赋给类的成员变量,可以通过getServletConfig()获得
  2. 为Servlet接口所有方法提供默认的实现(有的是空方法体)
  3. 提供一个新的不带参数的init()方法,子类通过重写这个方法而不是原始的init()方法来进行初始化工作

  大致的实现代码如下:

abstract class GenericServlet implements Servlet,ServletConfig{

	private ServletConfig servletConfig;
	
	public void init(ServletConfig servletConfig) throws ServletException {

		this.servletConfig=servletConfig;
		
                //调用自定义的init() 
		this.init();
	}

	//自定义的init()方法,可以由子类覆盖
	public void init(){ }
	

	//实现service()空方法,并且声明为抽象方法,强制子类必须实现service()方法 
	public abstract void service(ServletRequest request,ServletResponse response) 
			throws ServletException,java.io.IOException{
	}
	
	//实现空的destroy方法
	public void destroy(){ }

}

  自定义init()方法的原因是:如果子类要初始化必须覆盖父类的init(ServletConfig)而使它无效, this.servletConfig=servletConfig不起作用 这样就会导致空指针异常。自定义init()后,如果子类要初始化,可以直接覆盖不带参数的init()方法。

  不该参数的init()方法不是生命周期方法。

  进行了这样的封装,一个Servlet继承自GenericServlet要方便很多,但是在网络应用中,离不开Http协议,而GenericServlet中的方法还很少,无法满足我们的使用,于是出现了HttpServlet。

HttpServlet类

  HttpServlet是一个抽象类,进一步继承了GenericServlet。它不仅进一步封装了Serlvet,还提供了很大Http相关的方法,关于这些方法的使用都很简单,这里不再赘述,主要看一下HttpServlet是如何改进GenericServlet的。

  在HttpServlet中,主要做了如下两个改动:

  1. 重写原始service()方法,并且重新定义了一个service(HttpServletRequest req,HttpServletResponse res),注意这里的两个参数变了,HttpServletRequest是ServletRequest的子类,封装了Http相关内容,HttpServletResponse也是一样。
  2. 在自定义的service()方法中,针对不同的请求分别调用处理不同请求的方法doGet(),doPost()

  代码如下:

abstract class HttpServlet extends GenericServlet{
	
	//HttpServlet中的service
	protected void service(HttpServletRequest httpServletRequest,
                    HttpServletResponse httpServletResponse){
	        //该方法通过httpServletRequest.getMethod()判断请求类型调用doGet() doPost()
	}
	
	//必须实现父类的service()方法
	public void service(ServletRequest servletRequest,ServletResponse servletResponse){
		HttpServletRequest request;
		HttpServletResponse response;
                //由于HttpSerlvetRequest/response是ServletRequest/response的子类,可以转换
		try{
			request=(HttpServletRequest)servletRequest;
			response=(HttpServletResponse)servletResponse;
		}catch(ClassCastException){
			throw new ServletException("non-http request or response");
		}
		//调用service()方法
		this.service(request,response);
	}
	
}

  经过HttpSerlvet封装之后,我们只需要针对不同的请求重写doPost()或者doGet()方法即可,也就是我们平时在eclipse中最常使用的方式。

  这就是Servlet到HttpServlet的过程,了解了这个过程有助于帮助Serlvet的理解,平时在学习的时候,也应该从底层原理取理解一项技术,这样不仅能帮助我们理解,也可以学习到很多优秀的编程思想,这也是大家都建议程序员要学会阅读源码的原因之一。

  希望大家都可以在自己的道路上闯出一片天地!

猜你喜欢

转载自blog.csdn.net/tianc_pig/article/details/85050007