Context启动
因为Context对应着应用,因此Context启动的主要部分就是部署应用
主要看下StandardContext的startInternal方法,因为该方法比较长,所以主要看几个关键的部分
protected synchronized void startInternal() throws LifecycleException {
// 这里会创建一个专门用来加载应用程序的Loader
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader();
webappLoader.setDelegate(getDelegate());
// 将这个Loader和Context关联起来
setLoader(webappLoader);
}
// 获取当前的线程上下文加载器
ClassLoader oldCCL = bindThread();
// 启动这个loader
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
}
// 将新创建的WebappLoader绑定到当前线程
unbindThread(oldCCL);
oldCCL = bindThread();
// 发送CONFIGURE_START_EVENT,这个事件会触发Wrapper的创建,并且和当前Context建立关联
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// 启动子容器,也就是Wrapper,每个Wrapper代表一个servlet
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// 启动pipeline
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// 恢复成以前的线程上下文加载器
unbindThread(oldCCL);
}
这里我们看下Context生命周期监听器ContextConfig在接收到CONFIGURE_START_EVENT时进行的操作
public void lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
// 会走这个分支
configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
}
protected synchronized void configureStart() {
// Called from StandardContext.start()
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.start"));
}
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.xmlSettings",
context.getName(),
Boolean.valueOf(context.getXmlValidation()),
Boolean.valueOf(context.getXmlNamespaceAware())));
}
// 解析web.xml文件,在这里会创建Wrapper
webConfig();
context.addServletContainerInitializer(new JasperInitializer(),null);
if (!context.getIgnoreAnnotations()) {
applicationAnnotationsConfig();
}
if (ok) {
validateSecurityRoles();
}
// Configure an authenticator if we need one
if (ok) {
authenticatorConfig();
}
// Dump the contents of this pipeline if requested
if (log.isDebugEnabled()) {
log.debug("Pipeline Configuration:");
Pipeline pipeline = context.getPipeline();
Valve valves[] = null;
if (pipeline != null) {
valves = pipeline.getValves();
}
if (valves != null) {
for (Valve valve : valves) {
log.debug(" " + valve.getClass().getName());
}
}
log.debug("======================");
}
// Make our application available if no problems were encountered
if (ok) {
context.setConfigured(true);
} else {
log.error(sm.getString("contextConfig.unavailable"));
context.setConfigured(false);
}
}
web.xml文件的解析
这里主要看下webConfig
WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
context.getXmlValidation(), context.getXmlBlockExternal());
Set<WebXml> defaults = new HashSet<>();
// getDefaultWebXmlFragment解析以下两个地方的web.xml文件
// 1. 全局的web.xml文件,位置在${tomcat目录}/resource/conf/web.xml
// 2. host层面的web.xml文件,位置在${tomcat目录}/resource/conf/Catalina/localhost/web.xml.default
defaults.add(getDefaultWebXmlFragment(webXmlParser));
WebXml webXml = createWebXml();
// Parse context level web.xml
// 解析context层级也就是应用层级的web.xml
// ${tomcat目录}/resource/webapps/${应用}/WEB-INF/web.xml
InputSource contextWebXml = getContextWebXmlSource();
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
ok = false;
}
ServletContext sContext = context.getServletContext();
// Ordering is important here
// Step 1. Identify all the JARs packaged with the application and those
// provided by the container. If any of the application JARs have a
// web-fragment.xml it will be parsed at this point. web-fragment.xml
// files are ignored for container provided JARs.
// 解析应用/WEB-INF/lib下的jar包,解析其中的web-fragment.xml
Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
// Step 2. Order the fragments.
// 对多个地方出现的web.xml文件进行排序
Set<WebXml> orderedFragments = null;
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
// Step 3. Look for ServletContainerInitializer implementations
// 通过spi来找到ServletContainerInitializer的实现
if (ok) {
processServletContainerInitializers();
}
if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
// Steps 4 & 5.
// 解析/WEB-INF/classes下面的类的注解
processClasses(webXml, orderedFragments);
}
if (!webXml.isMetadataComplete()) {
// Step 6. Merge web-fragment.xml files into the main web.xml
// file.
if (ok) {
ok = webXml.merge(orderedFragments);
}
// Step 7. Apply global defaults
// Have to merge defaults before JSP conversion since defaults
// provide JSP servlet definition.
webXml.merge(defaults);
// Step 8. Convert explicitly mentioned jsps to servlets
if (ok) {
convertJsps(webXml);
}
// Step 9. Apply merged web.xml to Context
if (ok) {
configureContext(webXml);
}
} else {
// 一般会走这个分支
// 合并配置
webXml.merge(defaults);
convertJsps(webXml);
// 这里会生成wrapper对象
configureContext(webXml);
}
if (context.getLogEffectiveWebXml()) {
log.info("web.xml:\n" + webXml.toXml());
}
// Always need to look for static resources
// Step 10. Look for static resources packaged in JARs
if (ok) {
// Spec does not define an order.
// Use ordered JARs followed by remaining JARs
Set<WebXml> resourceJars = new LinkedHashSet<>(orderedFragments);
for (WebXml fragment : fragments.values()) {
if (!resourceJars.contains(fragment)) {
resourceJars.add(fragment);
}
}
processResourceJARs(resourceJars);
// See also StandardContext.resourcesStart() for
// WEB-INF/classes/META-INF/resources configuration
}
// Step 11. Apply the ServletContainerInitializer config to the
// context
if (ok) {
for (Map.Entry<ServletContainerInitializer,
Set<Class<?>>> entry :
initializerClassMap.entrySet()) {
if (entry.getValue().isEmpty()) {
context.addServletContainerInitializer(
entry.getKey(), null);
} else {
context.addServletContainerInitializer(
entry.getKey(), entry.getValue());
}
}
}
}
全局和host层级web.xml文件的解析
这里主要看下getDefaultWebXmlFragment这个方法
主要就是读取全局和host层级的web.xml文件,host层级的web.xml文件的配置优先级高于全局的,同时将配置文件解析结果加入缓存
private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
// Host should never be null
Host host = (Host) context.getParent();
// 从缓存中获取默认的webxml解析结果
DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
// 全局web.xml文件 ${tomcat目录}/resource/conf/web.xml文件
InputSource globalWebXml = getGlobalWebXmlSource();
// 当前host的web.xml ${tomcat目录}/resource/conf/Catalina/localhost/web.xml.default
InputSource hostWebXml = getHostWebXmlSource();
long globalTimeStamp = 0;
long hostTimeStamp = 0;
// 这里获取全局web.xml文件上一次修改的时间
if (globalWebXml != null) {
URLConnection uc = null;
try {
URL url = new URL(globalWebXml.getSystemId());
uc = url.openConnection();
globalTimeStamp = uc.getLastModified();
} catch (IOException e) {
globalTimeStamp = -1;
} finally {
if (uc != null) {
try {
uc.getInputStream().close();
} catch (IOException e) {
ExceptionUtils.handleThrowable(e);
globalTimeStamp = -1;
}
}
}
}
// 这里获取host层面的web.xml文件上一次修改的时间
if (hostWebXml != null) {
URLConnection uc = null;
try {
URL url = new URL(hostWebXml.getSystemId());
uc = url.openConnection();
hostTimeStamp = uc.getLastModified();
} catch (IOException e) {
hostTimeStamp = -1;
} finally {
if (uc != null) {
try {
uc.getInputStream().close();
} catch (IOException e) {
ExceptionUtils.handleThrowable(e);
hostTimeStamp = -1;
}
}
}
}
// 这里会判断之前的缓存是否有效,自从上次缓存之后,默认配置文件没有修改
if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
entry.getHostTimeStamp() == hostTimeStamp) {
InputSourceUtil.close(globalWebXml);
InputSourceUtil.close(hostWebXml);
return entry.getWebXml();
}
// Parsing global web.xml is relatively expensive. Use a sync block to
// make sure it only happens once. Use the pipeline since a lock will
// already be held on the host by another thread
// 这里对host的pipeline进行上锁
synchronized (host.getPipeline()) {
// 再次判断缓存
entry = hostWebXmlCache.get(host);
if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
entry.getHostTimeStamp() == hostTimeStamp) {
return entry.getWebXml();
}
WebXml webXmlDefaultFragment = createWebXml();
webXmlDefaultFragment.setOverridable(true);
// Set to distributable else every app will be prevented from being
// distributable when the default fragment is merged with the main
// web.xml
webXmlDefaultFragment.setDistributable(true);
// When merging, the default welcome files are only used if the app has
// not defined any welcomes files.
webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
// Parse global web.xml if present
// 开始解析全局的web.xml文件,将解析结果村粗到webXmlDefaultFragment中
if (globalWebXml == null) {
// This is unusual enough to log
log.info(sm.getString("contextConfig.defaultMissing"));
} else {
if (!webXmlParser.parseWebXml(
globalWebXml, webXmlDefaultFragment, false)) {
ok = false;
}
}
// Parse host level web.xml if present
// Additive apart from welcome pages
webXmlDefaultFragment.setReplaceWelcomeFiles(true);
// 解析host层级的web.xml文件
if (!webXmlParser.parseWebXml(
hostWebXml, webXmlDefaultFragment, false)) {
ok = false;
}
// Don't update the cache if an error occurs
// 更新默认web.xml文件的解析结果
if (globalTimeStamp != -1 && hostTimeStamp != -1) {
entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
globalTimeStamp, hostTimeStamp);
hostWebXmlCache.put(host, entry);
// Add a Lifecycle listener to the Host that will remove it from
// the hostWebXmlCache once the Host is destroyed
host.addLifecycleListener(new HostWebXmlCacheCleaner());
}
return webXmlDefaultFragment;
}
}
解析context层级的web.xml的过程比较简单,就不分析了
解析应用引入的jar中的配置文件
protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
WebXmlParser webXmlParser) {
JarScanner jarScanner = context.getJarScanner();
boolean delegate = false;
if (context instanceof StandardContext) {
delegate = ((StandardContext) context).getDelegate();
}
boolean parseRequired = true;
Set<String> absoluteOrder = application.getAbsoluteOrdering();
if (absoluteOrder != null && absoluteOrder.isEmpty() &&
!context.getXmlValidation()) {
// Skip parsing when there is an empty absolute ordering and
// validation is not enabled
parseRequired = false;
}
FragmentJarScannerCallback callback =
new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
// 重点在这里
jarScanner.scan(JarScanType.PLUGGABILITY,
context.getServletContext(), callback);
if (!callback.isOk()) {
ok = false;
}
return callback.getFragments();
}
看下StandardJarScanner的scan方法
主要会扫描三个地方的web-fragment.xml文件:WEB-INF/lib下、WEB-INF/classes(StandardJarScanner不会扫描这里)以及类路径下的jar
public void scan(JarScanType scanType, ServletContext context,
JarScannerCallback callback) {
if (log.isTraceEnabled()) {
log.trace(sm.getString("jarScan.webinflibStart"));
}
if (jarScanFilter instanceof StandardJarScanFilter) {
if (((StandardJarScanFilter) jarScanFilter).isSkipAll())
return;
}
Set<URL> processedURLs = new HashSet<>();
// Scan WEB-INF/lib
Set<String> dirList = context.getResourcePaths(Constants.WEB_INF_LIB);
if (dirList != null) {
for (String path : dirList) {
if (path.endsWith(Constants.JAR_EXT) &&
getJarScanFilter().check(scanType,
path.substring(path.lastIndexOf('/')+1))) {
// Need to scan this JAR
if (log.isDebugEnabled()) {
log.debug(sm.getString("jarScan.webinflibJarScan", path));
}
URL url = null;
try {
url = context.getResource(path);
processedURLs.add(url);
// 调用callback的scan进行处理
process(scanType, callback, url, path, true, null);
} catch (IOException e) {
log.warn(sm.getString("jarScan.webinflibFail", url), e);
}
} else {
if (log.isTraceEnabled()) {
log.trace(sm.getString("jarScan.webinflibJarNoScan", path));
}
}
}
}
// Scan WEB-INF/classes
try {
URL webInfURL = context.getResource(Constants.WEB_INF_CLASSES);
if (webInfURL != null) {
// WEB-INF/classes will also be included in the URLs returned
// by the web application class loader so ensure the class path
// scanning below does not re-scan this location.
processedURLs.add(webInfURL);
if (isScanAllDirectories()) {
URL url = context.getResource(Constants.WEB_INF_CLASSES + "/META-INF");
if (url != null) {
try {
callback.scanWebInfClasses();
} catch (IOException e) {
log.warn(sm.getString("jarScan.webinfclassesFail"), e);
}
}
}
}
} catch (MalformedURLException e) {
// Ignore. Won't happen. URLs are of the correct form.
}
// Scan the classpath
if (isScanClassPath()) {
doScanClassPath(scanType, context, callback, processedURLs);
}
}
看下FragmentJarScannerCallback的scan
public void scan(Jar jar, String webappPath, boolean isWebapp) throws IOException {
InputStream is = null;
WebXml fragment = new WebXml();
fragment.setWebappJar(isWebapp);
fragment.setDelegate(delegate);
try {
// Only web application JARs are checked for web-fragment.xml
// files.
// web-fragment.xml files don't need to be parsed if they are never
// going to be used.
// 扫描当前jar包下的META-INF/web-fragment.xml文件
if (isWebapp && parseRequired) {
is = jar.getInputStream(FRAGMENT_LOCATION);
}
if (is == null) {
// If there is no web.xml, normal JAR no impact on
// distributable
fragment.setDistributable(true);
} else {
String fragmentUrl = jar.getURL(FRAGMENT_LOCATION);
InputSource source = new InputSource(fragmentUrl);
source.setByteStream(is);
if (!webXmlParser.parseWebXml(source, fragment, true)) {
ok = false;
}
}
} finally {
// 将解析结果放到fragments这个map中
addFragment(fragment, jar.getJarFileURL());
}
}
将配置应用到context
ContextConfig的configContext中从解析的配置结果中,将各种配置set到context中,这里主要看下如何处理servlet的
private void configureContext(WebXml webxml) {
for (ServletDef servlet : webxml.getServlets().values()) {
// 会创建一个StandardWrapper,并且将各种监听器设置上去
Wrapper wrapper = context.createWrapper();
// Description is ignored
// Display name is ignored
// Icons are ignored
// jsp-file gets passed to the JSP Servlet as an init-param
// 设置当前wrapper的属性
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName());
// servlet配置的参数
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());
// servlet multipart部分的设置
MultipartDef multipartdef = servlet.getMultipartDef();
if (multipartdef != null) {
long maxFileSize = -1;
long maxRequestSize = -1;
int fileSizeThreshold = 0;
if(null != multipartdef.getMaxFileSize()) {
maxFileSize = Long.parseLong(multipartdef.getMaxFileSize());
}
if(null != multipartdef.getMaxRequestSize()) {
maxRequestSize = Long.parseLong(multipartdef.getMaxRequestSize());
}
if(null != multipartdef.getFileSizeThreshold()) {
fileSizeThreshold = Integer.parseInt(multipartdef.getFileSizeThreshold());
}
wrapper.setMultipartConfigElement(new MultipartConfigElement(
multipartdef.getLocation(),
maxFileSize,
maxRequestSize,
fileSizeThreshold));
}
// 是否支持异步
if (servlet.getAsyncSupported() != null) {
wrapper.setAsyncSupported(
servlet.getAsyncSupported().booleanValue());
}
wrapper.setOverridable(servlet.isOverridable());
// 将当前wrapper作为context的子容器
// 在将wrapper作为context的子容器的时候,会调用wrapper的start方法,start方法中会判断当前wrapper的状态为new,所以会先执行init方法,再执行start方法
// StandardWrapper并没有实现initInternal方法,因此没有什么特殊的逻辑
// startInternal也没什么特殊的逻辑
context.addChild(wrapper);
}
}