热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Tomcat7中web应用加载原理(三)Listener、Filter、Servlet的加载和调用

分析到了org.apache.catalina.deploy.WebXml类的configureContext方法,可以看到在这个方法中通过各种setXXX、addX

分析到了org.apache.catalina.deploy.WebXml类的 configureContext 方法,可以看到在这个方法中通过各种 setXXX、addXXX 方法的调用,使得每个应用中的 web.xml 文件的解析后将应用内部的表示 Servlet、Listener、Filter 的配置信息与表示一个 web 应用的 Context 对象关联起来。

这里列出 configureContext 方法中与 Servlet、Listener、Filter 的配置信息设置相关的调用代码:

for (FilterDef filter : filters.values()) {if (filter.getAsyncSupported() == null) {filter.setAsyncSupported("false");}context.addFilterDef(filter);
}
for (FilterMap filterMap : filterMaps) {context.addFilterMap(filterMap);
}

这是设置 Filter 相关配置信息的。 

for (String listener : listeners) {context.addApplicationListener(new ApplicationListener(listener, false));
}

这是给应用添加 Listener 的。 

for (ServletDef servlet : servlets.values()) {Wrapper wrapper = context.createWrapper();// Description is ignored// Display name is ignored// Icons are ignored// jsp-file gets passed to the JSP Servlet as an init-paramif (servlet.getLoadOnStartup() != null) {wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());}if (servlet.getEnabled() != null) {wrapper.setEnabled(servlet.getEnabled().booleanValue());}wrapper.setName(servlet.getServletName());Map params = servlet.getParameterMap();for (Entry entry : params.entrySet()) {wrapper.addInitParameter(entry.getKey(), entry.getValue());}wrapper.setRunAs(servlet.getRunAs());Set roleRefs = servlet.getSecurityRoleRefs();for (SecurityRoleRef roleRef : roleRefs) {wrapper.addSecurityReference(roleRef.getName(), roleRef.getLink());}wrapper.setServletClass(servlet.getServletClass());MultipartDef multipartdef = servlet.getMultipartDef();if (multipartdef != null) {if (multipartdef.getMaxFileSize() != null &&multipartdef.getMaxRequestSize()!= null &&multipartdef.getFileSizeThreshold() != null) {wrapper.setMultipartConfigElement(new MultipartConfigElement(multipartdef.getLocation(),Long.parseLong(multipartdef.getMaxFileSize()),Long.parseLong(multipartdef.getMaxRequestSize()),Integer.parseInt(multipartdef.getFileSizeThreshold())));} else {wrapper.setMultipartConfigElement(new MultipartConfigElement(multipartdef.getLocation()));}}if (servlet.getAsyncSupported() != null) {wrapper.setAsyncSupported(servlet.getAsyncSupported().booleanValue());}wrapper.setOverridable(servlet.isOverridable());context.addChild(wrapper);
}
for (Entry entry : servletMappings.entrySet()) {context.addServletMapping(entry.getKey(), entry.getValue());
}

这段代码是设置 Servlet 的相关配置信息的。

以上是在各个 web 应用的 web.xml 文件中(如果是 servlet 3,还会包括将这些配置信息放在类的注解中,所以解析 web.xml 文件之前可能会存在各个 web.xml 文件信息的合并步骤,这些动作的代码在前一篇文章中讲 ContextConfig 类的 webConfig 方法中)的相关配置信息的设置,但需要注意的是,这里仅仅是将这些配置信息保存到了 StandardContext 的相应实例变量中,真正在一次请求访问中用到的 Servlet、Listener、Filter 的实例并没有构造出来,以上方法调用仅构造了代表这些实例的封装类的实例,如 StandardWrapper、ApplicationListener、FilterDef、FilterMap。

那么一个 web 应用中的 Servlet、Listener、Filter 的实例究竟在什么时候构造出来的呢?答案在org.apache.catalina.core.StandardContext类的 startInternal 方法中:

这 303 行可以讲的东西有很多,为了不偏离本文主题只抽出与现在要讨论的问题相关的代码来分析。

第 125 行会发布一个CONFIGURE_START_EVENT事件,按前一篇博文所述,这里即会触发对 web.xml 的解析。第 205、206 行设置实例管理器为 DefaultInstanceManager(这个类在后面谈实例构造时会用到)。第 237 行会调用 listenerStart 方法,第 255 行调用了 filterStart 方法,第 263 行调用了 loadOnStartup 方法,这三处调用即触发 Listener、Filter、Servlet 真正对象的构造,下面逐个分析这些方法。

listenerStart 方法的完整代码较长,这里仅列出与 Listenner 对象构造相关的代码:

先从 Context 对象中取出实例变量 applicationListeners(该变量的值在 web.xml 解析时设置),第 12 行通过调用

instanceManager.newInstance(listener.getClassName())

,前面在看 StandardContext 的 startInternal 方法第 205 行时看到 instanceManager 被设置为 DefaultInstanceManager 对象,所以这里实际会执行 DefaultInstanceManager 类的 newInstance 方法: 

public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {Class clazz = loadClassMaybePrivileged(className, classLoader);return newInstance(clazz.newInstance(), clazz);
}

所以instanceManager.newInstance(listener.getClassName())这段代码的作用是取出 web.xml 中配置的 Listener 的 class 配置信息,从而构造实际配置的 Listener 对象。

看下 filterStart 方法:

这段代码看起来很简单,取出 web.xml 解析时读到的 filter 配置信息,在第 17 行调用 ApplicationFilterConfig 的构造方法:

默认情况下 filterDef 中是没有 Filter 对象的,所以会调用第 12 行 getFilter 方法:

与 Listener 的对象构造类似,都是通过调用

getInstanceManager().newInstance

方法。当然,按照 Servlet 规范,第 13 行还会调用 Filter 的 init 方法。

看下 loadOnStartup 方法:

在 web 应用启动时将会加载配置了 load-on-startup 属性的 Servlet。第 24 行,调用了 StandardWrapper 类的 load 方法:

在第 2 行 loadServlet 方法中与上面的 Listener 和 Filter 对象构造一样调用

instanceManager.newInstance

来构造 Servlet 对象,与 Filter 类似在第 5 行调用 Servlet 的 init 方法。

当然这种加载只是针对配置了 load-on-startup 属性的 Servlet 而言,其它一般 Servlet 的加载和初始化会推迟到真正请求访问 web 应用而第一次调用该 Servlet 时,下面会看到这种情况下代码分析。

以上分析的 web 应用启动后这些对象的加载情况,接下来分析一下一次请求访问时,相关的 Filter、Servlet 对象的调用。

在之前的《Tomcat 7 的一次请求分析》系列文章中曾经分析了一次请求如何与容器中的 Engine、Host、Context、Wrapper 各级组件匹配,并在这些容器组件内部的管道中流转的。在该系列第四篇文章最后提到,一次请求最终会执行与它最适配的一个 StandardWrapper 的基础阀org.apache.catalina.core.StandardWrapperValve的 invoke 方法。当时限于篇幅没继续往下分析,这里接着这段来看看请求的流转。看下 invoke 方法的代码:

因为要支持 Servlet 3 的新特性及各种异常处理,这段代码显得比较长。关注重点第 42 行,这里会调用 StandardWrapper 的 allocate 方法,不再贴出这个方法的代码,需要提醒的是在 allocate 方法中可能会调用 loadServlet() 方法,这就是上一段提到的请求访问 web 应用而第一次调用该 Servlet 时再加载并初始化 Servlet 。

第 87 到 91 行会构造一个过滤器链( filterChain )用于执行这一次请求所经过的相应 Filter ,第 111 和第 128 行会调用该 filterChain 的 doFilter 方法:

在该方法最后调用了 internalDoFilter 方法:

概述一下这段代码,第 6 到 60 行是执行过滤器链中的各个过滤器的 doFilter 方法,实例变量 n 表示过滤器链中所有的过滤器,pos 表示当前要执行的过滤器。其中第 7 行取出当前要执行的 Filter,之后将 pos 加 1,接着第 30 行执行 Filter 的 doFilter 方法。一般的过滤器实现中在最后都会有这一句:

FilterChain.doFilter(request, response);

这样就又回到了 filterChain 的 doFilter 方法,形成了一个递归调用。要注意的是,filterChain 对象内部的 pos 是不断加的,所以假如过滤器链中的各个 Filter 的 doFilter 方法都执行完之后将会执行到第 63 行,在接下来的第 92 行、第 95 行即调用 Servlet 的 service 方法。 


推荐阅读
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 标题: ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
author-avatar
漂浪男孩2010_218
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有