设为首页 收藏本站
查看: 377|回复: 0

[经验分享] [Tomcat源码系列]结构解析 2)生命期控制结构

[复制链接]

尚未签到

发表于 2017-2-5 13:30:16 | 显示全部楼层 |阅读模式
  一、生命期控制结构基础

Tomcat的生命期控制是一个两层的结构

1)Lifecycle(org.apache.catalina.Lifecycle):

    在前一篇
中看到的各组件(Service、Connector、Engine、Host、Context、Wrapper)都会实现这个接口,我们看看这个接口的定义

public interface Lifecycle {
public static final String INIT_EVENT = "init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String DESTROY_EVENT = "destroy";
public static final String PERIODIC_EVENT = "periodic";
public void addLifecycleListener(LifecycleListener listener);
public LifecycleListener[] findLifecycleListeners();
public void removeLifecycleListener(LifecycleListener listener);
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
}
   2)LifecycleListener (org.apache.catalina.LifecycleListener):

    在我们上一篇
看到HostConfig就是输入LifecycleListener。

如下是LifecycleLister的定义

public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}
  二、Tomcat初始化处理过程

1.下面是上一个范例中涉及到的处理器初始化主要过程,可以看出来,通过类似的结构,实现了层层的初始化过程

1.StandardEngine.start

--StandardEngine.init

--super(ContainerBase).start

       --fireLifecycleEvent BEFORE_START_EVENT

       --EngineConfig. lifecycleEvent(基本上啥事不干,可忽略)

    --initChildren

       --StandardHost.start(见2)

    --fireLifecycleEvent START_EVENT

    --fireLifecycleEvent AFTER_START_EVENT

2.StandardHost.start

--StandardHost.init

--super(ContainerBase).start

       --fireLifecycleEvent BEFORE_START_EVENT

       --HostConfig. lifecycleEvent  (识别出Context,并向StandardHost注册)

    --initChildren

            --StandardContext.start(见3)

    --fireLifecycleEvent START_EVENT

     --fireLifecycleEvent AFTER_START_EVENT

3.StandardContext.start

--StandardContext.init

--super(ContainerBase).start

    --fireLifecycleEvent BEFORE_START_EVENT

       --ContextConfig. lifecycleEvent  (解析web.xml,并向StandardContxt注册Wrapper)

       --initChildren

            --StandardWrapper.start

    --fireLifecycleEvent START_EVENT

    --fireLifecycleEvent AFTER_START_EVENT
  2.我们从StandardEngine为入口,看看几个重要的初始化过程的代码

1)Standard.start

public void start() throws LifecycleException {
if( started ) {
return;
}
if( !initialized ) {
init(); //初始化自己
}
// Look for a realm - that may have been configured earlier.
// If the realm is added after context - it'll set itself.
if( realm == null ) {
ObjectName realmName=null;
try {
realmName=new ObjectName( domain + ":type=Realm");
if( mserver.isRegistered(realmName ) ) {
mserver.invoke(realmName, "init",
new Object[] {},
new String[] {}
);
}
} catch( Throwable t ) {
log.debug("No realm for this engine " + realmName);
}
}
// Log our server identification information
//System.out.println(ServerInfo.getServerInfo());
if(log.isInfoEnabled())
log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
if( mbeans != null ) {
try {
Registry.getRegistry(null, null)
.invoke(mbeans, "start", false);
} catch (Exception e) {
log.error("Error in start() for " + mbeansFile, e);
}
}
//ContainerBase.start
super.start();
}
   非常简单明了,初始化自己、注册JMX、super.start三步骤,我们进入下一步

2)ContainerBase.start

    public synchronized void start() throws LifecycleException {
// Validate and update our current component state
if (started) {
if(log.isInfoEnabled())
log.info(sm.getString("containerBase.alreadyStarted", logName()));
return;
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
started = true;
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
logger = null;
getLogger();
if ((logger != null) && (logger instanceof Lifecycle))
((Lifecycle) logger).start();
if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
if ((resources != null) && (resources instanceof Lifecycle))
((Lifecycle) resources).start();
// Start our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children instanceof Lifecycle)
((Lifecycle) children).start();
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
// Start our thread
threadStart();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
   如我们上面所说,也是几个简单的过程:触发初始化事件、初始化各组件、初始化children(层次间的关系通过children实现)。注意,ContainerBase本身会继承Lifecycle接口和Container接口,而StandardEngine、StandardHost、StandardContext、StandardWrapper都继承自ContainerBase,因此初始化方式非常类似。

3)HostConfig,HostConfig是一个LifecycleListener,如我们上一篇的DEMO代码,作为LifecycleListener注册到StandardHost中,在ContainerBase初始化开始阶段,会触发一个BEFORE_START_EVENT,触发HostConfig的初始化,HostConfig初始化过程是:HostConfig. lifecycleEvent-->HostConfig.start-->HostConfig. deployApps,我们看看HostConfig. deployApps是如何实现的

   protected void deployApps() {
File appBase = appBase();
File configBase = configBase();
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs, and loop if additional descriptors are found
deployWARs(appBase, appBase.list());
// Deploy expanded folders
deployDirectories(appBase, appBase.list());
}
  可以看到,是3种类型,分别是{tomcat}/conf/{hostname}/目录下的{context.xml}、{tomcat}/{appBase}/下的war和{tomcat}/{appBase}下的目录。我们再看看HostConfig. deployDirectory是如何实现的

   protected void deployDirectory(String contextPath, File dir, String file) {
DeployedApplication deployedApp = new DeployedApplication(contextPath);
if (deploymentExists(contextPath))
return;
// Deploy the application in this directory
if( log.isDebugEnabled() )
log.debug(sm.getString("hostConfig.deployDir", file));
try {
Context context = (Context) Class.forName(contextClass).newInstance();
if (context instanceof Lifecycle) {
Class clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
((Lifecycle) context).addLifecycleListener(listener);
}
context.setPath(contextPath);
context.setDocBase(file);
File configFile = new File(dir, Constants.ApplicationContextXml);
if (deployXML) {
context.setConfigFile(configFile.getAbsolutePath());
}
host.addChild(context);
deployedApp.redeployResources.put(dir.getAbsolutePath(),
new Long(dir.lastModified()));
if (deployXML) {
deployedApp.redeployResources.put(configFile.getAbsolutePath(),
new Long(configFile.lastModified()));
}
addWatchedResources(deployedApp, dir.getAbsolutePath(), context);
} catch (Throwable t) {
log.error(sm.getString("hostConfig.deployDir.error", file), t);
}
deployed.put(contextPath, deployedApp);
}
  实际上也没有太多复杂的东西,需要更细节的了解可以看org.apache.catalina.startup.HostConfig的代码

4)ContextConfig,与HostConfig类似,我们这里直接进入ContextConfig.start

    protected synchronized void start() {
...略
// Set properties based on DefaultContext
Container container = context.getParent();
...略
// Process the default and application web.xml files
defaultWebConfig();
applicationWebConfig();
if (!context.getIgnoreAnnotations()) {
applicationAnnotationsConfig();
}
if (ok) {
validateSecurityRoles();
}
// Configure an authenticator if we need one
if (ok)
authenticatorConfig();
...略
}
  其中defaultWebConfig解析{tomcat}/config/web.xml(提供JSP和welcome file的支持),而applicationWebConfig当然是每个应用的WEB-INF/web.xml了。解析的全过程不再细节描述,可以参见org.apache.catalina.startup.ContextConfig

5)在初始化过程当中,向Container(Engine、Host、Context、Config)注册child(addChild方法)的过程是需要重点关注的地方,由于跟后续请求处理的过程相关联,因此放在后面再做解析

三、通过如上层层初始化,我们的StandardEngine创建完毕,一切准备就绪,可以等待请求接入

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-337850-1-1.html 上篇帖子: Tomcat源码---请求处理(接收,线程分配)一 下篇帖子: java tomcat 搭建SSL双向认证以及httpclient 代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表