WEB应用是怎么被部署的?

 

WEB应用在Tomcat中的自动部署实现!...



一个WEB应用,无论是解压后的目录,还是一个压缩的WAR文件,都支持部署到应用服务器中。

一个WEB应用安装到应用服务器的过程,我们称为部署

部署应用的形式又分多种:

  • 静态的:即应用服务器启动时就存在在Server中的应用。
  • 动态的:即应用服务器运行时动态的把应用添加应用服务器中,支持应用的请求。
            动态部署的实现在Tomcat中可以通过其自动部署特性来完成。

也可以通过Tomcat的Manager应用来实现远程部署。

我们今天先来看一下Tomcat的自动部署实现原理。

整个HostConfig中涉及到多种类型的部署,例如下图其提供的部署方式:



自动部署的时候,我们常规操作是直接把一个标准WAR应用放到

%Tomcat_HOME%/webapps

目录下,而后台线程会定时扫描,发现有需要部署的应用,就会启动部署流程进行应用部署。对于后台线程,感兴趣的朋友可以看前面的文章:对于过期的session,Tomcat做了什么?
后台线程发现了我们的应用后,会调用到
HostConfig
类中的
deployWar
方法。

由于Tomcat支持以context.xml这种提供xml配置格式的应用部署等,所以方法内有大量判断各类部署情况的逻辑。

最终,一个Web应用会对应到一个
StandardContext
对象上,一个Web应用又会被部署到一个虚拟主机上。

最核心的逻辑是下面这几行:

Class clazz = Class.forName(host.getConfigClass());

LifecycleListener listener =

(LifecycleListener) clazz.newInstance();

context.addLifecycleListener(listener);

context.setName(cn.getName());

context.setPath(cn.getPath());

context.setWebappVersion(cn.getVersion());

context.setDocBase(cn.getBaseName() + ".war");

host.addChild(context);
在设置完Context的属性后,将其添加到虚拟主机上⇒
host.addChild(context)

在·addChild·的逻辑中,主要是把这个应用启动起来

        // Start child

if ((getState().isAvailable() ||

LifecycleState.STARTING_PREP.equals(getState())) &&

startChildren) {

try {

child.start();

} catch (LifecycleException e) {

log.error("ContainerBase.addChild: start: ", e);

throw new IllegalStateException

("ContainerBase.addChild: start: " + e);

}

}
此处,启动内会处理许多事情,像Webapp的ClassLoader设置是否使用双亲委托,
web.xml
default
web.xml
merge,以及解析应用中是否包含
web-fragement.xml
,将多项配置合并后,这项工作是在
ContextConfig
类内完成的。
解析完web.xml后,会生成一个Webxml对象,需要根据具体的应用配置信息,初始化我们的组件了,例如Filter,Listener等,
例如下面的逻辑

private void configureContext(WebXml webxml) {

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 (ServletDef servlet : webxml.getServlets().values()) {

Wrapper wrapper = context.createWrapper();

wrapper.setOverridable(servlet.isOverridable());

context.addChild(wrapper);
}

}
整个解析出的Servlet组件,又会被做为Context的子组件添加到应用中。
在解析完Servlet等组件后,会再根据配置设置Session的超时时间,SessionCookie的名称等。对这一部分,可以看前面的文章:深入Tomcat源码分析Session到底是个啥!
一切都OK后,设置应用的配置状态

// Make our application available if no problems were encountered

if (ok) {

context.setConfigured(true);

} else {

log.error(sm.getString("contextConfig.unavailable"));

context.setConfigured(false);

}
再然后,就是对于
filter
listener
等组件的启动了。这些组件的启动是根据前一个组件是否启动成功,再决定是否继续

if (!getConfigured()) {

log.error( "Error getConfigured");

ok = false;

}

// Configure and call application event listeners

if (ok) {

if (!listenerStart()) {

log.error( "Error listenerStart");

ok = false;

}
根据Servlet规范进行的容器实现,都是先进行listener的启动,再进行filter启动。可能某天面试时会有人问到,或者涉及到应用组件加载问题时,记的在这里看到过。

在组件等全部启动完毕后,整个应用部署就完成了。
Tomcat那些事儿

本公众号由从事应用服务器核心研发的工程师维护。文章深入Tomcat源码,分析应用服务器的实现细节,工作原理及与之相关的技术,使用技巧,工作实战等。起于Tomcat但不止于此。同时会分享并发、JVM等,内容多为原创,欢迎关注。

扫描或长按下方二维码,即可关注!


    关注 Tomcat那些事儿


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册