Servlet、GenericServlet、HttpServlet概述和实现方式

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/ym15229994318ym/article/details/100883149

什么是Servlet

我们以前接触过纯静态的html,此时对比来看servlet就是一个动态资源,他可以写java代码来进行处理请求,服务器会把请求交给Servlet来处理,Servlet一般要做的工作就是:接收请求、处理请求、完成响应。

Servlet的实现方式有哪些

三种方式:

  • 直接实现Servlet接口,Servlet接口在javax.servlet.Servlet
  • 继承GenericServlet类,GenericServlet类在javax.servlet.GenericServlet
  • 继承HttpServlet,HTTPServlet在javax.servlet.http.HttpServlet
    我们所写的web项目一般是遵循http协议的,所以在开发中我们常常使用的是第三种方式,比较方便。

Servlet的生命周期

所谓生命周期像人一样就是从生到死,咱们先来看看API中Servlet的方法
servlet方法

  • ServletConfig getServletConfig()、String getServletInfo()是Servlet非生命周期方法,用来获取Servlet的配置信息和Servlet的信息(版本、作者等),我就不再多说,这里主要是对生命周期方法进一步了解。
  • void init(ServletConfig)、void service(ServletRequest,ServletResponse)、void destroy()是Servlet的三个生命周期方法。
  • 出生:服务器会在Servlet第一次被访问时创建Servlet,或者是在启动服务器是创建,此时Servlet就意味着出生了。Servlet出生(创建)后就会调用init()方法,这个方法只会被调用一次。这个方法且只会被调用一次,而且是在出生(创建)时调用,所以一般我们会把初识化要做的工作放在这个方法中。
  • 工作:当我们的服务器收到请求时,Servlet就会去调用service()方法,所以我们把处理请求的代码放在这个方法中,而且是收到一次调用一次,所以这个方法是被调用多次的。
  • 死亡:在服务器被关闭时,服务器会去关闭Servlet,此时我们的Servlet就意味着死亡了,所以我们把Servlet要释放的资源放在这个方法中。

上面可以看出这三个生命周期方法不是我们自己手动调用,而是通过服务器自己调用,这一点很重要。

下面我们开始上代码,创建Servlet。

第一种方式:直接实现Servlet接口

实现接口,需要重写接口中的方法

public class MyServlet implements Servlet {
	public void init(ServletConfig config) throws ServletException {
		System.out.println("Servlet出生!");
	}
	// 这里我们先返回一个null
	public ServletConfig getServletConfig() {return null;}
	public void destroy() {
		System.out.println("Servlet死亡!");
	}
	// 这里我们先返回一个null
	public String getServletInfo() {return null;}

	public void service(ServletRequest req, ServletResponse res)
			throws ServletException, IOException {
		System.out.println("servlet工作!");
	}
}

当我们启动服务器,并且第一次访问MyServlet时,init()就会被调用,然后再调用service()方法,再次访问时不会再调用init()方法,但是会调用service()方法,直到关闭服务器时,就会调用destroy()方法。

注意当工具中没有自动生成我们的Servlet对应的xml文件时,我们应该自己进行配置,否则访问服务器是无效的。

<servlet>
		<servlet-name>hello</servlet-name>
		<servlet-class>cn.bjwlxy.servlet.MyServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>hello</servlet-name>
		<url-pattern>/MyServlet</url-pattern>
	</servlet-mapping>  
</servlet>

servlet-name:此标签是我们在xml中给我们这个Servlet起的名字,可以为自定义但是必须要和标签中的servlet-name名字一样,这样就关联起来了。
servlet-class:此标签是我们Servlet的类路径
url-pattern:此标签是我们访问服务器时要在地址栏中敲的地址。

第二种方式:继承GenericServlet类

GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。
让我们先看看GenericServlet源码:

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;
    public GenericServlet() {}
    @Override
    public void destroy() {}
    @Override
    public String getInitParameter(String name) {
        return getServletConfig().getInitParameter(name);
    }
    @Override
    public Enumeration<String> getInitParameterNames() {
        return getServletConfig().getInitParameterNames();
    }
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
    @Override
    public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }
    @Override
    public String getServletInfo() {
        return "";
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    public void init() throws ServletException {}
    public void log(String msg) {
        getServletContext().log(getServletName() + ": " + msg);
    }
    public void log(String message, Throwable t) {
        getServletContext().log(getServletName() + ": " + message, t);
    }
    @Override
    public abstract void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;
    @Override
    public String getServletName() {
        return config.getServletName();
    }
}

  • GenericServlet源码中发现,在类中有两个init方法,一个有参数一个没有参数,我们明白有参数的是生命周期方法,所以在有参数的init中才能得到config信息,但是我们的子类重写init(ServletConfig)时务必会把config的值覆盖为null,那么类中所有依赖config的方法就都不能实现,所以才提供了一个无参的init方法,给子类去覆盖并且在有参的init方法中进行调用,避免了这个问题。
  • 随着“servlet得生命周期”,会调用有参的init方法,而在GenericServlet类中的有参init方法中调用了无参的init方法。
    不由得佩服前人想出的这个办法。
public class MyServlet extends GenericServlet {
	@Override
	public void init() throws ServletException {
		System.out.println("Servlet出生!");
	}

	@Override
	public void service(ServletRequest arg0, ServletResponse arg1)
			throws ServletException, IOException {
		System.out.println("Servlet工作!");
	}

	@Override
	public void destroy() {
		System.out.println("Servlet死亡!");
	}
}

  • 通过这种方式,已经减轻了开发者的一下负担,流程还是一样,当服务器创建了Servlet后,当有一个请求访问了这个Servlet,那么就随着“servlet得生命周期”一次执行init、service、destroy方法。

第三种方式:继承HttpServlet

  • HttpServlet类继承了GenericServlet类,他是对http请求的特殊支持,由于我们开发的web项目一般是遵循http协议,所以就会常常和HttpServlet打交道。
  • 还是一样先看看HttpServlet源码
public abstract class HttpServlet extends GenericServlet {
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        ……
}
    @Override
    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);
}
……
}

  • HttpServlet类中提供了service(HttpServletRequest,HttpServletResponse)方法,这个方法是HttpServlet自己的方法,不是从Servlet继承来的。在HttpServlet的service(ServletRequest,ServletResponse)方法中会把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,然后调用service(HttpServletRequest,HttpServletResponse)方法,这说明子类可以去覆盖service(HttpServletRequest,HttpServletResponse)方法即可,这就不用自己去强转请求和响应对象了。
    其实子类也不用去覆盖service(HttpServletRequest,HttpServletResponse)方法,因为HttpServlet还要做另一步简化操作,下面会介绍。
    这里和上面道理一样先覆盖,再提供一个方法,起到了不被覆盖,又干了自己需要干的事。
  • 那么init、destroy没有覆盖,那么则会像继承了GenericServlet一样调用,这里不再赘述。毕竟GenericServlet是你创建的“Servlet”的爷爷嘛。
public class MyServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("doget请求");
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("dopost请求");
	}
	
}
  • 在HttpServlet的service(HttpServletRequest,HttpServletResponse)方法会去判断当前请求是GET还是POST,如果是GET请求,那么会去调用本类的doGet()方法,如果是POST请求会去调用doPost()方法,这说明我们在子类中去覆盖doGet()或doPost()方法即可。

总结

到这里,我想应该懂了一点这个流程了。HttpServlet继承自GenericServlet,GenericServlet又实现了Servlet接口。
所以他们是可以理解为父子关系,爷孙关系。我们无论是通过什么方式实现的Servlet,只要服务器接到请求,他就会去调用那三个生命周期方法。
希望本文对你有帮助

猜你喜欢

转载自blog.csdn.net/ym15229994318ym/article/details/100883149