Tomcat 8 源码学习四之Catalina类
Contents
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.definition
和package.access
两个属性的值.如果这两个值为空,则设置为该类中默认的PACKAGE_DEFINITION
和PACKAGE_ACCESS
值.
最后,将这两个值,设置到java.security.Security
类中的属性时,Security
类主要用于管理提供者.
启动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()
获取或初化 StandardServer
注册关闭勾子(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()