[KANMARS原创] - JETTY服务器WEBAPPCONTEXT详解
<
1.new WebAppContext()
1.1 private MetaData _metadata=new MetaData();
1.2 调用构造方法
ServletContextHandler(HandlerContainer parent, String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler)
初始化 ContextHandler._scontext = new Context();
设置 _sessionHandler 即使为空
设置 _securityHandler 即使为空
设置 _servletHandler 即使为空
2、经server._handler._beans.start调用 – >WebAppContext(AbstractLifeCycle).start()
2.1 WebAppContext._metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
2.2 WebAppContext.doStart()
2.2.1 WebAppContext.preConfigure() //预先配置
2.2.1.1 WebAppContext.loadConfigurations //加载配置信息-》
用sun.misc.Launcher$AppClassLoader的类加载器预先加载储存于dftConfigurationClasses的class信息,并且初始化到_configurations[i]中
_configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance();
这些类包括:
”org.eclipse.jetty.webapp.WebInfConfiguration” WEBINFO文件解压等处理
”org.eclipse.jetty.webapp.WebXmlConfiguration” WebXml配置文件读取
”org.eclipse.jetty.webapp.MetaInfConfiguration” 根据webxml生成filter,servlet等对象
”org.eclipse.jetty.webapp.FragmentConfiguration” 未知,但似乎没什么用
”org.eclipse.jetty.webapp.JettyWebXmlConfiguration” 未知,但似乎某种情况下会起到什么用处
2.2.1.2 WebAppContext.loadSystemClasses //加载系统类
设置private ClasspathPattern _systemClasses = new ClasspathPattern(dftSystemClasses);
而dftSystemClasses=
public final static String[] dftSystemClasses =
{
“java.”, // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
“javax.”, // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
“org.xml.”, // needed by javax.xml
“org.w3c.”, // needed by javax.xml
“org.apache.commons.logging.”, // TODO: review if special case still needed
“org.eclipse.jetty.continuation.”, // webapp cannot change continuation classes
“org.eclipse.jetty.jndi.”, // webapp cannot change naming classes
“org.eclipse.jetty.plus.jaas.”, // webapp cannot change jaas classes
“org.eclipse.jetty.websocket.WebSocket”, // WebSocket is a jetty extension
“org.eclipse.jetty.websocket.WebSocketFactory”, // WebSocket is a jetty extension
“org.eclipse.jetty.websocket.WebSocketServlet”, // webapp cannot change WebSocketServlet
“org.eclipse.jetty.servlet.DefaultServlet” // webapp cannot change default servlets
} ;
2.2.1.3 WebAppContext.loadServerClasses() //加载服务类
设置private ClasspathPattern _serverClasses = new ClasspathPattern(dftServerClasses);
而dftServerClasses=
public final static String[] dftServerClasses =
{
“-org.eclipse.jetty.continuation.”, // don’t hide continuation classes
“-org.eclipse.jetty.jndi.”, // don’t hide naming classes
“-org.eclipse.jetty.plus.jaas.”, // don’t hide jaas classes
“-org.eclipse.jetty.websocket.WebSocket”, // WebSocket is a jetty extension
“-org.eclipse.jetty.websocket.WebSocketFactory”, // WebSocket is a jetty extension
“-org.eclipse.jetty.websocket.WebSocketServlet”, // don’t hide WebSocketServlet
“-org.eclipse.jetty.servlet.DefaultServlet”, // don’t hide default servlet
“-org.eclipse.jetty.servlet.listener.”, // don’t hide useful listeners
“org.eclipse.jetty.” // hide other jetty classes
} ;
2.2.1.4 创建WebAppClassLoader WebAppClassLoader classLoader = new WebAppClassLoader(this);
2.2.1.4.1 按照如下构造参数顺序构造URLClassLoader
WebAppClassLoader构造参数中的parent->Classloader
Thread.currentThread().getContextClassLoader()
WebAppClassLoader.class.getClassLoader()
ClassLoader.getSystemClassLoader()
2.2.1.5 WebAppContext.preConfigure(). 最后一步 configurations[i].preConfigure(this);调用2.2.1.1生成的对象中的preConfigure方法
2.2.1.5.1 WebInfConfiguration.findWorkDirectory() 查找context是否已经设置了webINF的参数
//如果有,就直接使用
2.2.1.5.2 WebInfConfiguration.resolveTempDirectory().makeTempDirectory //在用户的个人临时文件夹目录创建文件夹 new File(System.getProperty(“java.io.tmpdir”))
context.setTempDirectory(tmpDir);
文件夹的创建规则,可以参考 resolveTempDirectory方法的文档
A. Try to use an explicit directory specifically for this webapp:
Iff an explicit directory is set for this webapp, use it. Do NOT set delete
on exit.
Iff javax.servlet.context.tempdir context attribute is set for this webapp
&& exists && writeable, then use it. Do NOT set delete on exit.
B. Create a directory based on global settings. The new directory will be
called “Jetty“+host+”“+port+”__”+context+”“+virtualhost Work out where to
create this directory:
Iff $(jetty.home)/work exists create the directory there. Do NOT set delete
on exit. Do NOT delete contents if dir already exists.
Iff WEB-INF/work exists create the directory there. Do NOT set delete on
exit. Do NOT delete contents if dir already exists.
Else create dir in $(java.io.tmpdir). Set delete on exit. Delete contents if
dir already exists.
2.2.1.5.3 WebInfConfiguration.unpack (context);
2.2.1.5.3.1 web_app = context.newResource(war);
2.2.1.5.3.1.1 return Resource.newResource(urlOrPath); URL = file.toURI().toURL() file:/E:/webapp.war 返回了一个FileResources
2.2.1.5.3.2 Resource jarWebApp = JarResource.newJarResource(web_app); 将FileResources转化为一个jarsource 采用方式为将file:/E:/webapp.war修改为jar:file:/E:/webapp.war~/
2.2.1.5.3.3 context.getTempDirectory(), “.extractlock”位置生成一个控制锁
2.2.1.5.3.4 extractedWebAppDir.mkdir(); extractedWebAppDir即为临时文件夹
例如C:\Documents and Settings\Administrator\Local Settings\Temp\jetty-0.0.0.0-8080-webapp.war--any-\webapp
2.2.1.5.3.5 jar_web_app.copyTo(extractedWebAppDir); 将jar资源,即war包进行解压,拷贝到extractedWebAppDir即临时目录中
2.2.1.5.3.6 删除控制锁context.getTempDirectory(), “.extract_lock”
2.2.1.5.3.7 context.setBaseResource(web_app); 设置context的基础资源位置是刚才申请的临时文件夹
2.2.1.5.4 对父类的container进行处理 把父类依赖的jar包加入当前的上下文classloader中 //Apply ordering to container jars - if no pattern is specified, we won’t match any of the container jars
最终执行的方法context.getMetaData().addContainerJar(Resource.newResource(uri));
2.2.1.5.5 对WEBINF的jar包进行处理
最终执行的方法context.getMetaData().addWebInfJar(Resource.newResource(uri));
2.2.1.5.5.1 List<Resource> jars = findJars(context); 获取到web-inf/lib目录下的所有jar包
调用2.2.1.5.5执行webInfJarNameMatcher.match(webInfPattern, uris, true)
最终调用context.getMetaData().addWebInfJar(Resource.newResource(uri));,将web-inf/lib下的jar包或者zip包加入classpath
2.2.1.5.6 WebXmlConfiguration.preConfigure 预处理webxml
2.2.1.5.6.1 String defaultsDescriptor = context.getDefaultsDescriptor(); 解析 webdefault.xml org/eclipse/jetty/webapp/webdefault.xml
2.2.1.5.6.2 context.getMetaData().setDefaults (dftResource); 给MetaData设置webdefault.xml配置
2.2.1.5.6.2.1 MetaData._webDefaultsRoot = new DefaultsDescriptor(webDefaults);
2.2.1.5.6.2.1.1 _webDefaultsRoot.parse(); 解析webdefault.xml
2.2.1.5.6.2.1.1.1 webDescriptor.newParse() 新建一个XmlParser xmlParser=new XmlParser() 底层为SAXParserFactory
2.2.1.5.6.2.1.1.2 在xmlParse中注册dtd和URL的关系
2.2.1.5.6.2.1.1.3 webDescriptor._root = _parser.parse(_xml.getInputStream()) 将webdefault.xml解析为一个Node对象
2.2.1.5.6.2.1.1.4 webDescriptor.processVersion ,解析webdefault.xml中的版本号、metadata-complete(是否启用注解)
2.2.1.5.6.2.1.1.5 webDescriptor.processOrdering 解析如下参数
absolute-ordering 是否WebFragment要排序
2.2.1.5.6.3 Resource webxml = findWebXml(context); 寻找web.xml,解析,但是不执行
2.2.1.5.6.4 //parse but don’t process override-web.xml
2.2.1.5.7 MetaInfConfiguration.preConfigure 预处理MetaINF
2.2.1.5.7.1 //Merge all container and webinf lib jars to look for META-INF resources 将所有ContainerJars和WEBINF jars进行合并
2.2.1.5.7.2 JarScanner scanner新建jar包扫描类
2.2.1.5.7.3 scanner扫描WEBINF下的文件,如果是:web-fragment.xml和META-INF/resources/或者以DTD结尾,则分别加载到
addResource(context,METAINF_FRAGMENTS,Resource.newResource(jarUri));
addResource(context,METAINF_RESOURCES,Resource.newResource(“jar:”+jarUri+”!/META-INF/resources”));
addResource(context,METAINF_TLDS,Resource.newResource(“jar:”+jarUri+”!/“+name));
中
2.2.1.5.8 FragmentConfiguration.preConfigure 预处理Fragment
2.2.1.5.8.1 findWebFragments(context, context.getMetaData());,最终完成的是
metaData.addFragment(frag, Resource.newResource(frag.getURL()+”/META-INF/web-fragment.xml”));
metaData.addFragment(frag, Resource.newResource(“jar:”+frag.getURL()+”!/META-INF/web-fragment.xml”));
2.2.1.5.9 JettyWebXmlConfiguration.preConfigure 预处理jetty的webxml 空
2.2.1.6 WebAppContext.super.doStart() ContextHandler.doStart()
2.2.1.6.1 设置当前线程的类加载器为WebAppClassLoader
2.2.1.6.2 设置 contexthandler._mimeTypes = new MimeTypes()
2.2.1.6.3 设置当前线程的线程局部变量context.set(_scontext);
2.2.1.6.4 调用startContext方法,即调用WebAppContext.startContext()
2.2.1.6.4.1 WebAppContext.configure()
2.2.1.6.4.2 调用2.2.1.1五个变量的configure方法
”org.eclipse.jetty.webapp.WebInfConfiguration” WEBINFO文件解压等处理
”org.eclipse.jetty.webapp.WebXmlConfiguration” WebXml配置文件读取
”org.eclipse.jetty.webapp.MetaInfConfiguration” 根据webxml生成filter,servlet等对象
”org.eclipse.jetty.webapp.FragmentConfiguration” 未知,但似乎没什么用
”org.eclipse.jetty.webapp.JettyWebXmlConfiguration” 未知,但似乎某种情况下会起到什么用处
2.2.1.6.4.2.1 WebInfConfiguration.configure
2.2.1.6.4.2.1.1 ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes); 将/WEB-INF/classes增加到classpath中 最终增加到了URLClassLoader中的 private final URLClassPath ucp;上
2.2.1.6.4.2.1.2 ((WebAppClassLoader)context.getClassLoader()).addJars(lib); 将/WEB-INF/lib中的jar包或者zip包增加到classpath中, 最终增加到了URLClassLoader中的 private final URLClassPath ucp;上
2.2.1.6.4.2.1.3 Look for extra resource 查找扩展资源
2.2.1.6.4.2.2 WebXmlConfiguration.configure
2.2.1.6.4.2.2.1 context.getMetaData().addDescriptorProcessor(new StandardDescriptorProcessor()); 为MetaData增加webxml描述的处理器,是StandardDescriptorProcessor
2.2.1.6.4.2.2.2 new StandardDescriptorProcessor()时,需要注册一系列解析器
registerVisitor(“context-param”, this.getClass().getDeclaredMethod(“visitContextParam”, signature));
registerVisitor(“display-name”, this.getClass().getDeclaredMethod(“visitDisplayName”, signature));
registerVisitor(“servlet”, this.getClass().getDeclaredMethod(“visitServlet”, signature));
registerVisitor(“servlet-mapping”, this.getClass().getDeclaredMethod(“visitServletMapping”, signature));
registerVisitor(“session-config”, this.getClass().getDeclaredMethod(“visitSessionConfig”, signature));
registerVisitor(“mime-mapping”, this.getClass().getDeclaredMethod(“visitMimeMapping”, signature));
registerVisitor(“welcome-file-list”, this.getClass().getDeclaredMethod(“visitWelcomeFileList”, signature));
registerVisitor(“locale-encoding-mapping-list”, this.getClass().getDeclaredMethod(“visitLocaleEncodingList”, signature));
registerVisitor(“error-page”, this.getClass().getDeclaredMethod(“visitErrorPage”, signature));
registerVisitor(“taglib”, this.getClass().getDeclaredMethod(“visitTagLib”, signature));
registerVisitor(“jsp-config”, this.getClass().getDeclaredMethod(“visitJspConfig”, signature));
registerVisitor(“security-constraint”, this.getClass().getDeclaredMethod(“visitSecurityConstraint”, signature));
registerVisitor(“login-config”, this.getClass().getDeclaredMethod(“visitLoginConfig”, signature));
registerVisitor(“security-role”, this.getClass().getDeclaredMethod(“visitSecurityRole”, signature));
registerVisitor(“filter”, this.getClass().getDeclaredMethod(“visitFilter”, signature));
registerVisitor(“filter-mapping”, this.getClass().getDeclaredMethod(“visitFilterMapping”, signature));
registerVisitor(“listener”, this.getClass().getDeclaredMethod(“visitListener”, signature));
registerVisitor(“distributable”, this.getClass().getDeclaredMethod(“visitDistributable”, __signature));
这些解析器,既是web.xml的解析处理器
2.2.1.6.4.2.3 MetaInfConfiguration.configure 空
2.2.1.6.4.2.4 FragmentConfiguration.configure
2.2.1.6.4.2.4.1 context.getMetaData().orderFragments() 对Servlet3.0片段Fragment进行排序
—-需要看protected Ordering _ordering是否有值;
—-如果没有值,则只执行_orderedWebInfJars.addAll(_webInfJars);方法,将webINF的jar全部放到_orderedWebInfJars中
2.2.1.6.4.2.5 JettyWebXmlConfiguration.configure
2.2.1.6.4.2.5.1 在web-inf下寻找jetty8-web.xml,否则寻找web-jetty.xml,
XmlConfiguration jetty_config = (XmlConfiguration)context.getAttribute(XML_CONFIGURATION);
寻找到之后,调用jetty_config.configure(context);进行解析
2.2.1.6.4.3 WebAppContext._metadata.resolve(this); 对webAppContext进行解析处理
2.2.1.6.4.3.1 _origins.clear();
2.2.1.6.4.3.2 // set the webxml version 给context.getServletContext()设置版本号,即给_scontext设置版本号
2.2.1.6.4.3.3 执行StandardDescriptorProcessor p
2.2.1.6.4.3.3.1 p.process(context,getWebDefault()); 解析webdefault.xml 使用StandardDescriptorProcessor解析webdefault.xml中的标签
2.2.1.6.4.3.3.2 p.process(context,getWebXml()) 解析web.xml 使用StandardDescriptorProcessor解析web.xml中的标签
举例说明,servlet加载过程: 参见StandardDescriptorProcessor.protected void visitServlet(WebAppContext context, Descriptor descriptor, XmlParser.Node node)方法
1、获取ID
2、获取servlet-name
3、创建ServletHolder,设置servletName为 servlet-name
4、将ServletHolder设置入Context中的ServletHandler org.eclipse.jetty.servlet.ServletHandler 在步骤1.2或者首次调用getServletHandler时创建
5、解析init-param 并设置入ServletHolder
6、解析servlet-class
7、如果servlet-class是jspservlet,则设置初始化参数scratchdir
8、如果servlet-class不为空,则
8.1 ((WebDescriptor)descriptor).addClassName(servlet_class); 在WebDescriptor中增加一条记录
8.2 Origin o = context.getMetaData().getOrigin(servlet_name+”.servlet.servlet-class”); 尝试从缓存中获取servlet_name+”.servlet.servlet-class”
如果获取不到,则holder.setClassName(servlet_class);
context.getMetaData().setOrigin(servlet_name+”.servlet.servlet-class”, descriptor);
9、加载load-on-startup参数
10、加载security-role-ref参数
11、加载run-as参数
12、加载async-supported参数
13、加载enabled参数
14、加载multipart-config参数
2.2.1.6.4.3.3.3 p.process(context,wd); 解析WebDescriptor getOverrideWebs()
2.2.1.6.4.4 WebAppContext.super.startContext() 加载父类的startContext
2.2.1.6.4.4.1 将SessionHandler,SecurityHandler,ServletHandler组成管道队列
2.2.1.6.4.4.2 将sessionHandler作为第一个管道,设置入WebAppContext(handlerWrapper._handler)
2.2.1.6.4.4.3 WebAppContext(ServletContextHandler)super.startContext()
2.2.1.6.4.4.4 WebAppContext().super.doStart()->scopeHandler.dostart()->scopeHandler.doStart()—->>>>>>>>>>…….
2.2.1.6.4.4.4.1 sessionhandler.start()
2.2.1.6.4.4.4.2 ConstraintSecurityHandler.start()
2.2.1.6.4.4.4.3 ServletHandler.start()
2.2.1.6.4.4.4.3.1 updateNameMappings() 更新_servletNameMap和_servlets数组
2.2.1.6.4.4.4.3.2 updateMappings() 更新_filterPathMappings和_filterNameMappings,_servletPathMap,
2.2.1.6.4.4.5 WebAppContext(ContextHandler)._errorHandler.start();
2.2.1.6.4.4.6 callContextInitialized(((ServletContextListener)LazyList.get(_contextListeners, i)), event); 通知监听事件
2.2.1.6.4.4.7 结束 2.2.1.6.4.4.3 WebAppContext(ServletContextHandler)super.startContext()
2.2.1.6.4.4.8 OK to Initialize servlet handler now ServletContextHandler.startContext –> _servletHandler.initialize()
2.2.1.6.4.4.8.1 _filters.start()
2.2.1.6.4.4.8.2 servlets[i].start() ServletHolder.start
2.2.1.6.4.4.8.2.1 ServletHolder -> Holder -> Holder._class=Loader.loadClass(Holder.class, _className); 加载class类
2.2.1.6.4.4.8.2.2 checkServletType(); 检查servlet类型
2.2.1.6.4.4.8.2.3 initServlet(); 包括实例化,调用初始化方法等
_servlet=newInstance();
_config=new Config();
_servlet.init(_config);
2.2.1.6.5 结束2.2.1.6.4 WebContext.startContext()
2.2.1.6.6 current_thread.setContextClassLoader(old_classloader);将classloader恢复原样
2.2.1.7 WebAppContext.postConfigure()
WebInfConfiguration.postConfigure 空
WebXmlConfiguration.postConfigure空
MetaInfConfiguration.postConfigure空
FragmentConfiguration.postConfigure空
JettyWebXmlConfiguration.postConfigure空
—————————至此webAppContext启动完毕
作为Handler的运行过程如下,Servlet对象将请求交给webAppContext执行handler方法,WebAppContext含有三个handler链,顺序为sessionhandler,ConstraintSecurityHandler,ServletHandler
由于sessionhandler和ServletHandler是scopeHandler,而ConstraintSecurityHandler是普通Handler,因此执行顺序如下
3.1 sessionhandler.doScope() 创建session,对 cookie写出jsessionid
3.2 ServletHandler.doScope() 根据target,从_servletNameMap中获取到一个ServletHolder servlet_holder=
(ServletHolder)_servletNameMap.get(target);
设置到baseRequest.setUserIdentityScope(servlet_holder)中
3.3 sessionhandler.doHandler() 空,正常后延
3.4 ConstraintSecurityHandler.doHandler() 未知代码
3.5 ServletHandler.doHandler()
ServletHolder servlet_holder=(ServletHolder) baseRequest.getUserIdentityScope(); 公益企业ServletHolder
FilterChain 获取过滤器链
执行过滤器链
如果过滤器链执行完毕,执行servlet_holder.handle(baseRequest,req,res);
即:servlet.service(request,response);
———————————至此WebAppContext运行完毕
以上就是花三天时间整理的jetty-webAppContext运行过程
———–
对于我自己,要把代码忘掉,记住核心的部分。记得太多反而没用。
比如WebAppContext,最核心的部分应该是WebAppClassLoader类对不同的应用环境进行隔离的部分:
通过类加载器实现了不同应用在同一个进程内的妥善隔离,值得学习。这是一种非常有效的沙盒机制。
还有web.xml的解析部分,通过反射机制实现了每个标签的处理程序
但其中糟粕的地方也挺多,例如WebAppClassLoader被设置到了ThreadLocal变量中,这样意味着:服务器启动只能用单线程启动,否则就会异常。
虽然并没什么太大影响,但是仍然不太建议这么做。