首页 > 代码库 > tomcat源码Catalina
tomcat源码Catalina
Catalina的作用是初始化各个组件,并开始启动各个组件。
上文中介绍了Bootstrap是如何启动Catalina的,现在来看看Catalina的作用:
1,Catalina通过Digester类加载server.xml,实例化server.xml中各个组件,并为这些实例赋值(这个类是通过扩展SAX来完成的)。
2,调用server的start方法开启server组件,server会一级一级的将start传播下去,这样各个组件就从这里开启了。
根据第一个if语句可以知道server是通过load方法实例化的。load方法执行后,启动服务器,逻辑很简单。进入load方法:
如果想搞清楚出digester是如何加载server.xml并实例化各个组件的,你可能会进入 digester.parse(inputSource)方法,但这个方法
不能告诉你关键的内容。来看看digester是个什么类:
public class Digester extends DefaultHandler2
DefaultHandler2是SAX扩展包下面的,不懂SAX的一定要先去了解下SAX,不然看Catalina的源码很费劲。回过头来看看load()方法中的
第一行代码Digester digester = createStartDigester();进入createStartDigester方法:
仔细看着这些参数,xml文档的节点名称都有与之对应的类比如Server节点对象org.apache.catalina.core.StandardServer
从这个数据结构可以知道每个节点的完整名称对应一个Rule的list集合。来看看Rule是如何定义的:
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添加了三个规则:
这三个方法内部会分别创建三个规则类,ObjectCreateRule,SetPropertiesRule,SetNextRule。这三个类的作用分别是创建指定类,将xml标签上的属性赋值给改类,将该类赋值给它的上级类。这三个类被创建后添加到了一个以“Server”为键的map中前面提到的RulesBase.cache。在reader类解析xml到<Server>标签的开始节点时会调用startElement()方法,并将当前节点的节点名和属性值等一系列值传给改方法。方法内部则通过节点全名称获取对应的规则类Rule对象的list结合,并挨个调用rule对象的begin方法。以server对应的ObjectCreateRule规则为例:
创建了与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>的结束标签时调用的。下面是该方法的源码:
上文中介绍了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() { //这里剔除了一些判断,日志,服务器钩子函数,等代码 ...... if (getServer() == null) { load(); } ...... getServer().start(); if (await) { await(); stop(); } }
根据第一个if语句可以知道server是通过load方法实例化的。load方法执行后,启动服务器,逻辑很简单。进入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
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。