《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://ericcenblog.com/2017/08/13/jettyyuan-ma-pou-xi-xi-lie-2-web-xmlde-jie-xi-yu-zhi-xing/ 「Eric Cen」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

当用Jetty来启动一个web项目的时候, Jetty会去读取war包的WEB-INFO目录里面的web.xml文件,然后解析这个web.xml。那这一步是什么时候进行和整个过程是怎样的?我们一起来分析一下。

以用Jetty Runner来启动一个war包为例, 在org.eclipse.jetty.runner.Runner类的configure方法里,我们看到它先构建出一个WebAppContext:img

上面的代码中我们要注意webapp.setConfigurationClasses(_plusConfigurationClasses)这一句,我们先来看看_plusConfigurationClasses是什么东西:img我们可以看到就是一个String数组,里面包含了各种Configuration类的类名,这里我们要注意的是WebXmlConfiguration这个类,一看名字就知道是跟web.xml有关的:)。我们再来看setConfigurationClasses这个方法做了什么:imgimgimgimg可以看到会把_plusConfigurationClasses里面的所有类都通过反射创建出一个实例对象来,其中包括了WebXmlConfiguration

接下来我们要跳去看WebAppContextdoStart方法————至于是怎样调用到这个方法,我要另起一篇文章来剖析,简单说一下,就是,WebAppContext是实现了Handler接口,同时也实现了LifeCycle接口,而每个Handler又是被当成org.eclipse.jetty.server.Server(Jetty HTTP Servlet Server)的内部管理的一个Bean,当Server start的时候,这些Bean也会被start,然后再调用每个bean的doStart方法。 WebAppContext.doStart()img我们可以看到这个调用了preConfigure方法,我们对叫这种名字的方法一定不能放过,我们再进入WebAppContextpreConfigure方法里,看到它先是做了一大堆工作,我们暂且不管,重要的是它执行以下代码:_configurations.preConfigure(this); 这个是调用了ConfigurationspreConfigure方法:img可以看到,这个方法其实是遍历了上文中的_plusConfigurationClasses所有Configuration实例对象,然后调用它的preConfigur方法,这里我们只关注WebXmlConfiguration的preConfigure方法:img看到上面调用的findWebXml方法没?到这里,我们终于找到了Jetty是在什么时候和哪里去加载web.xml这个文件!img这里我们要注意的是它调用了WebAppContextgetWebInf方法:img我们可以看到,它会去调用getBaseResource方法,得出一个路径,然后再拼接”WEB-INF”目录,那么这个BaseResource又是什么时候设置的呢?其实它是在另外一个Configuration类,WebInfConfigurationpreConfigure方法时设置的: WebInfConfiguration.preConfigure():imgWebInfConfigurationpreConfigure方法里,它会在tmp创建一个目录,然后将war包解压出来,这个解压出来的目录就会被设置为BaseResource。 这样WebXmlConfiguration就会把找出来的web.xml设置给WebAppContextMetaData:img我们继续看WebAppContextdoStart方法,执行完preConfigure方法后,它就会调用super.doStart(),而实际上在super.doStart方法里,又会调用子类的startContext方法,我们看WebAppContextstartContext方法:img终于到了configure了:imgConfigurations.configure():img同样是遍历了所有Configuration,然后调用它的configure方法,这里我们同样只关注WebXmlConfigurationWebXmlConfiguration.configure():img我们可以看到它给WebAppContextMetaData添加了一个叫StandardDescriptorProcessorDescriptorProcessor,那我们看看这个StandardDescriptorProcessor是个什么东西:imgimgimg

看到listener,filter-mapping,servlet,servlet-mapping这些是不是觉得似曾相识?这些不就是web.xml文件里我们常见的标签吗?!再看registerVisitor方法:img看到这里,我们大概可以猜出它里面实现的东西了,就是注册了当解析到web.xml里面的某个node时候,应该调用StandardDescriptorProcessor的方法,比如当解析到listener这个标签的时候,应该调用visitListener方法:imgimg我们可以看到,它会从web.xml里面,解析出listener-class,然后通过反射创建出一个listener的实例,然后把它加到WebAppContextEventListener列表里。这些是在WebAppContextstartContext方法里执行完configure后,调用了MetaDataresolve方法来进行的,MetaData.resolve():imgStandardDescriptorProcessor调用的是它的父类IterativeDescriptorProcessorprocess方法imgimg嗯,看到这里,已经证实我们前文的猜测,解析到web.xml的某个tag时,就会通过反射调用前文注册的StandardDescriptorProcessor的相应方法!

在回到前文,把在web.xml中解析出来的Listener加到EventListener列表里做什么呢?我们回过头来继续看WebAppContextstartContext方法,在configure完和resolve完metadata后,它会调用super.startContext(),也就是它的父类ServletContextHandlerstartContext方法,而ServletContextHandler又会继续调用它自己的父类的startContext()方法,也就是ContextHandlerstartContext()方法:img我们这时可以看到它会遍历context里的EventListener列表里的Listener,然后调用它的contextInitialized方法:img。 如果我们在web.xml里配置了如下Listenerimg那我们看看Spring的这个常见的ContextLoaderListener的contextInitialized方法:img它调用了父类ContextLoaderinitWebApplicationContext方法, ContextLoader.initWebApplicationContext:imgimgimg我们看到它尝试去从ServletContext(在本文是指WebAppContext这个子类)的initParameter中get到一个key为CONTEXT_CLASS_PARAM(“contextClass”)的值,这些initParameter都是在解析web.xml时设置进去的,如果我们在web.xml里面没有配置contextClass,那这个contextClassName就为null,接着它就会尝试去defaultStrategies里面找,img

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

我们可以看到它是到classpath里查找一个叫ContextLoader.properties的文件,在它的classpath的spring-web.jar里面找到这个文件,它里面内容如下:org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext找出这个default的XmlWebApplicationContext的全类名后,就会加载这个类,然后通过反射创建这个类的一个实例对象, 再回到ContextLoaderinitWebApplicationContext方法:img因为XmlWebApplicationContextConfigurableWebApplicationContext的一个实现类,所以我们会进入ContextLoaderconfigureAndRefreshWebApplicationContext方法,imgimg我们可以看到它会到ServletContextInitParameter里面找一个key为contextConfigLocation的参数,它同样也是在解析web.xml时得到的,其实它就是设置了springcontext配置文件,比如:img找到这个配置文件的位置后,就会开始Springcontext的初始化!

到此,本文剖析了Jetty解析和执行web.xml的过程,主要是分析了listener标签和context-param标签,其他标签,如Servletservlet-mapping,filter这些标签,也有着类似的解析过程,我之后继续另起文章剖析!

文章目录