ServletContainerInitializer加载机制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ServletContainerInitializer加载机制相关的知识,希望对你有一定的参考价值。

参考技术A 最近优化项目架构针对Logback日志框架需要结合Spring的profiles配置文件达到不同运行环境自动配置参数.

最开始想法是使用ServletContextListener监听器实现在项目初始化时读取Spring的profiles配置文件手动放入当前环境变量中但在测试过程中发现问题!

环境变量还没有开始加载, Logback框架已经开始被加载!

跟踪日志信息发现一个类LogbackServletContainerInitializer, Logback是通过该类进行Web环境初始化的!该类实现了javax.servlet.ServletContainerInitializer接口

第一次接触ServletContainerInitializer查找了一下相关规范, 于是自己也写了一个ServletContainerInitializer接口的类LogInitializer在但发现放置WebContent/META-INF/services/javax.servlet.ServletContainerInitializer文件无法被读取, 网上说只有放置在WebContent/WEB-INF/lib下的Jar包中才能被读取!

单独建立一个新的项目通过Maven引入到原有项目中, 忽然发现LogbackServletContainerInitializer和LogInitializer存在先后加载顺序问题, 经过几次测试发现跟Jar包名称的先后顺序有关, 于是开始跟踪Tomcat运行时的Debug调试看看Tomcat是怎么加载ServletContainerInitializer实例的!

开发工具: IDEA 2017.2.6版

Servlet容器: Tomcat 8.5.20

在LogInitializer#onStartup()方法第一行打断点等待

进入断点后观察调用堆栈, 发现是org.apache.catalina.core.StandardContext#startInternal() 调用了LogInitializer#onStartup()

进入org.apache.catalina.core.StandardContext#startInternal()方法, 发现一个成员变量迭代调用

跟踪成员属性 initializers 发现是一个链表哈希映射

继续根据 initializers 被调用情况, 发现org.apache.catalina.core.StandardContext#addServletContainerInitializer()向成员属性中放入实例

查找org.apache.catalina.core.StandardContext#addServletContainerInitializer()方法被调用情况, 发现在org.apache.catalina.startup.ContextConfig#webConfig()方法中一个成员属性迭代调用

跟踪 initializerClassMap 成员属性发现类似于刚才的initializers

跟踪 initializerClassMap 被放入对象, 发现org.apache.catalina.startup.ContextConfig#processServletContainerInitializers()方法

进入org.apache.catalina.startup.WebappServiceLoader#load()方法观察ServletContainerInitializer实例被加载情况

观察了一下 javax.servlet.ServletContext.ORDERED_LIBS 静态常量发现与web.xml中的<absolute-ordering>元素相关, 因为没有配置所以先不考虑, 这样判断 orderedLibs!=null 不会被执行类加载器loader的引用就是当前项目Servlet容器的类加载器去加载资源

查找到五个资源, Tomcat容器自身2个, LogInitializer, LogbackServletContainerInitializer, SpringServletContainerInitializer(Spring的)  这个时候已经明了ServletContainerInitializer一般情况下是基于类加载器加载资源的枚举迭代顺序, 因为委托加载机制会从最顶级类加载向下一直加载到当前项目Web容器类加载器下的资源, 到此基本已经弄清楚ServletContainerInitializer加载流程!

因为看见了 ORDERED_LIBS 常量所以想继续研究一下<absolute-ordering>元素, 大概看了一下相关规范文档定义, 该元素是Servlet3.0出现的为第三方插件Jar包自动配置一些Servlet, Filter时所有第三方插件按照指定绝对顺序加载Jar包资源

1. 在Jar包的META-INF目录下建立web-fragment.xml

2, 配置web.xml

在org.apache.catalina.startup.WebappServiceLoader#load()方法打断点, 发现找到一个jar包

将当前项目 Web容器类加载器 替换为 Tomca容器类加载器 是因为委托加载机制从最顶级类加载器加载到当前类加载器所以会导致当前项目Web容器不会被加载到, 就会跳过当前项目下WEB-INF/lib中的Jar包查找所以只有log-initializer-0.1.jar中的ServletContainerInitializer被加载,  LogbackServletContainerInitializer, SpringServletContainerInitializer的配置丢失

不符合预期结果, 继续看<absolute-ordering>文档, 发现一个<others />元素, 在<absolute-ordering>中追加

重新开始在org.apache.catalina.startup.WebappServiceLoader#load()方法断点Debug

这次就有所有Jar包了, 但很好奇去项目部署的Tomcat下WEB-INF/lib 看了一下Jar包数量 44个  而非37个逐一匹配发现缺少这些Jar包

非常好奇为什么会丢失Jar包, 如果只是丢失apache相关的也可能Tomcat排除自身lib下相关的, 但是slf4j, aspecj居然也没有, 本着求知的好奇心继续调试, 找出缺少Jar包的原因

因为是通过 ORDERED_LIBS 常量获取的容器属性值, 所以直接在org.apache.catalina.core.ApplicationContext#setAttribute()使用条件断点

查看堆栈信息, 发现被org.apache.tomcat.util.descriptor.web.WebXml#orderWebFragments()方法调用

跟踪局部变量 orderedFragments

发现是局部变量 fragments 映射迭代放入, 而 fragments 是由外部传递

继续向上跟踪堆栈

找到org.apache.tomcat. JarScanner 实现类org.apache.tomcat.util.scan. StandardJarScanner #scan()方法

找到org.apache.tomcat. JarScanFilter 实现类org.apache.tomcat.util.scan. StandardJarScanFilter #check()方法

至此基本上算是了解ServletContainerInitializer加载机制!

IE6 css 设置PNG背景图片透明问题。

参考技术A 目标背景样式只能用这种方法去做
background:url("images/search_inpbg.png")
no-repeat
0
0;*background:
none
transparent
scroll
repeat
0%
0%;
FILTER:
progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/search_inpbg.png',
sizingMethod='scale');
滤镜里面的图片是相对于你页面的路径。而不是样式的路径
。。。。你可以去看下我的
http://www.ok22.org
指向视频
会显示播放效果..你可以把我的样式下载下来看下。加粗是你应该理解的地方。其实滤镜的图片要刚刚跟你控制的DIV大小一样才行。。(除非你用的是一个的没有过渡效果透明PNG)这样才不会变形

以上是关于ServletContainerInitializer加载机制的主要内容,如果未能解决你的问题,请参考以下文章