您的位置:首页 > 运维架构 > Tomcat

tomcat解析(十九)对客户请求的处理3

2010-02-09 18:14 387 查看
下面是StandardContextValve的invoke方法内容

<1>对于路径中直接访问META-INF或WEB-INF目录下资源的,直接返回404错误

MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
notFound(response);
return;
}


<2>如果此时当前Context正在reload,则线程暂停并设置最新的ClassLoader;如果匹配到的wrapper处理不存在,则返回404错误,若wrapper处于不可用的状态,重新获取,因为可以处理reload的过程中

// Wait if we are reloading
boolean reloaded = false;
while (context.getPaused()) {
reloaded = true;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
;
}
}

// Reloading will have stopped the old webappclassloader and
// created a new one
if (reloaded &&
context.getLoader() != null &&
context.getLoader().getClassLoader() != null) {
Thread.currentThread().setContextClassLoader(
context.getLoader().getClassLoader());
}
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null) {
notFound(response);
return;
} else if (wrapper.isUnavailable()) {
// May be as a result of a reload, try and find the new wrapper
wrapper = (Wrapper) container.findChild(wrapper.getName());
if (wrapper == null) {
notFound(response);
return;
}
}


<3>触发该Context下已注册的ServletContextAttributeListener,ServletRequestAttributeListener,ServletRequestListener及HttpSessionAttributeListener的contextInitialized事件,在第四步后还触发其requestDestroyed事件,这里没有注册这几个监听器,暂时不讲.

<4>调用对应wrapper的方法:wrapper.getPipeline().getFirst().invoke(request, response);

依然可以根据StandardWrapper的构造方法看到将调用的是StandardWrapperValve的invoke方法

内容大概有

1.查看当前context及wrapper对象是否处理于unavailable状态,主要是服务器关闭或重启问题

2.状态可用,分配实例化及初始化当前Wrapper对应的Servlet对象,servlet = wrapper.allocate()

3.以当前请求,servlet及wrapper对象为参数创建对应的ApplicationFilterChain对象(实现了FilterChain接口:Filter将使用到),此时会将当前Context里与该request对应的Filter加到ApplicationFilterChain里,无特殊顺序,因此将是按照web.xml里的声明顺序来的

4.调用ApplicationFilterChain对象的doFilterEvent或doFilter方法(两者如何区别仍未知,后续再说),java Web开发很多都在此部分内容;doFilter方法里主要又是调用了internalDoFilter方法进行处理

3.对实例化的Servlet及ApplicationFilterChain对象进行资源回收

ApplicationFilterChain.internalDoFilter内容大概如下:

1.调用所持有的第一个Filter的doFilter方法,以request,response及自身为参数

// Call the next filter if there is one
if (pos < n) { //n为filter的个数
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
filter = filterConfig.getFilter();
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filter, request, response);

if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();

Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege
("doFilter", filter, classType, args, principal);

args = null;
} else {
filter.doFilter(request, response, this);
}

support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response);
} catch (IOException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (ServletException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (RuntimeException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (Throwable e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw new ServletException
(sm.getString("filterChain.filter"), e);
}
return;
}


设想开发者编写了两个Filter,在过滤请求的时候便会执行上面这一段代码来调用第一个Filter,如果在第一个Filter里又调用了FilterChain.doFilter,则又会调用上面这一段代码,而此时pos已自加1,因此将会调用第二个Filter处理类,依次类推直到最后的Filter的doFilter方法执行完,之后便会返回前一个Filter继续运行其调用FilterChain.doFilter之后的代码,又依次类推直到第一个Filter.当然这部分是Filter的执行,我们都知道还有一个Servlet会执行,那到底Servlet是什么时候执行呢?依然是ApplicationFilterChain.internalDoFilter,当所有的Filter都已执行,并且在最后一个Filter又调用了FilterChain.doFilter,再来到这个方法时则不会再执行上面列出的代码,而是运行下面这段代码:

// We fell off the end of the chain -- call the servlet instance
try {
if (Globals.STRICT_SERVLET_COMPLIANCE) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}

support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
servlet, request, response);
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {

if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
args = null;
} else {
servlet.service((HttpServletRequest) request,
(HttpServletResponse) response);
}
} else {
servlet.service(request, response);
}
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response);
} catch (IOException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (ServletException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (RuntimeException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (Throwable e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw new ServletException
(sm.getString("filterChain.servlet"), e);
} finally {
if (Globals.STRICT_SERVLET_COMPLIANCE) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}


可以看到在最后的Filter再执行FilterChain.doFilter的时候将会执行请求对应的Servlet类的service方法,这也是我们早已知道的一个事实,现在只是揭开了其实现的面纱.当然既然还有Servlet需要执行,那上面的一段话应该是这样描述才对,依次类推直到Servlet执行完成后又返回其前面的Filter,再依次类推直到第一个Filter.这个实现其实与之后我们看过的PipeLine方式实现上异曲同工,但个人认为相对于Pipeline的实现,Filter的开发键较偏面向过程,而Pipeline的实现较OO(if
u know what i mean:),如之前所说,在Struts2的拦截器上也是相似的模式,但也是OO模式的,所以我在猜想编写Filter实现的开发人员可能拥有较长时间的面向过程编程资历,所以一下子比较难转换过来(纯属个人看法:)

另有一点需要注意的,客户端的返回是什么时候给的呢?如果我们不用JSP写界面,直接用Servlet或许比较明显地看出,我们一般都会写

PrintWriter out = Response.getWriter();

out.print();

out.close();

这里即会打开一个客户请求的返回结果流,然后将结果写入,因此即使我们有Filter可对返回结果进行处理,但事实在Servlet返回之后的处理是不会表现到客户端的.在实际的MVC框架如STRUTS2,SPRING MVC中,使用了JSP做为输出,但情况也是如此的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: