Tomcat7 源码分析--Degister使用

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等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值