转载自ph0ly:http://www.ph0ly.com
一、WebAppContext的概念
WebAppContext是Jetty处理器中核心的Handler,集成了众多其他基础Handler,并提供标准的JavaEE类加载机制
二、应用场景
WebAppContext提供完善的Session、Servlet、Filter的处理逻辑,它可以帮助我们构建一个标准的J2EE Web服务
三、继承体系
可以看到WebAppContext是一个ServletContextHandler,同时实现了WebAppClassLoader.Context接口,完成系统类判断和控制类加载顺序
四、源码剖析
1 、WebAppContext的创建
构造函数重载比较多,我们看到options默认会包含SESSIONS和SECURITY,也就是默认会启用SessionHandler和SecurityHandler
可以看到作为一个传统的war形式的路径传入,WebAppContext也能解析
最终其他构造函数全部调用到这里,父类ServletContextHandler会先初始化,之后创建WebAppContext.Context,这个类其实就是在ServletContextHandler.Context基础之上扩展了一些Listener操作,这里就不在多说
然后就是异常处理器和保护路径,保护路径即Servlet规范要求的WEB-INF、META-INF目录了
2、WebAppContext的启动
前面是处理MetaData,这个类在HttpParser解析这块用的比较多,这里就不细说
主要来看preConfigure和postConfigure
第一步完成配置加载,主要是实例化默认的web-inf、web-xml、meta-inf等配置的类
第二步完成系统类加载,主要是初始化系统类名称,例如java、javax、org.xml、org.w3c等开头的包名或者具体的类
第三步完成服务端类加载,主要是初始化服务端类名称,例如org.eclipse.jetty开头的类(也有部分排除)
这里为什么会区分系统类、服务端类呢?待会儿我们一起来分析下WebAppClassLoader就明白了
如果这里没有设置过类加载器,则自动创建一个WebAppClassLoader
配置刚才的所有配置,调用了preConfigure,预配置
postConfigure比较简单,就是前面的configurations调用postConfigure,这里不多讲
3、WebAppClassLoader的实现
可以看到它是一个URLClassLoader,我们来看它最核心的方法loadClass
先来看下Servlet规范要求,摘自Jetty官网(http://www.eclipse.org/jetty/documentation/9.3.x/jetty-classloading.html)
1. 默认优先加载WEB-INF/lib、WEB-INF/classes,其次是父类加载器
2. 系统类如java.lang.String不需要webapp类加载器加载
3. 服务端类如Server类不应该对应用程序类加载器可见,即应用程序不应该去加载Web容器的类
======== 再回头看代码 =========
这里对同一名称的全类名类加锁,不言而喻,防止重复加载
首先是常规操作,是否加载过了,加载过就直接返回
然后就是根据是否以父类加载模式方式加载,即双亲委派模型加载(默认不是双亲委派模式)
如果是父类加载器优先:
首先调用父类加载器加载类,如果当前类不是服务端类,才返回,也就是说大部分jetty自有的类是不允许使用父类加载器
父类加载器未找到该类,则尝试webapp自身的加载方式加载
如果是应用类加载器优先:
如果不是系统类,则将类文件读入并定义类,加载完成
如果当前是系统类,例如java本身的类或者部分jetty自身实现的类,则会使用父类加载器加载,如果父类加载器仍然加载不到,会使用webapp自身加载方式尝试加载,加载不到就抛ClassNotFoundException
相信看完WebAppClassLoader后,大家应该清楚之前的systemClasses和serverClasses是干什么的了
顺便推荐一篇文章 血泪的Jetty ClassLoader
4、WebAppContext.startContext启动上下文
该方法在ContextHandler.doStart触发,这里首先configure,完成各配置的configure,由于不属于核心,这里不细讲
metadata元数据比较简单,读者可以自行下来查看
之后启动webapp,其实就是调用了ServletContextHandler.startContext
五、总结
WebAppContext作为Jetty核心Handler,是我们构建Jetty标准Web必选的处理器。对于Spring Boot会在创建Server后,再创建一个JettyEmbeddedWebAppContext(其实是一个WebAppContext),从而Spring Boot能基于Jetty构建一个完整的Web应用。至此Jetty常规的传统Handler已完成分析,当然还有很多细节,感兴趣的读者下来可以自己分析,同时欢迎留言交流。后期我会增加WebSocket、Http2协议的相关知识,欢迎继续关注~