这里写自定义目录标题
什么是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的方法
- 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,只要服务器接到请求,他就会去调用那三个生命周期方法。
希望本文对你有帮助