Tomcat war包加载过程
2018-11-15 本文已影响44人
晴天哥_王志
开篇
这篇文章试图对war包的解析过程进行更加深刻的分析,不过似乎这篇文章分析的也不是特别的清楚,不过这篇文章至少说清楚了add和start两个过程,也算欣慰。
用一句话来概括war包的解析过程,就是父容器添加子容器并在子容器的启动过程中触发子容器递归加载子子容器。
如StandardHost加载StandardContext,在启动StandardContext的过程中加载StandardWrapper,因为StandardWrapper是最后一个容器直接执行启动。
图解war包加载过程
war包解析过程 war包解析过程说明:上图说明war包的启动过程,Engine->Host->Context->Wrapper的流程时刻谨记。
-
1、StandardHost的addChild添加StandardContext对象。
-
2、StandardHost的start启动StandardContext对象。
-
3、StandardContext的addChild添加StandardWrapper对象。
-
4、StandardContext的start启动StandardWrapper对象。
说明:上图是来自idea的调用栈
-
1、StandardHost的addChild方法添加StandardContext。
-
2、StandardContext的addChild方法添加StandardWrapper。
源码解析
1、DeployWar启动部署
- config.deployWAR(cn, war);
public class HostConfig implements LifecycleListener {
private static class DeployWar implements Runnable {
private HostConfig config;
private ContextName cn;
private File war;
public DeployWar(HostConfig config, ContextName cn, File war) {
this.config = config;
this.cn = cn;
this.war = war;
}
@Override
public void run() {
// 开始部署war包
config.deployWAR(cn, war);
}
}
}
2、StandardHost添加StandardContext
- host.addChild(context);
public class HostConfig implements LifecycleListener {
protected String contextClass = "org.apache.catalina.core.StandardContext";
protected void deployWAR(ContextName cn, File war) {
context = (Context) Class.forName(contextClass).getConstructor().newInstance();
try {
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName() + ".war");
// 添加StandardContext对象
host.addChild(context);
} catch (Throwable t) {
}
}
}
3、StandardHost添加StandardContext对象
- super.addChild(child);
public class StandardHost extends ContainerBase implements Host {
public void addChild(Container child) {
child.addLifecycleListener(new MemoryLeakTrackingListener());
// 调用父类进行添加StandardContext对象
super.addChild(child);
}
}
4、StandardHost启动StandardContext对象
- addChildInternal(child);
- child.start();
public abstract class ContainerBase extends LifecycleMBeanBase
implements Container {
public void addChild(Container child) {
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> dp =
new PrivilegedAddChild(child);
AccessController.doPrivileged(dp);
} else {
// 添加StandardContext对象
addChildInternal(child);
}
}
private void addChildInternal(Container child) {
synchronized(children) {
child.setParent(this);
children.put(child.getName(), child);
}
try {
if ((getState().isAvailable() ||
LifecycleState.STARTING_PREP.equals(getState())) &&
startChildren) {
// StandardContext启动
child.start();
}
} catch (LifecycleException e) {
// 省略代码
} finally {
fireContainerEvent(ADD_CHILD_EVENT, child);
}
}
}
5、StandardContext基类LifecycleBase启动
- startInternal();
public abstract class LifecycleBase implements Lifecycle {
public final synchronized void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) ||
LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
// 执行StandardContext启动
startInternal();
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
}
}
}
6、StandardContext启动
- fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null)
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
protected synchronized void startInternal() throws LifecycleException {
boolean ok = true;
if (namingResources != null) {
namingResources.start();
}
postWorkDirectory();
if (getResources() == null) { // (1) Required by Loader
try {
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
}
}
if (ok) {
resourcesStart();
}
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
getCharsetMapper();
try {
if (ok) {
// 通过HostConfig启动war包
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// 启动StandardWrapper
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// Start the Valves in our pipeline (including the basic),
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
}
if (!ok) {
setState(LifecycleState.FAILED);
} else {
setState(LifecycleState.STARTING);
}
}
}
7、StandardContext执行事件
- listener.lifecycleEvent(event);
public abstract class LifecycleBase implements Lifecycle {
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
// ContextConfig对象
listener.lifecycleEvent(event);
}
}
}
8、ContextConfig执行事件
- war包解压缩: beforeStart();
- 解析web.xml:configureStart();
public class ContextConfig implements LifecycleListener {
public void lifecycleEvent(LifecycleEvent event) {
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
return;
}
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)) {
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();
}
}
}
9、解析war包
- fixDocBase();
public class ContextConfig implements LifecycleListener {
protected synchronized void beforeStart() {
try {
fixDocBase();
} catch (IOException e) {
}
}
protected void fixDocBase() throws IOException {
Host host = (Host) context.getParent();
File appBase = host.getAppBaseFile();
String docBase = context.getDocBase();
if (docBase == null) {
// Trying to guess the docBase according to the path
String path = context.getPath();
if (path == null) {
return;
}
ContextName cn = new ContextName(path, context.getWebappVersion());
docBase = cn.getBaseName();
}
File file = new File(docBase);
if (!file.isAbsolute()) {
docBase = (new File(appBase, docBase)).getPath();
} else {
docBase = file.getCanonicalPath();
}
file = new File(docBase);
String origDocBase = docBase;
ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
String pathName = cn.getBaseName();
boolean unpackWARs = true;
if (host instanceof StandardHost) {
unpackWARs = ((StandardHost) host).isUnpackWARs();
if (unpackWARs && context instanceof StandardContext) {
unpackWARs = ((StandardContext) context).getUnpackWAR();
}
}
boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
URL war = UriUtil.buildJarUrl(new File(docBase));
if (unpackWARs) {
docBase = ExpandWar.expand(host, war, pathName);
file = new File(docBase);
docBase = file.getCanonicalPath();
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
} else {
ExpandWar.validate(host, war, pathName);
}
} else {
File docDir = new File(docBase);
File warFile = new File(docBase + ".war");
URL war = null;
if (warFile.exists() && docBaseInAppBase) {
war = UriUtil.buildJarUrl(warFile);
}
if (docDir.exists()) {
if (war != null && unpackWARs) {
ExpandWar.expand(host, war, pathName);
}
} else {
if (war != null) {
if (unpackWARs) {
docBase = ExpandWar.expand(host, war, pathName);
file = new File(docBase);
docBase = file.getCanonicalPath();
} else {
docBase = warFile.getCanonicalPath();
ExpandWar.validate(host, war, pathName);
}
}
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
}
}
context.setDocBase(docBase);
}
}
10、解析web.xml生成路由
- configureContext(webXml);
- context.addServletMappingDecoded(urlPattern, jspServletName, true);
public class ContextConfig implements LifecycleListener {
protected synchronized void configureStart() {
// 启动webConfig配置
webConfig();
}
protected void webConfig() {
WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
context.getXmlValidation(), context.getXmlBlockExternal());
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment(webXmlParser));
WebXml webXml = createWebXml();
InputSource contextWebXml = getContextWebXmlSource();
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
ok = false;
}
ServletContext sContext = context.getServletContext();
Map<String,WebXml> fragments =
processJarsForWebFragments(webXml, webXmlParser);
Set<WebXml> orderedFragments = null;
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
if (!webXml.isMetadataComplete()) {
if (ok) {
ok = webXml.merge(orderedFragments);
}
webXml.merge(defaults);
if (ok) {
convertJsps(webXml);
}
if (ok) {
configureContext(webXml);
}
} else {
webXml.merge(defaults);
convertJsps(webXml);
// 配置
configureContext(webXml);
}
}
}
11、解析web.xml
- Wrapper wrapper = context.createWrapper();
public class ContextConfig implements LifecycleListener {
private void configureContext(WebXml webxml) {
context.setPublicId(webxml.getPublicId());
context.setEffectiveMajorVersion(webxml.getMajorVersion());
context.setEffectiveMinorVersion(webxml.getMinorVersion());
for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
context.addParameter(entry.getKey(), entry.getValue());
}
context.setDenyUncoveredHttpMethods(
webxml.getDenyUncoveredHttpMethods());
context.setDisplayName(webxml.getDisplayName());
context.setDistributable(webxml.isDistributable());
for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
context.getNamingResources().addLocalEjb(ejbLocalRef);
}
for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
context.getNamingResources().addEjb(ejbRef);
}
for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
context.getNamingResources().addEnvironment(environment);
}
for (ErrorPage errorPage : webxml.getErrorPages().values()) {
context.addErrorPage(errorPage);
}
for (FilterDef filter : webxml.getFilters().values()) {
if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
}
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
for (String listener : webxml.getListeners()) {
context.addApplicationListener(listener);
}
for (Entry<String, String> entry :
webxml.getLocaleEncodingMappings().entrySet()) {
context.addLocaleEncodingMappingParameter(entry.getKey(),
entry.getValue());
}
if (webxml.getLoginConfig() != null) {
context.setLoginConfig(webxml.getLoginConfig());
}
for (MessageDestinationRef mdr :
webxml.getMessageDestinationRefs().values()) {
context.getNamingResources().addMessageDestinationRef(mdr);
}
context.setIgnoreAnnotations(webxml.isMetadataComplete());
for (Entry<String, String> entry :
webxml.getMimeMappings().entrySet()) {
context.addMimeMapping(entry.getKey(), entry.getValue());
}
// Name is just used for ordering
for (ContextResourceEnvRef resource :
webxml.getResourceEnvRefs().values()) {
context.getNamingResources().addResourceEnvRef(resource);
}
for (ContextResource resource : webxml.getResourceRefs().values()) {
context.getNamingResources().addResource(resource);
}
boolean allAuthenticatedUsersIsAppRole =
webxml.getSecurityRoles().contains(
SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
if (allAuthenticatedUsersIsAppRole) {
constraint.treatAllAuthenticatedUsersAsApplicationRole();
}
context.addConstraint(constraint);
}
for (String role : webxml.getSecurityRoles()) {
context.addSecurityRole(role);
}
for (ContextService service : webxml.getServiceRefs().values()) {
context.getNamingResources().addService(service);
}
for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
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.addServletMappingDecoded(entry.getKey(), entry.getValue());
}
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());
}
}
for (String welcomeFile : webxml.getWelcomeFiles()) {
if (welcomeFile != null && welcomeFile.length() > 0) {
context.addWelcomeFile(welcomeFile);
}
}
for (JspPropertyGroup jspPropertyGroup :
webxml.getJspPropertyGroups()) {
String jspServletName = context.findServletMapping("*.jsp");
if (jspServletName == null) {
jspServletName = "jsp";
}
if (context.findChild(jspServletName) != null) {
for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
context.addServletMappingDecoded(urlPattern, jspServletName, true);
}
} else {
}
}
for (Entry<String, String> entry :
webxml.getPostConstructMethods().entrySet()) {
context.addPostConstructMethod(entry.getKey(), entry.getValue());
}
for (Entry<String, String> entry :
webxml.getPreDestroyMethods().entrySet()) {
context.addPreDestroyMethod(entry.getKey(), entry.getValue());
}
}
public void addServletMappingDecoded(String pattern, String name) {
addServletMappingDecoded(pattern, name, false);
}
public void addServletMappingDecoded(String pattern, String name,
boolean jspWildCard) {
String adjustedPattern = adjustURLPattern(pattern);
synchronized (servletMappingsLock) {
String name2 = servletMappings.get(adjustedPattern);
Wrapper wrapper = (Wrapper) findChild(name2);
wrapper.removeMapping(adjustedPattern);
}
servletMappings.put(adjustedPattern, name);
}
Wrapper wrapper = (Wrapper) findChild(name);
wrapper.addMapping(adjustedPattern);
fireContainerEvent("addServletMapping", adjustedPattern);
}
}
12、默认解析来自Catalina目录下的web.xml
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>