0%

tomcat学习笔记CH6

​ 最近在看《how tomcat works》。看完第6章,对整个时序流程还是感觉有点模糊,所以画了一个时序图总结下。

​ 其中省略了一部分内容:

  • 生命周期管理,事件发生调用fireLifecycleEvent,相关listener会有动作,省略了
  • 管道的逐级调用省略了

Lifecycle

​ 生命周期时间发生时,调用fireLifecycleEvent,被注册的listener会逐个触发,在listener内部检测事件类型,进行相应的处理,fireLifecycleEvent方法的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void fireLifecycleEvent(String type, Object data) 

{

​ LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);

​ LifecycleListener interested[] = null;

synchronized (listeners) {

​ interested = (LifecycleListener[]) listeners.clone();

​ }

for (int i = 0; i < interested.length; i++)

​ interested[i].lifecycleEvent(event);

}

​ LifecycleListener在lifecycleEvent方法中对事件类型进行检查,并做出相应的响应。

Servlet的载入

​ Serlet的载入是在pipline的逐级调用中完成的。查看bootstrap的代码:

1
2
3
4
5
6
7
8
9
10
11
Wrapper wrapper1 = new SimpleWrapper();

wrapper1.setName("Primitive");

wrapper1.setServletClass("PrimitiveServlet");

Wrapper wrapper2 = new SimpleWrapper();

wrapper2.setName("Modern");

wrapper2.setServletClass("ModernServlet");

​ 可以看到Serlvlet跟SimpleWrapper相关,而SimpleWrapper的构造器有如下代码:

1
2
3
4
5
6
7
public SimpleWrapper() 

{

​ pipeline.setBasic(new SimpleWrapperValve());

}

​ 可以看到SimpleWrapperValve被设置为基础阀。在查看SimpleWrapperValve的invoke方法代码:

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
public void invoke(Request request, Response response, ValveContext valveContext)  throws IOException, ServletException { 

​ SimpleWrapper wrapper = (SimpleWrapper) getContainer();

​ ServletRequest sreq = request.getRequest();

​ ServletResponse sres = response.getResponse();

​ Servlet servlet = null;

​ HttpServletRequest hreq = null;

if (sreq instanceof HttpServletRequest)

​ hreq = (HttpServletRequest) sreq;

​ HttpServletResponse hres = null;

if (sres instanceof HttpServletResponse)

​ hres = (HttpServletResponse) sres;

// Allocate a servlet instance to process this request

try {

​ servlet = wrapper.allocate();

if (hres!=null && hreq!=null) {

​ servlet.service(hreq, hres);

​ } else {

​ servlet.service(sreq, sres);

​ }

​ } catch (ServletException e) {

​ }

}

​ 可以看到servlet的载入。

那么SimpleWrapperValve是怎么找到对应的Wrapper的呢?

​ 从代码

1
SimpleWrapper wrapper = (SimpleWrapper) getContainer(); 
看到是调用getContainer方法得到的。而setContainer()发生在addValve和setBasic方法中。

值得一提的是,SimpleWrapperValve在SimpleWrapper的pipeline实例中被设置为基础阀,而SimpleContext的pipeline实例中对应的基础阀则是SimpleContextValve,在SimpleContextValve的invoke方法中有获取Wrapper实例的代码,正是这样实现的从Context调用的子容器Wrapper

​ SimpleWrapperValve的invoke方法如下:

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
public void invoke(Request request, Response response, ValveContext valveContext)  throws IOException, ServletException {

// Validate the request and response object types

if (!(request.getRequest() instanceof HttpServletRequest)

​ || !(response.getResponse() instanceof HttpServletResponse))

​ {

return;

// NOTE - Not much else we can do generically

​ }

// Disallow any direct access to resources under WEB-INF or META-INF

​ HttpServletRequest hreq = (HttpServletRequest) request.getRequest();

​ String contextPath = hreq.getContextPath();

​ String requestURI = ((HttpRequest) request).getDecodedRequestURI();

​ String relativeURI = requestURI.substring(contextPath.length()).toUpperCase();

​ Context context = (Context) getContainer();

// Select the Wrapper to be used for this Request

​ Wrapper wrapper = null;

try {

​ wrapper = (Wrapper) context.map(request, true);

​ } catch (IllegalArgumentException e) {

​ badRequest(requestURI, (HttpServletResponse) response.getResponse());

return;

​ } if (wrapper == null)

​ {

​ notFound(requestURI, (HttpServletResponse) response.getResponse());

return;

​ }

// Ask this Wrapper to process this Request

​ response.setContext(context);

​ wrapper.invoke(request, response);

}

总结起来就是httpProcessor在process()方法中调用connector.getcontainer().invoke(),使得SimpleContext中的pipeline逐个调用阀,而SimpleContextValve这个基础阀调用了Wrapper,从而实现servlet的载入调用