StringManager

每个包一个StringManager。这样子,对同一个包(以及同一个locale)来说,它只有一个实例的。实现原理:

private static final Map<String, Map<Locale,StringManager>> managers = new Hashtable<>();

key:就是包名. value:就是它的由locale,StringManager组合的Map

它的作用,就是实现日志打印的国际化.比如,在包/org/apache/catalina/startup下,可以看到有以下几个国际化的资源文件:

╭─sky@sky-linux /ihome/java/tomcat/apache-tomcat-8.0.36-src/java/org/apache/catalina/startup  
╰─➤  ls -al LocalStrings*
-rw-r--r-- 1 sky sky  8590  6月  9 15:00 LocalStrings_es.properties
-rw-r--r-- 1 sky sky  4536  6月  9 15:00 LocalStrings_fr.properties
-rw-r--r-- 1 sky sky  6770  6月  9 15:00 LocalStrings_ja.properties
-rw-r--r-- 1 sky sky 12332  6月  9 15:00 LocalStrings.properties

ResourceBundle

StringManager是封装了ResourceBundle的,

ResourceBundle简单用法:

package com.example;

import java.util.Locale;
import java.util.ResourceBundle;

/**
 * Created by sky on 16-6-28.
 */
public class TestBundle {
    public static void main(String[] args) {
        String bundleName = "res";
        Locale localeZH = new Locale("zh", "CN");
        ResourceBundle resourceBundleZH = ResourceBundle.getBundle(bundleName, localeZH);
        System.out.println(resourceBundleZH.getString("hello"));

        Locale localeEN = new Locale("en", "US");
        ResourceBundle resourceBundleEN = ResourceBundle.getBundle(bundleName, localeEN);
        System.out.println(resourceBundleEN.getString("hello"));


    }
}

在classpath根目录下,创建两个文件:res_en_US.properties, res_zh_CN.properties

res_en_US.properties文件内容:

hello=hello world from en US

res_zh_CN.properties文件内容:

hello=hello world from zh CN

最后执行上面的代码,即可以看到它会输出下面的结果:

hello world from zh CN
hello world from en US

Process finished with exit code 0

Catalina

它的构造函数就一个,代码如下:

  public Catalina() {
        setSecurityProtection();
    }

setSecurityProtection()方法代码如下:

protected void setSecurityProtection(){
        SecurityConfig securityConfig = SecurityConfig.newInstance();
        securityConfig.setPackageDefinition();
        securityConfig.setPackageAccess();
    }

SecurityConfig

它的作用是,再次保护Tomcat的包访问权限.

默认情况下它是获取catalina.properties文件中的package.definitionpackage.access两个属性的值.如果这两个值为空,则设置为该类中默认的PACKAGE_DEFINITIONPACKAGE_ACCESS值.

最后,将这两个值,设置到java.security.Security类中的属性时,Security类主要用于管理提供者.

Oracle JavaSE Security文档

tutorialspoint

启动Tomcat

Catalina表示一个Tomcat服务器实例.现在我们来看看它的各种逻辑处理.

start()

启动前,会先判断有没有Server实例,即:

        if (getServer() == null) {
            load();
        }

        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

可以看到,如果第二次判断还是null,就直接打印日志并退出了.

那我们再来看看 load() 方法加载server的逻辑是如何的.

1. initDirs() : 初始化 java.io.tmpdir 目录
2. initNaming() : 初始化Java命名服务
3. 使用 Digester 读取`server.xml`配置文件作为server instance配置.
4. 设置server的catalina, catalinaHome, catalinaBase
5. initStreams(): 设置标准输出和错误输出为 SystemLogHandler 接管.
6. 最后调用 server.init() 方法来初始化server的生命周期阶段.

关于Tomcat组件的生命周期,它们是通过实现 Lifecyle 接口来进行处理的. 经过上面的步骤处理后,server就算是启动完毕了.

server.xml 解析器 Digester

org.apache.catalina.startup.Catalina.createStartDigester()方法上,可以看到整个xml的节点结构.

初始化的时候,就会调用这个方法然后初始化Server, GlobalNamingResources, Listener, Service, Executor, Connector, Engine等,然后它会调用相相关的setXXX方法来设置相应值。

例如:

digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResourcesImpl");

Server/GlobalNamingResources表示设置 Server 类的 globalNamingResources 属性. setGlobalNamingResources表示方法名 org.apache.catalina.deploy.NamingResourcesImpl:表示参数类型.

其他类似.

catalina.startServer()

  1. 获取或初化 StandardServer

  2. 注册关闭勾子(shutdownHook, JVM的一种勾子机制)。本质上是调用Catalina.stop()方法,然后Catalina.stop()方法的处理逻辑是: 移除这个勾子,然后调用 Server.stop(),最后再调用 Server.destroy().

catalina.stopServer()

通过源码可以看到,它本上是通过解析server.xml(通过 Digester digester = createStopDigester(); 获取stop时需要的节点元素及数据)然后向注册了关闭勾子的线程(shutdownHook)发送一个在server.xml里配置的shutdown的字符串的值发送到勾子线程监听的socket里,然后触发catalina.stop()方法的。(即在上面说的原理)

而这个shutdown字符串的值,是在server.xml里配置的.

<Server port="8005" shutdown="SHUTDOWN">

即,解析完server.xml后,获取这个shutdown属性的值(这里为SHUTDOWN),然后发送到shutdownHook勾子里,让它触发catalina.stop()的方法.

StandardServer 类

启动Server

启动的顺序,通过源码可知:

org.apache.catalina.core.StandardServer中的下面方法.

    @Override
    protected void startInternal() throws LifecycleException {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);

        globalNamingResources.start();

        // Start our defined Services
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

即:

Server -> globalNamingResources.start(), Service.start() service -> container.start() service -> executors.start() service -> listener.start() service -> connector.start()

即每个组件初始化启动时,都会负责去启动自己内部组件的嵌套组件.比如上面的情况,通过debug源码,可以看到上面的层次关系.

stopServer

destroyInternal(),源码如下:

    @Override
    protected void destroyInternal() throws LifecycleException {
        // Destroy our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].destroy();
        }

        globalNamingResources.destroy();

        unregister(onameMBeanFactory);

        unregister(onameStringCache);

        super.destroyInternal();
    }

server -> service.destroy()