揭开tomcat神秘的面纱之bootstrap加载

在上文揭开tomcat神秘的面纱之bootstrap初始化中,本菜鸟分析了bootstrap最终会经过初始化,加载,启动三个步骤。接着来分析加载过程。
加载过程如下:

tomcat的初始化过程.png

在加载过程中,实质是利用反射,调用catalinaload()方法.

//bootstrap.java
private void load(String[] arguments)
    throws Exception {
    String methodName = "load";
    //利用反射调用catalina的load方法 此处的catalinaDaemon为catalina
    Method method =
        catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    method.invoke(catalinaDaemon, param);
}

在catalina加载过程中,会解析加载conf/server.xml文件,初始化server

//catalina.java
 public void load() {
    initDirs();
    initNaming();
    Digester digester = createStartDigester();//创建解析器,degister解析xml文件使用
    InputSource inputSource = null;
    InputStream inputStream = null;
    File file = null;
    file = configFile();//文件为conf/server.xml
    inputStream = new FileInputStream(file);
    inputSource = new InputSource(file.toURI().toURL().toString());
    if (inputStream == null) {
        inputStream = getClass().getClassLoader()
            .getResourceAsStream(getConfigFile());
        inputSource = new InputSource
            (getClass().getClassLoader()
             .getResource(getConfigFile()).toString());
    }
    if (inputStream == null) {
        inputStream = getClass().getClassLoader()
                .getResourceAsStream("server-embed.xml");
        inputSource = new InputSource
        (getClass().getClassLoader()
                .getResource("server-embed.xml").toString());
    
    }
    if (inputStream == null || inputSource == null) {
        return;
    }
    inputSource.setByteStream(inputStream);
    digester.push(this);
    digester.parse(inputSource);//server初始化在这里
    getServer().setCatalina(this);//设置server的catalina
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    initStreams();
    getServer().init();//初始化server
}

而在server中初始化过程中,实则是调用调用StandardServiceinit();方法。

//StandardServer.java
//LifecycleBase.java
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    setStateInternal(LifecycleState.INITIALIZING, null, false);//INITIALIZING事件
    initInternal();//在这里初始化
    setStateInternal(LifecycleState.INITIALIZED, null, false);//初始化成功事件   
}

protected void initInternal() throws LifecycleException {
    super.initInternal();
    onameStringCache = register(new StringCache(), "type=StringCache");
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    onameMBeanFactory = register(factory, "type=MBeanFactory");
    globalNamingResources.init();
    if (getCatalina() != null) {//加载jar包资源
        ClassLoader cl = getCatalina().getParentClassLoader();
        while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
            if (cl instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) cl).getURLs();
                for (URL url : urls) {
                    if (url.getProtocol().equals("file")) {
                        File f = new File (url.toURI());
                        if (f.isFile() &&
                                f.getName().endsWith(".jar")) {
                            ExtensionValidator.addSystemResource(f);
                        }
                    }
                }
            }
            cl = cl.getParent();
        }
    }
    for (int i = 0; i < services.length; i++) {
        services[i].init();//调用StandardService 的init方法。
    }
}

而在StandardServiceinit();方法中,实质调用和server一样,都会调用initInternal() 方法,两者都继承与LifecycleBase接口,而在StandardService中,则是初始化了engineconnector,这两个配置都是在conf/server.xml中。

 @Override
//StandardService.java
protected void initInternal() throws LifecycleException {
    super.initInternal();
    if (engine != null) {
        engine.init();//初始化engine在这里
    }
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }
    mapperListener.init();
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            connector.init();//初始化连接器
        }
    }
}

而在初始化connection的时候,实际是调用protocolHandler(AjpNio2Protocol)init();方法,初始化 endPoint

protected void initInternal() throws LifecycleException {
    super.initInternal();
    if (protocolHandler == null) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
    }
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }
    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
                getProtocolHandlerClassName()));
    }
    if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                (AbstractHttp11JsseProtocol<?>) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &&
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }
    protocolHandler.init();//实例AjpNio2Protocol,在这里初始化endPoint.
}

public void init() throws Exception {
    if (oname == null) {
        oname = createObjectName();
        if (oname != null) {
            Registry.getRegistry(null, null).registerComponent(this, oname, null);
        }
    }

    if (this.domain != null) {
        rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        Registry.getRegistry(null, null).registerComponent(
                getHandler().getGlobal(), rgOname, null);
    }

    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);
    endpoint.init();//初始化endPoint
}

而在endPoint初始化过程中,实则是打开了ServerSocketChannel,调用了Selectoropen()方法。

//EndPoint.java
public final void init() throws Exception {
    if (bindOnInit) {
        bind();//在这里初始化ServerSocket
        bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}

@Override
public void bind() throws Exception {
    initServerSocket();//初始化ServerSocket
    if (acceptorThreadCount == 0) {
        acceptorThreadCount = 1;
    }
    if (pollerThreadCount <= 0) {
        pollerThreadCount = 1;
    }
    setStopLatch(new CountDownLatch(pollerThreadCount));
    initialiseSsl();
    selectorPool.open();//selector.open();
}

  protected void initServerSocket() throws Exception {
    if (!getUseInheritedChannel()) {
        serverSock = ServerSocketChannel.open();//初始化ServerSocket
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        serverSock.socket().bind(addr,getAcceptCount());
    } else {
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) {
            serverSock = (ServerSocketChannel) ic;
        }
        if (serverSock == null) {
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        }
    }
    serverSock.configureBlocking(true); //设置非阻塞
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容