tomcat源代码Catalina
Catalina的作用是初始化各个组件,并開始启动各个组件。
上文中介绍了Bootstrap是怎样启动Catalina的,如今来看看Catalina的作用:
1,Catalina通过Digester类载入server.xml,实例化server.xml中各个组件,并为这些实例赋值(这个类是通过扩展SAX来完毕的)。
2,调用server的start方法开启server组件,server会一级一级的将start传播下去,这样各个组件就从这里开启了。
3,初始化命名空间(tomcat会使用JNDI技术,比方在server.xml中配置了数据库连接池的话,就使用了JNDI)。最后还包装了System.out和System.err。
这里面的重点就是Digester解析server.xml的过程,先来看看start方法:
public void start() {
//这里剔除了一些推断,日志,server钩子函数,等代码
......
if (getServer() == null) {
load();
}
......
getServer().start(); if (await) {
await();
stop();
}
}
依据第一个if语句能够知道server是通过load方法实例化的。load方法运行后,启动服务器,逻辑非常easy。进入load方法:
public void load() { //这种方法的篇幅过长,我踢掉一些对流程不重要的代码
Digester digester = createStartDigester(); InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
//获取sever.xml配置文件
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
} //将sever.xml相应的流传给digester,由digester解析它。
inputSource.setByteStream(inputStream);
//这种方法非常重要,它将自己也就是Catalina这个对象放到了digester对象里的一个栈里面,后面解析xml实例化server后
//会从栈里拿出Catalina对象调用它的setServer方法来设置Catalina.server属性。server里的service属性也是通过这样的形式
//设置的。
digester.push(this);
digester.parse(inputSource); //server实例化后,将设置server.catalina属性,这里Catalina和Server是双向关联的。
getServer().setCatalina(this); // 包装了System.out和System.err
initStreams(); getServer().init(); }
假设想搞清楚出digester是怎样载入server.xml并实例化各个组件的,你可能会进入 digester.parse(inputSource)方法,但这种方法
不能告诉你关键的内容。来看看digester是个什么类:
public class Digester extends DefaultHandler2
DefaultHandler2是SAX扩展包以下的,不懂SAX的一定要先去了解下SAX,不然看Catalina的源代码非常费劲。回过头来看看load()方法中的
第一行代码Digester digester = createStartDigester();进入createStartDigester方法:
/**
* Create and configure the Digester we will be using for startup.
*/
protected Digester createStartDigester() { Digester digester = new Digester(); digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server"); digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResources"); digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
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, // MUST be specified in the element
"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, // 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/"));
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/"); return (digester); }
细致看着这些參数,xml文档的节点名称都有与之相应的类比方Server节点对象org.apache.catalina.core.StandardServer
digester实例化Server时是实例化了它的子类StanderServer。
进入addObjectCreate方法:
public void addObjectCreate(String pattern, String className,
String attributeName) { addRule(pattern,
new ObjectCreateRule(className, attributeName)); }
每调用一个add*方法都为pattern映射了一个Rule的子类,addSetProperties,addSetNext都为pattern映射了一个Rule的子类。
这个映射关系被存放在RulesBase.cache中:
public class RulesBase implements Rules { protected HashMap<String,List<Rule>> cache =
new HashMap<String,List<Rule>>();
}
从这个数据结构能够知道每一个节点的完整名称相应一个Rule的list集合。来看看Rule是怎样定义的:
public abstract class Rule {
public Digester getDigester() {
return (this.digester);
}
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
begin(attributes);
}
public void end(String namespace, String name)
throws Exception {
end();
}
}
begin和end是它的核心方法(有的子类可能没有当中的某个方法,这里为了说明xml的解析流程所以说它们重要)。讲到这里是想说明:digster事先为xml中的每一个节点定义了多个规则。上面提到Degister继承自DefaultHandler2,并重写了它的startDocument(),startElement(),endDocument()endElement()等方法。Degister内部定义了一个XMLReader类型的成员变量reader,并将自己作为ContentHandler和DTDHandler
传给自己的成员变量reader。那么reader在解析xml的时候就会回调Digster继承自DefaultHandler2的startDocument(),startElement(),endDocument(),endElement()等回调方法。在回调方法中就会调用与当前节点相应的Rule类的回调方法。如今拿Server的初始化来举例:在上面提到的createStartDigester()方法中为Server加入了三个规则:
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
这三个方法内部会分别创建三个规则类,ObjectCreateRule,SetPropertiesRule,SetNextRule。这三个类的作用各自是创建指定类,将xml标签上的属性赋值给改类,将该类赋值给它的上级类。这三个类被创建后加入到了一个以“Server”为键的map中前面提到的RulesBase.cache。在reader类解析xml到<Server>标签的開始节点时会调用startElement()方法,并将当前节点的节点名和属性值等一系列值传给改方法。方法内部则通过节点全名称获取相应的规则类Rule对象的list结合,并挨个调用rule对象的begin方法。以server相应的ObjectCreateRule规则为例:
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
创建了与server相应的实例(实例名是org.apache.catalina.core.StandardServer或是属性classname对象的值)并放在了digest的一个名为stack属性的栈顶。前面的load方法中digester将当前类对象也就是Catalina对象push到了stack里面,这时候Catalina应该在stack的底端,由于之前stack里没有数据。server标签是server.xml的第一个标签,这时候解析到它的開始标签并调用了与它对象的ObjectCreateRule规则的begin方法初始化了server对象并后push到了stack里面,那么此时stack有两个元素,Catalina在栈底,server在栈顶。ObjectCreateRule的start方法结束后会继续调用SetPropertiesRule的start方法,这个类是将标签的属性值赋值给上面创建的对象,它的begin方法的第一步就是从stack获取对象,然后将标签上的属性值赋值给该对象比方<Server
port="8005" shutdown="SHUTDOWN">中的port和shutdown属性。最后是SetNextRule类,这个类仅仅有一个end方法是在遇到</Server>的结束标签时调用的。以下是该方法的源代码:
@Override
public void end(String namespace, String name) throws Exception { Object child = digester.peek(0);
Object parent = digester.peek(1);
if (digester.log.isDebugEnabled()) {
if (parent == null) {
digester.log.debug("[SetNextRule]{" + digester.match +
"} Call [NULL PARENT]." +
methodName + "(" + child + ")");
} else {
digester.log.debug("[SetNextRule]{" + digester.match +
"} Call " + parent.getClass().getName() + "." +
methodName + "(" + child + ")");
}
} IntrospectionUtils.callMethod1(parent, methodName,
child, paramType, digester.getClassLoader()); }
在调用这种方法前,degister的成员变量stack已经push和pop好几个对象了,每次标签開始解析时创建对象push到stack里面,标签结束后从stack里pop出来。由于<Server>标签是顶层标签,所以server对象最先被创建并push到stack里面(Catalina一直在栈底),最后被pop出来,所以结束标签</server>解析时和開始一样,stack依旧还是两个元素,server在栈顶,Catalina在栈底。 Object child = digester.peek(0);Object
parent = digester.peek(1);分别取出了栈顶和栈底的元素。server.xml的解析不是非常好叙述,它的关系有点复杂。要记住一点server.xml的每一级标签相应一个tomcat组件,被外层标签包裹的标签是外层标签的一个属性比方serveice是server的一个属性。xml在被解析时会依据当前的事件(開始,或结束)来调用相应节点的解析规则,创建对象,并通过栈来完毕对象之间的关联关系。
tomcat源代码Catalina的更多相关文章
- 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)
本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Ec ...
- Eclipse中执行Tomcat源代码
1. 到http://archive.apache.org/dist/tomcat/tomcat-7下载Tomcat源码,本文用到的是apache-tomcat-7.0.19-src.zip: 注意: ...
- Tomcat请求处理过程(Tomcat源代码解析五)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/ ...
- [转帖]Apache、Tomcat与Catalina作为软件名字的含义与关系
Apache.Tomcat与Catalina作为软件名字的含义与关系 -- :: 复杂度掠夺者 阅读数 3356更多 分类专栏: 术语解释 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA ...
- [转载]Linux下tomcat的catalina.out文件过大,以及目录更改解决办法
原文链接:http://liyan0524.iteye.com/blog/1285644 方法1—分割流 使用cronolog工具切分Tomcat的catalina.out日志文件 cronolog一 ...
- Tomcat输出catalina.out的大小控制
资源URL:http://download.csdn.net/detail/attagain/7771065 Tomcat默认生成的日志文件catalina.out,随着时间的推移,逐渐增大,可能达到 ...
- 【转】Tomcat源代码阅读系列
在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码(Tomcat源代码阅读系列之一) Tomcat总体结构(Tomcat源代码阅读系列之二) Tomcat启动过程(Tomcat ...
- Tomcat 的 catalina.out 日志分割
一.背景 Tomcat 的 catalina.out 日志量不断增加,占用空间较大,且默认是不滚动的.因此,需要对其进行日志分割,并进行清理. 本文选用 Cronolog 对其进行分割. 二.安装 1 ...
- Tomcat中catalina run后台运行脚本
编写启动脚本start.sh,将其放在/srv/aubapp/bin/下 #!/bin/sh #设置web应用程序目录 export CATALINA_BASE="/srv/aubapp&q ...
随机推荐
- web前端利用HSTS(新的Web安全协议HTTP Strict Transport Security)漏洞的超级Cookie(HSTS Super Cookie)
web前端如果想实现cookie跨站点,跨浏览器,清除浏览器cookie该cookie也不会被删除这似乎有点难,下面的教程让你完全摆脱document.cookie 1.服务器端设置HSTS 如PHP ...
- 随着MapReduce job实现去加重,多种输出文件夹
总结以往的工作中遇到的一个问题. 背景: 操作和维护与scribe从apacheserver一再被推到日志记录,所以在这里ETL处理正在进行的重.有根据业务的输出类型是用于多文件夹一个需求.方便挂分区 ...
- hdu2571命
称号: Problem Description 穿过幽谷意味着离大魔王lemon已经无限接近了! 可谁能想到,yifenfei在斩杀了一些虾兵蟹将后.却再次面临命运大迷宫的考验.这是魔王lemon设下 ...
- Google调试技巧总结
工欲善其事 工欲善其事,必先利器. Google调试面板一一介绍:F12回想一下大家都应该知道,哈哈 element面板 这个面板显示了页面所有html代码.用于调试css代码.右側展示左側相应选择元 ...
- unix您不能使用crontab设置运营计划
unix您不能使用crontab设置运营计划 在系统中进行crontab例如,设置在下列现象时有发生: 解决方法: 编辑cron文件内容: #EDITOR=vi #export EDITOR ...
- 走进spring之springmvc
走进spring之springmvc 在动手之前,我们需要了解下springnvc.这里先献上一张springmvc的流程图及讲解. Spring的MVC框架是一个基于DispatcherServle ...
- response的contentType 几种类型
ajax开发中在请求server端的响应时, 对于每一种返回类型 规范的做法是要在服务端指定response的contentType 的. (当然 不指定绝大多数情况下也没什么问题 尤其是返回&quo ...
- 安卓开发笔记——探索EventBus(转)
1.关于EventBus: 组件通讯在Android开发中是不可避免的,随着业务需求的复杂化,代码中需要我们去处理的业务逻辑难度也不断增大.例如多个Fragment之间的数据传递,Service与Ac ...
- DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能 一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己 ...
- OJ提交题目中的语言选项里G++与C++的区别(转载)
原文链接:http://blog.polossk.com/201405/c-plus-plus-g-plus-plus G++? 首先更正一个概念,C++是一门计算机编程语言,G++不是语言,是一款编 ...