您的位置:首页 > 编程语言

servlet代码分析-整个执行流程

2017-11-02 14:29 495 查看
对于Servlet的理解,对于我们更好的理解框架非常的有帮助的,所以!我之前看过的书,好多都忘记了,这个东西太多,一时间忘记了也是很正常的涩,所以我们必须多去深刻的理解整个过程,帮助我们更好的完成工作。
首先我们先来看一下servlet家族图谱 


 

Servlet API的核心就是javax.servlet.Servlet接口,所有的Servlet 类(抽象的或者自己写的)都必须实现这个接口。在Servlet接口中定义了5个方法,其中有3个方法是由Servlet 容器在Servlet的生命周期的不同阶段来调用的特定方法。

1.javax.servlet.servlet接口
package javax.servlet;
import java.io.IOException;

public interface Servlet {

//负责初始化Servlet对象。容器一旦创建好Servlet对象后,就调用此方法来初始化Servlet对象
public void init(ServletConfig config) throws ServletException;

//方法负责响应客户的请求,提供服务。当容器接收到客户端要求访问特定的servlet请求时,就会调用Servlet的service方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

//Destroy()方法负责释放Servlet 对象占用的资源,当servlet对象结束生命周期时,servlet容器调用此方法来销毁servlet对象.

public void destroy();

//说明:Init(),service(),destroy() 这三个方法是Servlet生命周期中的最重要的三个方法。

//返回一个字符串,在该字符串中包含servlet的创建者,版本和版权等信息
public String getServletInfo();

//GetServletConfig: 返回一个ServletConfig对象,该对象中包含了Servlet初始化参数信息

public ServletConfig getServletConfig();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

GenericServlet抽象类实现了Servlet接口,它只是通用的实现,与任何网络应用层协议无关。 

同时,HttpServlet这个抽象类继承了GenericServlet抽象类,在Http协议上提供了通用的实现。
GenericServlet 代码
package javax.servlet;
import java.io.IOException;
import java.util.Enumeration;
//抽象类GenericServlet实现了Servlet接口的同时,也实现了ServletConfig接口和Serializable这两个接口
public abstract class GenericServlet
implements Servlet, ServletConfig, java.io.Serializable
{
//私有变量,保存 init()传入的ServletConfig对象的引用
private transient ServletConfig config;

//无参的构造方法
public GenericServlet() { }
------------------------------------
以下方法实现了servlet接口中的5个方法
实现Servlet接口方法开始
------------------------------------
/*
实现接口Servlet中的带参数的init(ServletConfig Config)方法,将传递的ServletConfig对象的引用保存到私有成员变量中,
使得GenericServlet对象和一个ServletConfig对象关联.
同时它也调用了自身的不带参数的init()方法
**/

public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();   //调用了无参的 init()方法
}

//无参的init()方法
public void init() throws ServletException {

}
//空实现了destroy方法
public void destroy() { }
//实现了接口中的getServletConfig方法,返回ServletConfig对象
public ServletConfig getServletConfig()
{
return config;
}
//该方法实现接口<Servlet>中的ServletInfo,默认返回空字符串
public String getServletInfo() {
return "";
}

//唯一没有实现的抽象方法service(),仅仅在此声明。交由子类去实现具体的应用
//在后来的HttpServlet抽象类中,针对当前基于Http协议的Web开发,HttpServlet抽象类具体实现了这个方法
//若有其他的协议,直接继承本类后实现相关协议即可,具有很强的扩展性
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
------------------------------------
实现Servlet接口方法结束
------------------------------------

---------------------------------------------
以下四个方法实现了接口ServletConfig中的方法
实现ServletConfig接口开始
---------------------------------------------
//该方法实现了接口<ServletConfig>中的getServletContext方法,用于返回servleConfig对象中所包含的servletContext方法
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}

//获取初始化参数
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}

//实现了接口<ServletConfig>中的方法,用于返回在web.xml文件中为servlet所配置的全部的初始化参数的值
public Enumeration getInitParameterNames() {
return getServletConfig().getInitParameterNames();

//获取在web.xml文件中注册的当前的这个servlet名称。没有在web.xml 中注册的servlet,该方法直接放回该servlet的类名。
//法实现了接口<ServleConfig>中的getServletName方法
public String getServletName() {
return config.getServletName();
}
---------------------------------------------
实现ServletConfig接口结束
---------------------------------------------

public void log(String msg) {
getServletContext().log(getServletName() + ": "+ msg);
}

public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

HttpServlet是继承了GenericServlet抽象类的一个抽象类,但是他的里面并没有任何抽象方法,这就是说他并不会强迫我们去做什么。我们只是按需选择,重写HttpServlet中的的部分方法就可以了。
HttpServlet 代码
Java代码
package javax.servlet.http;

public abstract class HttpServlet extends GenericServlet
implements java.io.Serializable
{

private static final String METHOD_GET = "GET";
private static final String METHOD_POST = "POST";
......

/**
* Does nothing, because this is an abstract class.
* 抽象类HttpServlet有一个构造函数,但是空的,什么都没有
*/
public HttpServlet() { }

/*分别执行doGet,doPost,doOpitions,doHead,doPut,doTrace方法
在请求响应服务方法service()中,根据请求类型,分贝调用这些doXXXX方法
所以自己写的Servlet只需要根据请求类型覆盖响应的doXXX方法即可。
*/

//doXXXX方法开始
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}

protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
.......
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException  {
//todo
}

protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//todo
}

protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException   {
//todo
}

protected void doDelete(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException   {
//todo
}
//doXXXX方法结束

//重载的service(args0,args1)方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();

if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}

} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);

} else if (method.equals(METHOD_POST)) {
doPost(req, resp);

} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);

} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);

} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);

} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);

} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//

String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);

resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

//实现父类的service(ServletRequest req,ServletResponse res)方法
//通过参数的向下转型,然后调用重载的service(HttpservletRequest,HttpServletResponse)方法

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);  //调用重载的service()方法
}

......//其他方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

值得一说的是,很多介绍servlet的书中,讲解servlet第一个实例时候,都习惯去覆盖HttpServlet的service(HttpServletRequest request,HttpServletResponse response)方法来演示servlet.

当然,直接覆盖sevice()方法,作为演示,可以达到目的。 因为servlet首先响应的就是调用service()方法,直接在此方法中覆盖没问题。但在实际的开发中,我们只是根据请求的类型,覆盖响应的doXXX方法即可。最常见的是doGet和doPost方法。

从HtttpServlet的源码中关于service() 方法的实现,可以看出,它最终也是根据请求类型来调用的各个doxxx 方法来完成响应的。如果自己写的servlet覆盖了service()方法,若没对相应的请求做处理,则系统无法调用相关的doxxx方法来完成提交的请求任务。

在javax.servlet.Servlet接口中,定义了针对Servlet生命周期最重要的三个方法,按照顺序,依次是init(),Serveice()和destroy()这三个方法. 

Servlet初始化阶段,包括执行如下四个步骤: 

1. servlet容器(如tomcat)加载servlet类,读入其.class类文件到内存 

2. servlet容器开始针对这个servlet,创建ServletConfig对象 

3. servlet容器创建servlet对象 

4. servlet容器调用servlet对象的init(ServletConfig config)方法,在这个init方法中,建立了sevlet对象和servletConfig对象的关联,执行了如下的代码:
public void init(ServletConfig config) throws ServletException
{
this.config = config;  //将容器创建的servletConfig 对象传入,并使用私有成员变量引用该servletConfig对象
this.init();
}
1
2
3
4
5

通过以上的初始化步骤建立了servlet对象和sevletConfig对象的关联,而servletConfig对象又和当前容器创建的ServleContext对象获得关联.

运行时阶段: 

当容器接受到访问特定的servlet请求时,针对这个请求,创建对应的ServletRequest对象和ServletResponse对象,并调用servlet的service()方法,service()根据从ServletRequest对象中获得客户的请求信息 

并将调用相应的doxxx方法等进行响应,再通过ServletResponse对象生成响应结果,然后发送给客户端,最后销毁创建的ServletRequest 和ServletResponse

销毁阶段: 

只有当web应用被终止时,servlet才会被销毁。servlet容器现调用web应用中所有的Servlet对象的destroy()方法,然后再销毁这些servlet对象,此外,容器还销毁了针对各个servlet所创建的相关联的serveltConfig对象

以下程序代码演示了Servlet对象的生命周期:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class LifeServlet extends HttpServlet
{
private int initvar=0;
private int servicevar=0;
private int destroyvar=0;
private String name;

//覆盖父类的销毁方法,加入销毁的计数器
public void destroy()
{
destroyvar++;
System.out.println(name+">destroy()被销毁了"+destroyvar+"次");
}

//覆盖父类的init(ServletConfig config)方法 加入计数
/*  public void init(ServletConfig config) throws ServletException
{
super.init(config);  //调用父类的带参数的init方法
name=config.getServletName();
initvar++;
System.out.println(name+">init():Servlet 被初始化了"+initvar+"次");

}*/

/* 根据分析GenericServlet的源码,其init(ServletConfig config)方法最终还是会调用了不带参数的init()方法,
* 所以也可以在自己编写的的servlet中覆盖不带参数的init()方法来加入统计计数
*/

public void init()  //覆盖父类的不带参数的初始化方法
{
initvar++;
//name=getServleConfig.getServletNmae();
name=getServletName();  //直接返回
System.out.println(name+"init(): servlet被初始化了了 "+initvar+"次");
}
*/

public void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException
{
servicevar++;  //请求服务计数器自增
System.out.println(name+">service():servlet共响应了"+servicevar+"请求");

String content1="初始化次数: "+initvar;
String content2="销毁次数: "+destroyvar;
String content3="请求服务次数 "+servicevar;
response.setContentType("text/html;charset=GBK");
PrintWriter out=response.getWriter();
out.print("<html><head><title>LifeServlet</title></head>");
out.print("<body>");
out.print("<h1>"+content1+"</h1><br>");
out.print("<h1>"+content2+"</h1><br>");
out.print("<h1>"+content3+"</h1><br>");
out.print("</body>");
out.print("</head>");
out.close();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

之前提到servlet 生命周期中的三个阶段,第一个阶段中servlet容器会执行init方法来初始化一个servlet. 

init方法和destroy这两个方法在servlet生命周期中之执行一次。servlet容器(或者说是servlet引擎)创建了servlet实例对象后立即调用该init方法。 

Init方法是在servlet对象被创建后,再由servlet容器调用的方法,其执行位于构造方法之后,在执行init方法时,会传递一个serveltConfig对象。 

所以,如果要在初始代码中用到servletConfig对象,则这些初始操作只能在init方法中编写,不能在构造方法中编写.
Java代码
GenericServlet中关于init方法的示意源码:

public void init(ServletConfig config) throws ServletException
{
this.config=config;
......

}
1
2
3
4
5
6
7
8
9

如果在要在自己编写的servlet类中增加一些额外的初始化功能,则必须覆盖GenricServlet类的init(ServletConfig config)方法,如果覆盖后,父类的init(ServletConfig config)方法就不会被调用。GenericServlet类中有些方法依赖init方法执行的结果,例如
public ServletContext getServeltContext()
{
return config.getServletContext;
}
1
2
3
4

这里的config对象的引用就来自Init(ServletConfig config)执行的结果.

所以如果子类需要覆盖了父类的init(ServletConfig config)方法,则首先要调用父类的init(ServletConfig config)方法,也就是先加入 super.init(config) 语句来先执行父类的方法.否则,会产生空指针异常 java.lang.NullPointerException ,因为config对象的引用为空。

为了避免这样的情况的产生,GenericServlet的设计人员 在GenericServlet中又定义了一个无参数的init()空方法. 且在init(servletConfig config)方法最后也调用了这个无参的空方法
Java代码
public void init(ServletConfig config) throws ServletException
{
this.config=config;
init();
}
1
2
3
4
5
6

所以,我们只需覆盖这个无参的init方法加入自己的初始代码即可,而无需覆盖带参数的父类的init方法. 

如果有多个重载的init方法,对以servlet而言,servlet容器始终就之调用servlet接口中的那个方法:init(ServletConfig config) (当然也会执行已经覆盖的无参的init()方法),其他的覆盖的init方法不会执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: