目录
- 什么叫发布
- webapp发布方式
- reload
- 总结
什么叫发布
发布就是让tomcat知道我们的程序在哪里,并根据我们的配置创建Context,进行初始化、启动,如下:
- 程序所在的位置
- 创建Context,添加到Host
- 初始化(创建解析webxml的digester)
- 启动(初始化filter、listener、servlet)
webapp发布方式
在tomcat 中发布webapp的方式不同会导致app启动的先后顺序不一样(这里按照启动顺序或者时机不同进行划分):
- 在xml中配置,在conf/server.xml中配置(在host标签内部)
- 直接将webapp文件夹、war包放在tomcat下面的"webapps"目录,或者在webapps下面新建一个xml(根标签为context,在属性中表明应用程序的位置)
其实两种发布方式只是在启动顺序上稍有不同,启动过程完全一致,先以在server.xml中配置说明
在server.xml进行配置
这情况在我们使用eclipse等工具进行开发发布的时候,eclipse会帮我们在server.xml的Host标签内添加Context,如下:
在新建server的时候会解析server.xml,同时也会根据我们上面的配置新建StandardContext,而且会将StandardContext作为Host的一个Child(可以在Catalina.createStartDigester方法中查看怎么解析server.xml),在tomcat的启动过程中context是由父容器host启动的
额,看到这张图发现似曾相识,对滴,就是Context的初始化,因为在tomcat中一个context实例就代表一个webapp,所以其实应用程序webapp的发布本身就是context的初始化和启动,再看看context的启动
以上已经完整展示了一个webapp的发布过程,也就是在server.xml的配置的webapp发布过程,接着来看看在webapps目录下的发布过程。
在webapps目录下
在StandardHost的startInternal方法中调用了父类的ContainerBase.startInternal方法,在StandardHost发布完在server.xml中配置的app之后,会调用setState来切换自身的状态,这个时候就会触发listener HostConfig的lifecycleEvent方法
@Overrideprotected synchronized void startInternal() throws LifecycleException { // Start our subordinate components, if any if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); logger = null; getLogger(); if ((manager != null) && (manager instanceof Lifecycle)) ((Lifecycle) manager).start(); if ((cluster != null) && (cluster instanceof Lifecycle)) ((Lifecycle) cluster).start(); Realm realm = getRealmInternal(); if ((realm != null) && (realm instanceof Lifecycle)) ((Lifecycle) realm).start(); if ((resources != null) && (resources instanceof Lifecycle)) ((Lifecycle) resources).start(); // Start our child containers, if any // 因为在解析server.xml的时候已经新建了Context,这里就可以直接start Container children[] = findChildren(); List> results = new ArrayList >(); for (int i = 0; i < children.length; i++) { results.add(startStopExecutor.submit(new StartChild(children[i]))); } boolean fail = false; for (Future result : results) { try { result.get(); } catch (Exception e) { log.error(sm.getString("containerBase.threadedStartFailed"), e); fail = true; } } if (fail) { throw new LifecycleException( sm.getString("containerBase.threadedStartFailed")); } // Start the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start(); // 调用lifecycleEvent方法,触发StandardHost的start事件,触发监听器HostConfig的start方法,所有在webapps下面的都在start方法中发布 setState(LifecycleState.STARTING); // Start our thread threadStart();}
调用过程如下:
(上图中主要画出了deployWars的调用过程,其他两个deploy方法也类似)
在HostConfig.deployApps方法中主要进行了:
- 调用deployDescriptors:发布所有使用xml配置的webapp,因为可能有多个xml,所以在该方法内部又调用了deployDescriptor来发布每个xml对应的webapp
- 调用deployWars:发布所有在webapps目录下的war包,也可能存在多个
- 调用deployDirectories:发布所有直接部署在webapps目录下的应用程序
前面说过了,发布webapp就是新建一个context对象并初始化、启动,上面三个方法中主要的作用为:
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);
- 构建一个StandardContext
- 将context添加到host中,在Container.addChildInternal方法中会调用context.start
接下来的步骤就和上面在server.xml配置的webapp启动一致了。
reload
在我们使用tomcat开发web的时候经常会用到“热加载”(热部署)功能,那么原理究竟是什么呢?上面介绍了webapp发布,因为reload功能也是从HostConfig开始的,所以继续介绍reload功能
上面的ContainerBase.startInternal是由StandardEngine.startInternal方法调用的,启动了一个daemon线程定时检测webapp是否发生变化(文件是否被修改),如果被修改了则会重新启动StandardContext。
总结
像tomcat部署和发布,我们天天都在用,可是不知道究竟原理怎么样的,在学习了tomcat源码之后,对这一切都更加明了。知其然,知其所以然。