tomcat加载server.xml过程
tomcat启动类是Bootstrap,当启动时会去调用init()方法,在这个方法过程中,会设置Catalina path,通过反射将Catalina给实例化
public void init()
throws Exception
{
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
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);
catalinaDaemon = startupInstance;
}
然后再通过返回去调用Catalina.load方法
private void load(String[] arguments)
throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
method.invoke(catalinaDaemon, param);
}
最后会根据Catalina path路径和默认的configFile = "conf/server.xml",找到server.xml
Tomcat解析server.xml
tomcat解析server.xml是用封装SAX的工具类Degisrter,在学习degister之前,我们先来了解一下,SAX解析XML过程
SAX(Simple API for XML)
是一种以事件驱动的XMl API,是XML解析的一种新的替代方法,SAX与DOM不同的是它边扫描边解析,自顶向下依次解析,由于边扫描边解析,所以它解析XML具有速度快,占用内存少的优点,缺点有无法知道当前节点的父节点,要想知道只能自己动手记录、只能读xml不能修改、无法随机访问某个节点
步骤:
1. 创建SAX解析工厂
2. 让工厂生产一个sax解析器
3. 创建处理器(继承DefaultHandler)
4. 加载文件返回文件的输入流
5. 传入输入流和handler,解析
// 1. 创建SAX解析工厂
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
// 2. 让工厂生产一个sax解析器
SAXParser newSAXParser = saxParserFactory.newSAXParser();
// 3. 创建处理器(继承DefaultHandler)
XmlParseHandler handler = new XmlParseHandler();
// 4. 加载文件返回文件的输入流
InputStream is = XmlParseUtil.class.getClassLoader().getResourceAsStream("users.xml");
// 5. 传入输入流和handler,解析
newSAXParser.parse(is, handler);
举一个例子
先创建User类
public class User {
private String id;
private String name;
private String password;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
再创建xml parse 处理类 XmlParseHandler
public class XmlParseHandler extends DefaultHandler{
private List<User> users;
private String currentTag;
private User currentUser;
/**
* 只会调用一次
*/
@Override
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("-------------startDocument()");
users = new ArrayList<User>();
}
/**
* 当遇到开始标签的时候会触发
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
System.out.println("-------------startElement()--tagName=" + qName);
if ("user".equals(qName)) {
currentUser = new User();
//判断标签是否有属性
if (attributes != null) {
for (int i=0; i<attributes.getLength(); i++) {
System.out.println("-------------startElement()--tagName=" + qName + "--attribute_name="+attributes.getLocalName(i));
if ("id".equals(attributes.getLocalName(i))) {
currentUser.setId(attributes.getValue(i));
}
}
}
}
this.currentTag = qName;
}
/**
* 读到获取标签的内容触发
*/
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
super.characters(ch, start, length);
System.out.println("-------------characters()--currentTag=" + currentTag);
String value = new String(ch, start, length);
if ("name".equals(currentTag)) {
currentUser.setName(value);
} else if ("password".equals(currentTag)) {
currentUser.setPassword(value);
}
}
/**
* 读到标签的结束时触发
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(uri, localName, qName);
System.out.println("-------------endElement()--localName=" + qName);
if ("user".equals(qName)) {
users.add(currentUser);
currentUser = null;
}
currentTag = null;
}
/**
* 读取最后结束标签,只触发一次
*/
@Override
public void endDocument() throws SAXException {
super.endDocument();
System.out.println("-------------endDocument()");
}
public List<User> getUsers() {
return users;
}
}
创建users.xml
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>java</name>
<password>123456</password>
</user>
<user id="2">
<name>tomcat</name>
<password>123456</password>
</user>
</users>
再创建client main
// 1. 创建SAX解析工厂
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
// 2. 让工厂生产一个sax解析器
SAXParser newSAXParser = saxParserFactory.newSAXParser();
// 3. 创建处理器(继承DefaultHandler)
XmlParseHandler handler = new XmlParseHandler();
// 4. 加载文件返回文件的输入流
InputStream is = XmlParseUtil.class.getClassLoader().getResourceAsStream("users.xml");
// 5. 传入输入流和handler,解析
newSAXParser.parse(is, handler);
is.close();
System.out.println("--------users size :" + handler.getUsers().size());
SAX 用例已说完,下面来说下Degister是如何解析server.xml
Degister类是继承了org.xml.sax.ext.DefaultHandler2,如果看过源码的话,其实DefaultHandler2没做什么,它还是继承了DefaultHandler和实现了LexicalHandler, DeclHandler, EntityResolver2
前文说到,当tomcat启动的时候,会去调用catalina.load(),在load方法中Digester digester = createStartDigester();
protected Digester 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
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/");
long t2=System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Digester for server.xml created " + ( t2-t1 ));
}
return (digester);
}
Degister 解析server.xml是利用SAX去解析的,只不过Degister在SAX基础上了一个rule,Rule是一个抽象类
它的实现类有 很多
加上一些规则之后开始解析
try {
inputSource.setByteStream(inputStream);
digester.push(this);
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;
}
注意digester.push(this);这个方法,是把catalina这个对象放到Degister 栈中,所以Degister root就是Catalina对象
1. getXMLReader方法把SAX解析xml前4个步骤做完
public XMLReader getXMLReader() throws SAXException {
if (reader == null){
reader = getParser().getXMLReader();
}
reader.setDTDHandler(this);
reader.setContentHandler(this);
if (entityResolver == null){
reader.setEntityResolver(this);
} else {
reader.setEntityResolver(entityResolver);
}
reader.setProperty(
"http://xml.org/sax/properties/lexical-handler", this);
reader.setErrorHandler(this);
return reader;
}
2.getXMLReader().parse(input);实现了SAX解析xml第5个步骤,它会去触发startElement、startPrefixMapping、characters、endElement、endPrefixMapping方法
startElement
public void startElement(String namespaceURI, String localName,
String qName, Attributes list)
throws SAXException {
boolean debug = log.isDebugEnabled();
if (saxLog.isDebugEnabled()) {
saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
qName + ")");
}
// Parse system properties
list = updateAttributes(list);
// Save the body text accumulated for our surrounding element
bodyTexts.push(bodyText);
bodyText = new StringBuilder();
// the actual element name is either in localName or qName, depending
// on whether the parser is namespace aware
String name = localName;
if ((name == null) || (name.length() < 1)) {
name = qName;
}
// Compute the current matching rule
StringBuilder sb = new StringBuilder(match);
if (match.length() > 0) {
sb.append('/');
}
sb.append(name);
match = sb.toString();
if (debug) {
log.debug(" New match='" + match + "'");
}
// Fire "begin" events for all relevant rules
List<Rule> rules = getRules().match(namespaceURI, match);
matches.push(rules);
if ((rules != null) && (rules.size() > 0)) {
for (int i = 0; i < rules.size(); i++) {
try {
Rule rule = rules.get(i);
if (debug) {
log.debug(" Fire begin() for " + rule);
}
rule.begin(namespaceURI, name, list);
} catch (Exception e) {
log.error("Begin event threw exception", e);
throw createSAXException(e);
} catch (Error e) {
log.error("Begin event threw error", e);
throw e;
}
}
} else {
if (debug) {
log.debug(" No rules found matching '" + match + "'.");
}
}
}
a.updateAttributes(list)先获取标签的属性
b.对于match进行赋值,刚开始match是"",当解析到第一个节点的是,match值是“Server”,遇到下一个节点的时候,match的值变成"Server/Listener",与此类推
c.List<Rule> rules = getRules().match(namespaceURI, match); 通过match,去获取相应的规则,也就是一下几个
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer","className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
d. 然后去执行这规则 begin方法
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer","className");
会直接调用ObjectCreateRule中begin方法
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
// Identify the name of the class to instantiate
String realClassName = className;
if (attributeName != null) {
String value = attributes.getValue(attributeName);
if (value != null) {
realClassName = value;
}
}
if (digester.log.isDebugEnabled()) {
digester.log.debug("[ObjectCreateRule]{" + digester.match +
"}New " + realClassName);
}
if (realClassName == null) {
throw new NullPointerException("No class name specified for " +
namespace + " " + name);
}
// Instantiate the new object and push it on the context stack
Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
通过反射将StandardServer给实例化,再讲实例化对象放到degister栈中,到结束标签的时候,让它出栈
后面规则执行方法都一样,只不过规则的实现方式不一样。
最终tomcat对server.xml解析完后,会构造Server 、service、engine、host、context、connector等