上一篇文章初步看了一下Tomcat启动的入口,而且粗略了解了Tomcat的classLoader的设计。。。。
其实对于启动过程,Bootstrap对象只能算是一个入口吧,例如它初始化了一些目录,创建了最顶层的3个classLoader,然后对于接下来的启动过程就交给了Catalina对象来搞了。。。
另外,在Bootstrap对象中,将当前的线程classLoader设置为了Catalina(也就是专属于Tomcat服务器的classLoader),然后调用了创建的Catalina对象的setParentClassLoader方法,将sharedLoader传了进去,这里也就是属于web应用程序那条线的classLoader,。。。。。把这部分记住就可以开始下面的内容了。。。。
接下来就可以开始说Catalina对象的工作了。。。简单概括一下它的工作:
(1)解析配置的xml文件
(2)根据配置文件创建对象,例如Server,service,connector,container啥的。。
(3)初始化上面创建的对象
(4)启动上面创建的对象。。。
这四部完成,整个tomcat服务器就算是启动了。。。。
其实这重要的事情就是对配置xml进行解析处理。。。。
这里先来看看Catalina的start方法吧:
//创建一个tomcatServer的实例
public void start() {
if (getServer() == null) {
load(); //加载server,这里其实主要是解析xml文件,读取里面的元素定义,用于生成server对象,并且初始化server对象
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start(); //调用server的start,最终启动tomcat服务器,其实server要做的是启动里面的service
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
这里先是调用load方法,用于解析xml文件,创建相应的对象,然后在调用server的start方法来真正的启动tomcat服务器,。。那么这里就主要来看看这个load方法做了什么事情吧。。。。
public void load() {
long t1 = System.nanoTime();
initDirs(); //初始化一些目录参数
// Before digester - it may be needed
initNaming();
// Create and execute our Digester
Digester digester = createStartDigester(); //创建处理xml文件的对象,并会生成相应的处理规则,例如如何创建server对象
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
file = configFile(); //获取server.xml
inputStream = new FileInputStream(file); //读取server.xml
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
getConfigFile()), e);
}
}
}
// This should be included in catalina.jar
// Alternative: don't bother with xml, just create it manually.
if( inputStream==null ) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
"server-embed.xml"), e);
}
}
}
if (inputStream == null || inputSource == null) {
if (file == null) {
log.warn(sm.getString("catalina.configFail",
getConfigFile() + "] or [server-embed.xml]"));
} else {
log.warn(sm.getString("catalina.configFail",
file.getAbsolutePath()));
if (file.exists() && !file.canRead()) {
log.warn("Permissions incorrect, read permission is not allowed on the file.");
}
}
return;
}
try {
inputSource.setByteStream(inputStream);
digester.push(this); //这里先将当前对象放到Digester的栈底,但会server生成之后会调用当前对象的setServer方法来保存起来
digester.parse(inputSource);
} catch (SAXParseException spe) {
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
} catch (Exception e) {
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
} finally {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
getServer().setCatalina(this); //设置sever的catalina对象
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); //设置server的目录
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// Start the new server
try {
getServer().init(); //初始化创建的server对象,其实这里主要是初始化service
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
代码虽然很多,但是这里只要关注最核心的代码就好了。。首先初始化了一些目录的参数。。。。然后创建了Digester对象,这个对象是用于解析配置文件的关键对象。。。接着获取server.xml文件的输入流,然后利用刚刚创建的Digester对象进行解析。。。。。
那么这里关键就是要搞定Digester对象的运行原理了。。。。先来看看createStartDigester方法都干了什么事情吧。。。
protected Digester createStartDigester() {
long t1=System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester(); //这个对象用于解析处理xml文件
digester.setValidating(false);
digester.setRulesValidation(true);
HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
ArrayList<String> attrs = new ArrayList<>();
attrs.add("className");
fakeAttributes.put(Object.class, attrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true);
// Configure the actions we will be using
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className"); //创建一个创建server对象的规则
digester.addSetProperties("Server"); //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server"); //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl"); //创建全局名字资源
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl"); //其实这里实在server对象上面设置resource
digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className"); //根据xml文件配置来创建listener
digester.addSetProperties("Server/Listener"); //设置listener对象配置的属性
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); //这里其实是在server对象上面添加lifecyclelistener
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className"); //创建service对象
digester.addSetProperties("Server/Service"); //设置service的属性
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service"); //在server上面添加调用addService方法
//为service创建listener,不一定有
digester.addObjectCreate("Server/Service/Listener",
null, // MUST be specified in the element
"className"); //根据配置文件参数来创建service的listener
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
//Executor
//创建service的executor,这个不一定有
digester.addObjectCreate("Server/Service/Executor",
"org.apache.catalina.core.StandardThreadExecutor",
"className");
digester.addSetProperties("Server/Service/Executor");
digester.addSetNext("Server/Service/Executor",
"addExecutor",
"org.apache.catalina.Executor");
//为servie添加connector规则
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule()); //这个规则会创建connector,而且如果有属性执行executor的话,还会设置connector的executor
digester.addRule("Server/Service/Connector", //<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>
new SetAllPropertiesRule(new String[]{"executor"})); //这里会将所有的属性都设置,除了executor
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector"); //在service上面添加这个connector
//这里是设置connector的listener
digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
// Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/")); //engine元素的定义的处理,这里主要是创建eingie
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/")); //engine里面host的定义
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/")); //一些context的配置处理
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader)); //如果有发现engine,那么设置container(Engine对象)的parentClassloader,shareloader
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
long t2=System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Digester for server.xml created " + ( t2-t1 ));
}
return (digester);
}
Tomcat对xml配置文件的处理感觉还挺麻烦的,相对于jetty来说,jetty是将整个配置文件看成了类似于一个脚本来解释处理,但是Tomcat则是为每一个xml元素设置相应的处理规则。。。这里就举其中一个规则来说吧:
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className"); //创建一个创建server对象的规则
digester.addSetProperties("Server"); //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server"); //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法
这个意思就是说遇到Server这个元素的的时候创建了3个规则,第一个规则是创建server对象的规则,这里
org.apache.catalina.core.StandardServer
参数表示要创建的server的类型,然后还有一个参数是”className“,待会在处理的时候,会看Server元素是否在属性域设置了className,如果有的话,那么将会使用指定的类型来创建Server对象,而不是上面那个默认的类型。。。
接着创建了一个规则,这个规则用于设置创建的Server对象的属性,也就是根据XML文件的Server元素后面的属性来相应的设置当前的Server对象的属性。。。。
然后还创建了一个SetNext规则,这个规则比较有意思。。。。它表示在整个Server这个XML元素处理完了之后要做的动作,调用setServer方法,那么调用的是谁的setServer方法呢。。?其实调用的当前这个Catalina对象的SetServer方法将已经创建的Server对象保存起来。。。
这里也就是简单的介绍了一下规则的作用,其实Digester的工作原理还是挺简单的,这里就不细说了。。。可以去看一下代码,很简单就能够明白。。。。基本上XML文件解析处理要用到的所有对象都在digester包中。。。。。
这部分搞明白了。。。就基本上能够搞清楚tomcat的server.xml的每一个配置项都要做什么事情了。。。
那么当Server对象创建完成之后就可以进行接下来的初始化和启动过程了。。。。这部分的内容以后再说吧。。。
通过阅读的源码,可以将Tomcat服务器的结构大概用下图来描述:
一个服务器可能会定义多个server对象,每个server对象对应多个service对象,而每个service对象对应多个connector对象,每个service对象包含一个container对象。。。
大概的层次关系如上图。。。