扫码关注公众号:芋道源码

发送: 百事可乐
获取永久解锁本站全部文章的链接

《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这些标签,也有着类似的解析过程,我之后继续另起文章剖析!

文章目录