前一章大概看了bootstrap的代码,知道了bootstrap除了主要实例化那三个类加载器,其他都是用CatalinaClassLoaler去加载Catalina类的执行对应方法,主要是init load start stop是这几个方法,在看Catalina源码之前,要首先看下tomcat里面的解析XML配置文件的工具集Digester和Rule。Digester源码在org\apache\tomcat\util\digester\Digester.java,最后编译的jar包是tomcat-util-scan.jar。
Digester 部分
首先看下Digester类的定义public class Digester extends DefaultHandler2
可以看到Digester继承了DefaultHandler2类,我们知道DefaultHandler2是java中使用SAX方式解析XML文件的处理类。这里可以看出tomcat是使用SAX的方式来处理tomcat中的各种XML配置文件的。 看SAX主要是看SAX引擎解析XML的时候的几个回调方法,(startDocument、endDocument、startElement、endElement、characters),至于其他的几个方法可以待到将来更加深入分析的时候去看,现在一个个的来看这几个主要方法
1、 startDocument;
记录日志,实例化log 、saxlog、设置configured为true。
2、 startElement重点方法;
1 //记录log
2 boolean debug = log.isDebugEnabled();
3
4 if (saxLog.isDebugEnabled()) {
5 saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + qName + ")");
6 }
7
8 //格式化xml元素的属性,将${}属性用System属性来替换
9 list = updateAttributes(list);
10
11 //为类似<a>xxxx</a>标签做解析文本的准备
12 bodyTexts.push(bodyText);
13 bodyText = new StringBuilder();
14
15
16 String name = localName;
17 if ((name == null) || (name.length() < 1)) {
18 name = qName;
19 }
20
21 /**
22 类似<a><b></b></a>这个xml标签结构会被解析成
23 a/b
24 */
25 StringBuilder sb = new StringBuilder(match);
26 if (match.length() > 0) {
27 sb.append('/');
28 }
29 sb.append(name);
30 match = sb.toString();
31 if (debug) {
32 log.debug(" New match='" + match + "'");
33 }
34
35 //更具match过滤出Rules,rule是addRuleXXX方法添加的
36 List<Rule> rules = getRules().match(namespaceURI, match);
37 matches.push(rules);
38 if ((rules != null) && (rules.size() > 0)) {
39 //循环调用rule的begin方法
40 for (int i = 0; i < rules.size(); i++) {
41 try {
42 Rule rule = rules.get(i);
43 if (debug) {
44 log.debug(" Fire begin() for " + rule);
45 }
46 rule.begin(namespaceURI, name, list);
47 } catch (Exception e) {
48 log.error("Begin event threw exception", e);
49 throw createSAXException(e);
50 } catch (Error e) {
51 log.error("Begin event threw error", e);
52 throw e;
53 }
54 }
55 } else {
56 if (debug) {
57 log.debug(" No rules found matching '" + match + "'.");
58 }
59 }
3、 characters方法;
1 //记录log
2 if (saxLog.isDebugEnabled()) {
3 saxLog.debug("characters(" + new String(buffer, start, length) + ")");
4 }
5 //类似<a>xxxxx</a>,将xxxxx暂存bodyText
6 bodyText.append(buffer, start, length);
4、 endElement方法;
1 //记log
2 boolean debug = log.isDebugEnabled();
3 if (debug) {
4 if (saxLog.isDebugEnabled()) {
5 saxLog.debug("endElement(" + namespaceURI + "," + localName + "," + qName + ")");
6 }
7 log.debug(" match='" + match + "'");
8 log.debug(" bodyText='" + bodyText + "'");
9 }
10
11 //用system属性格式化bodyText
12 bodyText = updateBodyText(bodyText);
13
14 String name = localName;
15 if ((name == null) || (name.length() < 1)) {
16 name = qName;
17 }
18
19 //获得startElment方法match出的rules
20 List<Rule> rules = matches.pop();
21 if ((rules != null) && (rules.size() > 0)) {
22 String bodyText = this.bodyText.toString();
23 //循环调用rule的body方法,将解析得到的当前标签的body传给rule的body方法,
24 for (int i = 0; i < rules.size(); i++) {
25 try {
26 Rule rule = rules.get(i);
27 if (debug) {
28 log.debug(" Fire body() for " + rule);
29 }
30 rule.body(namespaceURI, name, bodyText);
31 } catch (Exception e) {
32 log.error("Body event threw exception", e);
33 throw createSAXException(e);
34 } catch (Error e) {
35 log.error("Body event threw error", e);
36 throw e;
37 }
38 }
39 } else {
40 if (debug) {
41 log.debug(" No rules found matching '" + match + "'.");
42 }
43 if (rulesValidation) {
44 log.warn(" No rules found matching '" + match + "'.");
45 }
46 }
47
48 //弹出当前的bodyText,给其他标签使用
49 bodyText = bodyTexts.pop();
50 if (rules != null) {
51 //循环调用rule的end方法,注意是反向调用的,最先添加的rule最后被调用end方法
52 for (int i = 0; i < rules.size(); i++) {
53 int j = (rules.size() - i) - 1;
54 try {
55 Rule rule = rules.get(j);
56 if (debug) {
57 log.debug(" Fire end() for " + rule);
58 }
59 rule.end(namespaceURI, name);
60 } catch (Exception e) {
61 log.error("End event threw exception", e);
62 throw createSAXException(e);
63 } catch (Error e) {
64 log.error("End event threw error", e);
65 throw e;
66 }
67 }
68 }
69
70 // Recover the previous match expression
71 int slash = match.lastIndexOf('/');
72 if (slash >= 0) {
73 match = match.substring(0, slash);
74 } else {
75 match = "";
76 }
5、 endDocument方法;
1 //记log
2 if (saxLog.isDebugEnabled()) {
3 if (getCount() > 1) {
4 saxLog.debug("endDocument(): " + getCount() + " elements left");
5 } else {
6 saxLog.debug("endDocument()");
7 }
8 }
9
10 while (getCount() > 1) {
11 pop();
12 }
13
14 //循环调用所用rule的finish方法
15 Iterator<Rule> rules = getRules().rules().iterator();
16 while (rules.hasNext()) {
17 Rule rule = rules.next();
18 try {
19 rule.finish();
20 } catch (Exception e) {
21 log.error("Finish event threw exception", e);
22 throw createSAXException(e);
23 } catch (Error e) {
24 log.error("Finish event threw error", e);
25 throw e;
26 }
27 }
28 //初始化Digester的属性
29 clear();
Rule 部分
我们先看下rule,rule主要是这几个方法,这是所有rule的父类主要几个方法
1 Public abstract class Rule{
2 ………………
3 public Digester getDigester() {
4 return digester;
5 }
6
7 public void setDigester(Digester digester) {}
8 …………..
9 //startElement的时候调用
10 public void begin(String namespace, String name, Attributes attributes) throws Exception {
11 }
12 //endElement的时候调用
13 public void body(String namespace, String name, String text) throws Exception {
14
15 }
16 //endElement的时候调用
17 public void end(String namespace, String name) throws Exception {
18
19 }
20 //endDocument的时候调用
21 public void finish() throws Exception {
22 }
23 }
我们主要分析Digest类中AddRuleXXX中的几个Rule(CallMethodRule、CallParamRule、FactoryCreateRule、ObjectCreateRule、SetNextRule、SetPropertiesRule)
1、 CallMethodRule和CallParamRule配合使用(以xxxxx举例);
当解析到的时候,CallMethodRule的begin方法调用实例化parameter数组push进Digester,调用CallParamRule的begin方法赋值parameter数组
当解析到的时候,从Digester中peek出object,object反射调用method,传入begin的时候解析到的参数
2、 FactoryCreateRule(以xxxxx举例);
Begein方法,解析调用实现了ObjectCreationFactory接口的对象的createObject方法,传入标签的attribute创建对象,push进Digester
End方法,解析到Digester pop出begin push的object
3、 ObjectCreateRule(以<aclassname=’xxxxx’>xxxxx举例)Begin方法,通过catalinaClassLoader加载classname类名的类;
4、 SetNextRuleEnd方法,在parent对象上调用methodName参数的方法,child作为方法参数传入,下图是Digester中ArrayStack
5、 SetPropertiesRule(<aatt1=’1’att2=’2’>);
Begin方法,peek Digester的object,读取标签的attributes设置object的属性,括号中标签为例,设置object的att1属性值1,att2属性值2
Digester中的addRulexxx(String pattern,XXXX),其中pattern参数,和Digester方法startElement中match比较来过滤得到当前标签对应的 rules
下面是我调用Digester解析一个简单xml的小例子,如果想测试的话需要导入lib下面的2个jar包(tomcat-util-scan.jar、tomcat-util.jar)和bin下面的jar包(tomcat-juli.jar),
我没添加解析Address标签的rule,可以动手试下
实体类
Address类
package tomcat9DigesterTest;
public class Address {
private String contry;
private String state;
public String getContry() {
return contry;
}
public void setContry(String contry) {
this.contry = contry;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
Container类
package tomcat9DigesterTest;
import java.util.ArrayList;
import java.util.List;
public class Container {
private List<Note> notes = new ArrayList<Note>();
public void addNote(Note note){
notes.add(note);
}
public List<Note> getNotes() {
return notes;
}
public void writeNote(String issue){
for(Note note: notes){
note.setFrom("huangshi");
note.setTo("shenzhen");
note.setHeading("My Dear");
note.setBody("balabalabalabalabalabalabalabalabalabala");
}
}
}
Note类
package tomcat9DigesterTest;
public class Note {
private String from;
private String to;
private String heading;
private String body;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getHeading() {
return heading;
}
public void setHeading(String heading) {
this.heading = heading;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
Xml文件
<?xml version="1.0" ?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
<address contry="USA" state="L.A."></address>
<address contry="UK" state="London"></address>
</note>
Main类
1 package tomcat9DigesterTest;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5
6 import org.apache.tomcat.util.digester.CallMethodRule;
7 import org.apache.tomcat.util.digester.Digester;
8 import org.xml.sax.SAXException;
9
10 public class DigesterTest {
11
12 public static void main(String[] args) throws IOException, SAXException {
13 // TODO Auto-generated method stub
14 Digester digester= new Digester();
15
16 Container c = new Container();
17 digester.push(c);
18
19 InputStream is = DigesterTest.class.getClassLoader().getResourceAsStream("test.xml");
20
21 digester.setValidating(false);
22
23 digester.setNamespaceAware(false);
24
25 digester.addRule("note",new CallMethodRule("writeNote",0));
26 digester.addObjectCreate("note","tomcat9DigesterTest.Note");
27 digester.addSetNext("note", "addNote", "tomcat9DigesterTest.Note");
28
29 digester.parse(is);
30
31 System.out.println(c.getNotes().size());
32
33 Note note = c.getNotes().get(0);
34 System.out.println(note.getFrom()+":"+note.getTo()+":"+note.getHeading()+":"+note.getBody());
35 }
36 }
最近发现,tomcat用的xml解析方式,现在是apache commons下的一个项目
http://commons.apache.org/proper/commons-digester/guide/core.html