Tomcat - 初始化流程分析
前言
在 Tomcat - 源码阅读环境搭建 这篇文章中我们搭建了 Tomcat
源码阅读的环境;在 Tomcat - 从源码阅读中分析核心组件 中我们分析了 Tomcat
的核心组件及其依赖关系。接下来我们就一起来跟踪一下 Tomcat
源码中的初始化流程
分析
org.apache.catalina.startup.Bootstrap
入口类
public static void main(String args[]) {
synchronized (daemonLock) {
if (daemon == null) {
// 首先会初始化一个 Bootstrap 对象,初始化一些静态的变量和代码块
Bootstrap bootstrap = new Bootstrap();
try {
// 1、核心流程
bootstrap.init();
}
...
daemon = bootstrap;
}
...
}
try {
// 默认命令为 start
String command = "start";
...
} else if (command.equals("start")) {
// 调用 Catalina 实例的 setAwait 方法,参数为 true
daemon.setAwait(true);
// 2、核心流程,利用反射调用 Catalina 实例的 load 无参方法
daemon.load(args); // 核心组件初始化流程(Server、Connector、Engine、Service、Endpoint、ProtocolHandle等),并绑定好了端口,准备接收数据
// 3、利用反射调用 Catalina 实例的 start 方法
daemon.start();
// 利用反射调用 Catalina 实例的 getServer 方法
if (null == daemon.getServer()) {
System.exit(1);
}
}
...
}
1、init()
方法
public void init() throws Exception {
// 初始化类加载器
initClassLoaders();
...
// 获取到 org.apache.catalina.startup.Catalina 类
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
// 初始化 Catalina 实例
Object startupInstance = startupClass.getConstructor().newInstance();
...
// 下面这一块是调用 Catalina 类中的 setParentClassLoader 方法,把 sharedLoader 作为方法入参
{
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
}
// startupInstance 是初始化后的 Catalina,并且设置了其中的 parentClassLoader 属性
catalinaDaemon = startupInstance;
}
1.1、initClassLoaders()
方法
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
/**
* 初始化 3 种类加载器
*/
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if (commonLoader == null) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader = this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
2、load()
方法
private void load(String[] arguments) throws Exception {
// Call the load() method
String methodName = "load";
...
// 调用 Catalina 的 load 无参方法
Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
method.invoke(catalinaDaemon, param);
}
- 查看
Catalina
的load()
方法
public void load() {
...
// Parse main server.xml
// 1、解析 server.xml 配置文件,并创建了 Server 对象
parseServerXml(true);
Server s = getServer();
if (s == null) {
return;
}
...
// Start the new server
try {
// 这里初始化好了 service、engine、connector
// 2、核心流程
getServer().init(); // 初始化 server
}
...
}
2.1、parseServerXml()
方法
protected void parseServerXml(boolean start) {
// Set configuration source
// 首先获取到默认的配置文件 conf/server.xml
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
File file = configFile();
...
ServerXml serverXml = null;
if (serverXml != null) {
serverXml.load(this);
} else {
try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
// Create and execute our Digester
// 1、创建 解析器,来解析 xml 文件
Digester digester = start ? createStartDigester() : createStopDigester();
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
...
// 2、核心流程,解析到了 Server
digester.parse(inputSource);
...
}
...
}
}
2.2.1、createStartDigester()
创建解析器
protected Digester createStartDigester() {
// Initialize the digester
Digester digester = new Digester();
...
// Configure the actions we will be using
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
...
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
...
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
...
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
...
digester.addRule("Server/Service/Connector", new AddPortOffsetRule());
...
// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader));
...
return digester;
}
2.2.2、Digester
的 parse()
方法
从 server.xml
中解析出 Server
对象
public Object parse(InputSource input) throws IOException, SAXException {
configure();
getXMLReader().parse(input); // 获取到 XMLReader,并解析 server.xml
return root; // 封装为 Catalina 后返回
}
getXMLReader()
获取XMLReader
public XMLReader getXMLReader() throws SAXException {
if (reader == null) {
// 1、getParser
// 2、getXMLReader
reader = getParser().getXMLReader();
}
...
return reader;
}
getParser()
获取解析器:
public SAXParser getParser() {
// Return the parser we already created (if any)
if (parser != null) {
return parser;
}
// Create a new parser
try {
// 工厂模式
parser = getFactory().newSAXParser();
} catch (Exception e) {
log.error(sm.getString("digester.createParserError"), e);
return null;
}
return parser;
}
获取到工厂类:
public SAXParserFactory getFactory() throws SAXNotRecognizedException, SAXNotSupportedException,
ParserConfigurationException {
if (factory == null) {
// 返回工厂类 SAXParserFactory 的实例
factory = SAXParserFactory.newInstance();
...
}
return factory;
}
newInstance()
:
public static SAXParserFactory newInstance() {
return FactoryFinder.find(
/* The default property name according to the JAXP spec */
SAXParserFactory.class,
/* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
}
即 getFactory()
返回的是 com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
实现类。
那么 parser
通过 SAXParserFactoryImpl
的 newSAXParser()
返回:
public SAXParser newSAXParser()
throws ParserConfigurationException
{
SAXParser saxParserImpl;
try {
// 调用构造函数创建,会初始化 xmlReader 为 JAXPSAXParser
saxParserImpl = new SAXParserImpl(this, features, fSecureProcess);
} catch (SAXException se) {
// Translate to ParserConfigurationException
throw new ParserConfigurationException(se.getMessage());
}
return saxParserImpl;
}
getXMLReader()
即返回 SAXParserImpl
中的 xmlReader
,在构造函数中初始化为 JAXPSAXParser
:
SAXParserImpl(SAXParserFactoryImpl spf, Map<String, Boolean> features, boolean secureProcessing)
throws SAXException
{
fSecurityManager = new XMLSecurityManager(secureProcessing);
fSecurityPropertyMgr = new XMLSecurityPropertyManager();
// Instantiate a SAXParser directly and not through SAX so that we use the right ClassLoader
xmlReader = new JAXPSAXParser(this, fSecurityPropertyMgr, fSecurityManager);
...
}
parse(input)
解析server.xml
文件流
这里就是根据定义好的解析规则来解析并返回一个 Catalina
,同时根据 server.xml
中的内容生成了 Server
对象(这个解析过程并非主要流程,这里也不展开了,感兴趣的朋友可以自己 Debug
看一下):
2.2、Server
的 init()
方法
在 Tomcat - 从源码阅读中分析核心组件 这篇文章中我们知道,Server
等组件都是有生命周期的,实现了 Lifecycle
接口:
LifecycleBase
使用模板设计模式,定义了执行流程,由子类实现具体逻辑
@Override
public finalsynchronized void init() throws LifecycleException {
...
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 核心流程,由子类实现具体初始化过程
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
...
}
protected abstract void initInternal() throws LifecycleException;
- 子类
StandardServer
实现initInternal()
方法:
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
...
// Initialize our defined Services
for (Service service : services) {
// 初始化 Server 的时候,会初始化 Service
service.init();
}
}
2.2.1、Service
的 init()
方法
- 继续查看
Service
的init()
方法,同样是由实现类StandardService
实现具体的initInternal()
模板方法:
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// 初始化 service 之前需要先初始化 engine
if (engine != null) {
engine.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
// 会初始化 endpoint,并绑定好端口
connector.init();
}
}
}
2.2.2、Engine
的 init()
方法
Engine
中可能会处理一些 Realm
@Override
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
2.2.3、Connector
的 init()
方法
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
...
try {
// 协议处理器初始化
protocolHandler.init();
}
...
}
2.2.4、ProtocolHandler
的 init()
方法
ProtocolHandler
的 init()
方法由抽象类 AbstractProtocol
实现:
@Override
public void init() throws Exception {
...
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
// 端点初始化
endpoint.init();
}
2.2.5、AbstractEndpoint
的 init()
方法
public final void init() throws Exception {
if (bindOnInit) {
bindWithCleanup();
bindState = BindState.BOUND_ON_INIT;
}
if (this.domain != null) {
// Register endpoint (as ThreadPool - historical name)
oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
Registry.getRegistry(null, null).registerComponent(this, oname, null);
ObjectName socketPropertiesOname = new ObjectName(domain +
":type=SocketProperties,name=\"" + getName() + "\"");
socketProperties.setObjectName(socketPropertiesOname);
Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);
for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
registerJmx(sslHostConfig);
}
}
}
private void bindWithCleanup() throws Exception {
try {
// 由实现类实现
bind();
}
...
}
public abstract void bind() throws Exception;
bind()
由实现类 NioEndpoint
实现:
@Override
public void bind() throws Exception {
// 初始化 ServerSocket
initServerSocket();
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed
initialiseSsl();
}
protected void initServerSocket() throws Exception {
...
} else {
// java nio 中的 ServerSocketChannel 打开一个 channel
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
// 绑定端口,到这里为止还只是初始化 ServerSocket。并没有准备好开始接收请求
serverSock.bind(addr, getAcceptCount());
}
serverSock.configureBlocking(true); //mimic APR behavior
}
到此为止,已经走到了 Server
初始化栈的顶端,表示相关的组件 Service
、Engine
、Connector
等都已经初始化完了。
同时也是终于执行完了 Catalina
对象的 load()
方法,整个过程还是比较长的,而且涉及到的概念比较多,又使用了设计模式,所以这块可能需要多看几遍才能理解。
3、start()
方法
上一部分中已经完成了 Bootstrap
对象的 load()
方法,同时也初始化好了 Server
对象。在下一篇文章 Tomcat - 启动流程分析 中我们再来分析 启动流程