环境:tomcat7.0.28
坚持一下,把源码看完,勤奋一点,不要在懒惰了,你已经落下别人很多了
本文主要讲解Catalina的load方法,顺带简单介绍一下Digester组件
一、总体流程
二、代码解析
1、createStartDigester()方法解析
long t1=System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
HashMap<Class<?>, List<String>> fakeAttributes =
new HashMap<Class<?>, List<String>>();
ArrayList<String> attrs = new ArrayList<String>();
attrs.add("className");
fakeAttributes.put(Object.class, attrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true);
// Configure the actions we will be using
//创建StandardServer,当遇到Server标签的时候
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");//为StandardServer属性设置值
//在Catalina.load方法中,当定义好所有规则之后,会把Catalina对象压栈,此时去解析server.xml,会将Server对象压栈,然后调用Catalina的setServer方法
digester.addSetNext("Server","setServer","org.apache.catalina.Server");
//创建NamingResources遇到server标签下的GlobalNamingResources标签的时候
digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");//设置属性
digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
//在配置文件里 Listener 标签有个className 的属性,根据读入的值进行类的创建
digester.addObjectCreate("Server/Listener", null, "className");//第二个参数为空,说明需要在server.xml中Listener标签指定实现类
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
digester.addObjectCreate("Server/Service/Listener", null, "className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
//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");
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());//创建连接器
digester.addRule("Server/Service/Connector",new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");
digester.addObjectCreate("Server/Service/Connector/Listener", null, "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/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
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));
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
long t2=System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Digester for server.xml created " + ( t2-t1 ));
}
return (digester);
通过查看代码可以知道,基本上的操作都是在调用digester的方法。
1.1Digester简介
Digester是
apache的一个组件,通过它可以很方便的从xml文件生成java对象。
Digester由"事件"驱动,通过调用预定义的规则操作对象栈,将XML文件转换为Java对象。工作原理如下:
Digester底层采用
SAX解析XML文件,所以很自然的,对象转换由"事件"驱动,即在识别出特定XML元素时(实际被细分为begin、body、end、finish四个时点),将执行特定的动作,比如创建特定的Java对象,或调用特定对象的方法等。此处的XML元素根据匹配模式(matching pattern)识别,而相关操作由规则(rule)定义。在转换过程中,Digester维持了一个对象栈,可以看作对象转换的工作台,用来存放转换中生成的、或是为转换临时创建的Java对象。对输入XML文件作了一趟完整的扫描后,对象栈的栈顶元素即为目标对象。由于Digester屏蔽了
SAX解析的细节,使用者仅需关注转换操作本身,大大简化了转换操作。关于Digester的简单实用,读者可以参考这篇文章:
点击打开链接
1.2 Digester.addObjectCreate 方法解析
public void addObjectCreate(String pattern, String className,
String attributeName) {
addRule(pattern,
new ObjectCreateRule(className, attributeName));
}
调用addRule方法,同时封装一个ObjectCreateRule 的规则。
1.3Digester.addRule() 方法解析
public void addRule(String pattern, Rule rule) {
rule.setDigester(this); //将规则rule 与 Digester进行绑定
getRules().add(pattern, rule);
}
1.4 Digester.getRules() 方法解析
public Rules getRules() {
if (this.rules == null) {
this.rules = new RulesBase();
this.rules.setDigester(this);
}
return (this.rules);
}
走到这里的时候,我们可以看到在Digester中声明了一个Rules的对象rules。
1.5 Rules 和RulesBase
Rules类是一个接口类,我们可以把它看做是操纵 一组规则(Rule)的工具类。既然是操作一组规则,那么它的实现类肯定需要定义一个集合存储这组规则。
RulesBase 是Rules 的实现类,并且它的数据结构如下:
/**
* The set of registered Rule instances, keyed by the matching pattern.
* Each value is a List containing the Rules for that pattern, in the
* order that they were originally registered.
*/
protected HashMap<String,List<Rule>> cache = new HashMap<String,List<Rule>>(); //Rule集合的键值对
/**
* The Digester instance with which this Rules instance is associated.
*/
protected Digester digester = null; //关联的解析器
/**
* The namespace URI for which subsequently added <code>Rule</code>
* objects are relevant, or <code>null</code> for matching independent
* of namespaces.
*/
protected String namespaceURI = null; //命名空间URI,主要终于查找
/**
* The set of registered Rule instances, in the order that they were
* originally registered.
*/
protected ArrayList<Rule> rules = new ArrayList<Rule>(); //所有的Rule的集合
前文中 我们可以看到Digester的addRule方法,调用了 Rules定义的add方法,我们就直接查看RulesBase的add方法的代码:
public void add(String pattern, Rule rule) {
// to help users who accidently add '/' to the end of their patterns
int patternLength = pattern.length();
if (patternLength>1 && pattern.endsWith("/")) {
pattern = pattern.substring(0, patternLength-1);
}
List<Rule> list = cache.get(pattern);
if (list == null) {
list = new ArrayList<Rule>();
cache.put(pattern, list);
}
list.add(rule);
rules.add(rule);
if (this.digester != null) {
rule.setDigester(this.digester);
}
if (this.namespaceURI != null) {
rule.setNamespaceURI(this.namespaceURI);
}
}
通过上面的代码不难发现,该add方法主要是往cache 键值对里面添加rule。
了解Digester,还得需要知道server.xml的大体结构,读者可以自行去查看tomcat/conf/server.xml的内容。
结论1:通过以上代码 我们就可以知道 创建Digester对象的时候,会定义各种各样的规则,在上面的方法中,我们可以看到总共调用了以下这么几个规则:
(1)ObjectCreateRule 对象创建规则,并且在Digester中入栈,当遇到元素的末尾,则该对象出栈<
(2)SetPropertiesRule 栈顶对象元素设置规则,获取但不取出栈顶对象,针对该对象,如果xml文件配置了相应的属性,则调用对象中对应属性的set方法设置值。
(3)SetNextRule 设置栈顶第二个元素(父)与栈顶第一个元素(子)之间的父子关系规则。
(4)ConnectorCreateRule 连接器创建规则
(5)SetAllPropertiesRule 通过自省和反射机制设置属性的规则
以及一些嵌套元素的规则集合RuleSet,RuleSet可以看做一组Rule集合的封装,RuleSet定义了一个addRuleInstances方法,其实是为Digester定义一系列的规则,就想批处理一样。将一堆命令封装起来,外面只需要调用批处理即可,不用再手动写很多命令。放在这里也一样,可以直接调用封装的RuleSet,使代码更加的简洁。
(1)NamingRuleSet 处理JDN 资源池的 规则集合
(2)EngineRuleSet 处理Engine元素定义的内容 的规则集合
(3)HostRuleSet 处理Host元素定义内容的规则集合
(4)ContextRuleSet 处理Context元素定义内容的规则集合
结论2:Digester定义了很多规则,在解析之前,Digester首先把Catalina对象入栈,通过查看createStartDigester方法,可以确认,通过解析server.xml,
Digester
(1) 创建了一个StandardServer对象,并且 调用了Catalina的setServer方法,
(2)创建了一个NamingResources对象,并且调用了StandardServer的setGlobalNamingResources方法
(3)创建了一个或多个LifecycleListener接口的子对象,该子对象在server.xml必须指定class属性,即实现类,并且调用了StandardServer的addLifecycleListener方法。
其实StandardServer的父类LifecycleBase 中有个LifecycleSupport类的对象,该对象有个LifecycleListener listeners[] 的数组。最终是数组成员添加。
(4)创建了一个或多个StandardService 对象,并且调用了StandardServer的addService方法。
(5)创建了一个或多个LifecycleListener接口的子对象,不过是嵌套在标签<Service> </Service> 标签内的listener,
(6)创建了一个或多个Executor对象,并且调用了StandardService的addExecutor方法。
(7)创建了一个或多个Connector对象,并且调用了StandardService的addConnector方法。
(8)创建了一个StandardEngine对象,并且调用了StandardService 的setContainer 方法
(9)创建了一个或多个StandardHost对象,并且调用了StandardEngine的addChild方法。
(10)创建了一个或多个StandardContext对象,并且调用了StandardHost的addChild方法。
再上一张图:
就上面这张图而言,罗列了大致的Tomcat的接口 ,实现类,抽象类之间的关系 以及 各个类之间的包含关系,1:1,1:n分别表示1对1 和1对多的关系。
2、Catalina.getServer().init();方法解析
通过使用digester解析server.xml,我们可以确定Catalina的server对象是类StandardServer的对象。
StandardServer类的继承关系如下
调用StandardServer的init方法其实是调用LifecycleBase的init方法,该方法代码如下:
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
最终会调用子类的initInternal方法。注意 设置state 的时候方法是用synchronized修饰的。
3、StandardServer.initInternal()方法解析:
protected void initInternal() throws LifecycleException {
super.initInternal();
// Register global String cache
// Note although the cache is global, if there are multiple Servers
// present in the JVM (may happen when embedding) then the same cache
// will be registered under multiple names
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
在代码最后一行,启动所有service的初始化方法init, 该services 是在Digester解析server.xml的时候 注入到Server里的。
4、StandardServer.initInternal()方法解析
protected void initInternal() throws LifecycleException {
super.initInternal();
if (container != null) {
container.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof LifecycleMBeanBase) {
((LifecycleMBeanBase) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
(1)在 service 的initInternal中,首先 判断容器 container 是否进行了初始化。我们在Catalina的createStartDigester 方法中会发现有这么一行代码
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
继续深入查看EngineRuleSet 的addRuleInstances代码可以发现有这么一条规则:
digester.addSetNext(prefix + "Engine","setContainer", "org.apache.catalina.Container");
也就是说在Digester解析server.xml的时候,如果在<service></service>标签中发现了<Engine></Engine>的元素,这时候会先创建Engine的对象也就是StandardEngine,然后通过调用StandardService中的setContainer 方法 将StandardEngine 注入到StandardService中。
(2)如果在<Service></Service>定义了线程池,则调用线程池的init方法。
线程池的实现类,首先查看Catalina的createStartDigester方法:
//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");
从上面就可以确定Executor的实现类是StandardThreadExecutor。在该类中,没有进行额外的初始化工作。
(3)最后对连接器Connector进行初始化。连接器在后面的章节进行分析。