什么是servlet
servlet是运行在服务器端的一个小程序,主要功能在于交互式地浏览和修改数据(处理用户请求,然后响应结果给浏览器端),生成动态Web内容。其过程为:
- 客户端发起请求到服务器端
- 服务器端接收到请求后发送到servlet
- servlet处理请求并将响应内容发送给服务器(响应内容根据客户端请求而定)
- 服务器在响应的内容发送给客户端
- 客户端进行响应内容展示
Servlet是一个接口,对于任何一个实现了该接口的类或者是继承了实现了servlet接口的类,我们都可以称之为servlet。
servlet的生命周期
我们看下servlet的源码
public interface Servlet {
public abstract void init(ServletConfig servletconfig)
throws ServletException;
public abstract ServletConfig getServletConfig();
public abstract void service(ServletRequest servletrequest,
ServletResponse servletresponse) throws ServletException,
IOException;
public abstract String getServletInfo();
public abstract void destroy();
}
从源码中我们可以看出有五个方法,其中servlet的生命周期中主要涉及到的方法是init,servlet,destroy方法。
init方法:当服务器启动的时候(web.xml中配置load-on-startup=1,默认为0)或者是第一次访问servlet的时候,init方法就会执行,此时会初始化一个servle对象。
servlet方法:主要处理来自用户的请求。
destroy方法:当服务器关闭的时候,servlet销毁,此时destroy方法会执行。
用图表示如下:
下面通过运行代码来测试一下
web.xml配置如下
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.linewell.servlet.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
MyServlet代码如下
package com.linewell.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet implements Servlet {
@Override
public void init(ServletConfig servletconfig) throws ServletException {
System.out.println("init方法执行。。。。。。。");
}
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}
@Override
public void service(ServletRequest servletrequest,
ServletResponse servletresponse) throws ServletException,
IOException {
System.out.println("service方法执行。。。。。。。");
}
@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return null;
}
@Override
public void destroy() {
System.out.println("destroy方法执行。。。。。。。");
}
}
第一次访问
结果如下
当我们再次执行访问时,运行结果如下
由此可以看出init方法只有在第一次访问servlet的时候才会执行(这里不考虑load-on-startup=1的情况),而service方法对于用户发起的每一次请求都会执行。
在使用myeclipse工具正常关闭tomcat服务器,控制台并没有打印出destrory方法,无奈最后只能通过shutdow.bat命令来关闭tomcat,控制台结果如下
实际开发中如果我们要编写一个自己的servlet,并不会去实现实现servlet接口,而是继承httpservert。关于servle继承体系如下
根据它的继承体系我们看下GenericServlet这个类,源码如下
package javax.servlet;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.ResourceBundle;
public abstract class GenericServlet implements Servlet, ServletConfig,
Serializable {
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
ServletConfig sc = getServletConfig();
if (sc == null)
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
else
return sc.getInitParameter(name);
}
public Enumeration getInitParameterNames() {
ServletConfig sc = getServletConfig();
if (sc == null)
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
else
return sc.getInitParameterNames();
}
public ServletConfig getServletConfig() {
return config;
}
public ServletContext getServletContext() {
ServletConfig sc = getServletConfig();
if (sc == null)
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
else
return sc.getServletContext();
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
init();
}
public void init() throws ServletException {
}
public void log(String msg) {
getServletContext().log(
(new StringBuilder()).append(getServletName()).append(": ")
.append(msg).toString());
}
public void log(String message, Throwable t) {
getServletContext().log(
(new StringBuilder()).append(getServletName()).append(": ")
.append(message).toString(), t);
}
public abstract void service(ServletRequest servletrequest,
ServletResponse servletresponse) throws ServletException,
IOException;
public String getServletName() {
ServletConfig sc = getServletConfig();
if (sc == null)
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
else
return sc.getServletName();
}
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle
.getBundle("javax.servlet.LocalStrings");
private transient ServletConfig config;
}
通过源码可以看出与servlet生命周期有关的的三个方法都有。但是我们看下这个init方法,一个有参的,一个无参的。为什么要这样做呢?这个其实是便于我们在init方法做一些自己的逻辑操作,我们如果添加一些的自己的逻辑代码,我们只需要在无参的init方法里面加入自己的逻辑代码就可以了。这样既可以不破坏原始init方法(有参的)代码,又可以在初始化的得执行自己的逻辑代码。我们再来看下servvice方法,这里的service方法是一个抽象的方法并且只是做了一个空的实现。如果我们要继承GenericServlet 那还要重写service方法,那不得累死。这样说明它的子类中肯定进行了重写。我们看下它的子类HttpServlet源码中的service如下:
这里只是对ServletRequest对象和ServletResponse对象做了一个强转。这样是没什么问题的,
通过类名就可以知道与http协议有关,所以我们在编写的servlet的时候,我们只要继承httpservlet即可。继承httpservlet之后我们可以获取servletcontext对象,获取servletconfig对象,获取servlet的名称,获取到初始化参数而且还可以根据请求的类型来处理不同的请求以及一些关于请求的信息包括请求头,请求资源路径等等
这里不做测试,有兴趣的可以自行进行测试。