【Tomcat源码学习】-3.应用管理
通过上一节我们完成了对容器进行了加载、初始化、启动,而对于应用的加载部分独立出来,本节进行单独的讲解
一、应用加载流程
1)应用识别,Context创建
2)应用加载,Context启动
- 在Host启动后,会调用Host的监听HostConfig进行启动事件处理
- HostConfig在监听到启动事件后,会分别尝试从context.xml下,wabapps下war,webapp下文件夹进行应用的读取,读取过程中通过Host的启停线程池(startStopExecutor)进行独立线程读取,读取最终结果生成Context对象并添加进入Host容器中。注意这里用到了Future<?>协同方式
- 这里构建的Context仅包含如下基本参数(如:name,path,webappVersion,docBase),另外会给Context默认增加监听ContextConfig
- Engine启动ContainerBackgroundProcessor线程进行对所有容器进行后台处理,即调用所有容器的backgroundProcess()方法
- 容器backgroundProcess()方法处理完成后,通知相应的监听器(事件类型为Lifecycle.PERIODIC_EVENT)
- HostConfig会根据Context是否启动,进行相应启动操作(context.start()),
- Context启动过程如下:
- 获取一个WebAppLoader,
- 应用类加载,执行WebAppLoader.start()方法,该方法内,会创建一个ClassLoader(WebappClassLoader),在调用ClassLoader.start()方法进行jar与class的加载
- 添加应用级监听器至Context成员变量中,其中主要有:ServletContextAttributeListener,ServletRequestAttributeListener,ServletRequestListener,HttpSessionIdListener,HttpSessionAttributeListener,ServletContextListener,Context成员变量声明如下:
private List<Object> applicationEventListenersList = new CopyOnWriteArrayList<>(); - 执行SevletContextListener监听器上下文完成初始化方法(注意,Spring就是这里进行的初始化的)
- Context启动完成后,通知相应的监听器ContextConfig,进行启动完成处理
- ContextConfig.start()方法(核心为调用webConfig()方法)
- 读取/WEB-INF/web.xml文件,构建WebXml对象
- 解析所有class,判断是否有使用servlet相关注解(Ljavax/servlet/annotation/WebServlet),如果使用就注解对象手动添加至WebXml对象中,即生成ServletDef定义文件
- 将jsp转换为ServletDef定义文件,并添加至WebXml对象中 将WebXml对象内容解析至Context对象中(方法:configcontext),一个Servlet对应一个Wrapper容器
for (Entry<String, String> entry : webxml.getContextParams().entrySet()) { context.addParameter(entry.getKey(), entry.getValue()); }//context全局参数 .... for (ErrorPage errorPage : webxml.getErrorPages().values()) { context.addErrorPage(errorPage); }//404页面 for (FilterDef filter : webxml.getFilters().values()) { if (filter.getAsyncSupported() == null) { filter.setAsyncSupported("false"); } context.addFilterDef(filter); }//拦截器 for (FilterMap filterMap : webxml.getFilterMappings()) { context.addFilterMap(filterMap); }//拦截器Map context.setJspConfigDescriptor(webxml.getJspConfigDescriptor()); for (String listener : webxml.getListeners()) { context.addApplicationListener(listener); }//监听器 ...... for (ServletDef servlet : webxml.getServlets().values()) { Wrapper wrapper = context.createWrapper();//一个servlet对应一个封装器Wrapper // Description is ignored // Display name is ignored // Icons are ignored // jsp-file gets passed to the JSP Servlet as an init-param if (servlet.getLoadOnStartup() != null) { wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue()); } if (servlet.getEnabled() != null) { wrapper.setEnabled(servlet.getEnabled().booleanValue()); } wrapper.setName(servlet.getServletName()); Map<String,String> params = servlet.getParameterMap(); for (Entry<String, String> entry : params.entrySet()) { wrapper.addInitParameter(entry.getKey(), entry.getValue()); } wrapper.setRunAs(servlet.getRunAs()); Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs(); for (SecurityRoleRef roleRef : roleRefs) { wrapper.addSecurityReference( roleRef.getName(), roleRef.getLink()); } wrapper.setServletClass(servlet.getServletClass()); MultipartDef multipartdef = servlet.getMultipartDef(); if (multipartdef != null) { if (multipartdef.getMaxFileSize() != null && multipartdef.getMaxRequestSize()!= null && multipartdef.getFileSizeThreshold() != null) { wrapper.setMultipartConfigElement(new MultipartConfigElement( multipartdef.getLocation(), Long.parseLong(multipartdef.getMaxFileSize()), Long.parseLong(multipartdef.getMaxRequestSize()), Integer.parseInt( multipartdef.getFileSizeThreshold()))); } else { wrapper.setMultipartConfigElement(new MultipartConfigElement( multipartdef.getLocation())); } } if (servlet.getAsyncSupported() != null) { wrapper.setAsyncSupported( servlet.getAsyncSupported().booleanValue()); } wrapper.setOverridable(servlet.isOverridable()); context.addChild(wrapper); } for (Entry<String, String> entry : webxml.getServletMappings().entrySet()) { context.addServletMapping(entry.getKey(), entry.getValue()); } //sesseionConfig对象 SessionConfig sessionConfig = webxml.getSessionConfig(); if (sessionConfig != null) { if (sessionConfig.getSessionTimeout() != null) { context.setSessionTimeout( sessionConfig.getSessionTimeout().intValue()); } SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig(); scc.setName(sessionConfig.getCookieName()); scc.setDomain(sessionConfig.getCookieDomain()); scc.setPath(sessionConfig.getCookiePath()); scc.setComment(sessionConfig.getCookieComment()); if (sessionConfig.getCookieHttpOnly() != null) { scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue()); } if (sessionConfig.getCookieSecure() != null) { scc.setSecure(sessionConfig.getCookieSecure().booleanValue()); } if (sessionConfig.getCookieMaxAge() != null) { scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue()); } if (sessionConfig.getSessionTrackingModes().size() > 0) { context.getServletContext().setSessionTrackingModes( sessionConfig.getSessionTrackingModes()); } }
- 从WebApp加载中读取ServletContainerInitializer的实现,并添加至context容器中
至此,host下面的context,wrapper,等容器就就已经生成并初始化了。