Bootstrap在实例化之后会实例化一个Catalina的实例.当命令是启动的时候,就调用了Catalina的start来启用服务器.也就是构造并实例化了Tomcat的连接器和容器.这个所谓的连接器和容器也是Java编码,在Tomcat的实现之内.他们之间的关系并不是通过硬编码来实现的.而是通过了Server.xml等配置文件.而Digester就是用来解析xml配置文件,并根据他们的关系来生成和配置文件的属性.
首先,我们应该知道的是Digester是apache基金会的一个开源项目.它能够根据编码人员定义的规则去解析XML并生成和配置相应的属性.Digester是它的主类.对于XML文档中的每个元素,它的对象会检查是否要执行预定义的事件.这些事件也就是继承了Rule的类,Rule类有2个方法,begin和end.当Digester实例遇到某个模式的开始标签的时候调用相应Rule对象的begin方法,遇到结束的话调用相应Rule对象的end方法.这一系列的解析过程是一个类似堆栈的过程.Digester对象中有一个stack属性来持有这些中间的对象.了解了上述规则后,我们开始看Catalina的start方法.
if (getServer() == null) { load(); } if (getServer() == null) { log.fatal("Cannot start server. Server instance is not configured."); return; } long t1 = System.nanoTime(); // Start the new server try { getServer().start(); } catch (LifecycleException e) { log.error("Catalina.start: ", e); }
刚刚开始启动的时候getServer返回的是空,于是调用Catalina的load方法,这个方法里面构造出Server等.getServer().start()是启动服务器.这里指谈load方法的细节,它是通过Digester,解析Server.xml文件并构造出Server对象及设置其中属性,并出示话Server.
Digester digester = createStartDigester(); //省略文件的读取. 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; } finally { try { inputStream.close(); } catch (IOException e) { // Ignore } } getServer().setCatalina(this); // Stream redirection initStreams(); // Start the new server try { getServer().init(); } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new java.lang.Error(e); } else { log.error("Catalina.start", e); } }
上述是load方法的主体代码.createStartDigester是创建并设置它的事件(Rule)属性.接着digester.push(this)就是在解析Server.xml之前,将Catalina对象放到digester解析对象的堆栈顶.此时这个对象栈只有Catalina对象这个元素.接着是digester.parse(inputSource)对文件进行解析.后续的代码是Server的初始化,此处不做赘述.
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"); //省略一些Rule规则.
上述代码是createStartDigester方法的主要代码.addObjectCreate的意思是匹配xml中的根元素Server标签,按照它的className属性的配置创建一个对象放入栈顶,如果Server标签没有className属性则默认是org.apache.catalina.core.StandardServer类的对象. addSetProperties方法是设置Server对应的属性,也就是匹配它的属性与该对象的属性,调用对象的set方法.addSetNext是调用栈顶的第二个元素的setServer方法把栈顶元素作为参数传入,其中参数类型是org.apache.catalina.Server.上面所描述的是规则的使用.上面3个方法都是添加对应规则的方法.
//addObjectCreate所使用的规则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); } //addSetNext所使用的规则SetNextRule的end方法 public void end(String namespace, String name) throws Exception { // Identify the objects to be used 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 + ")"); } } // Call the specified method IntrospectionUtils.callMethod1(parent, methodName, child, paramType, digester.getClassLoader()); } //SetPropertiesRule的begin方法比较长就不贴出.
上述代码是addObjectCreate和addSetNext所使用规则的主要起作用的方法,通过代码很容易看出addObjectCreate的作用就是Instantiate the new object and push it on the context stack而addSetNext的作用就是调用栈顶第一个元素的方法并把栈顶元素传入.
有了对Digester的了解,想要从配置文件Server.xml中探寻Catalina初始化Server,及之中组件的关系就不是什么难事了.