cheng029 发表于 2017-1-24 08:38:46

tomcat源码分析三

  这里看下tomcat中责任链模式的使用。首先看下什么是责任链模式,责任链模式是抽象的处理者和具体的处理者组成。而具体处理者都拥有其下家的应用,从而形成处理链。直到有处理者处理,并且可以任意扩展链的长度。从简单点的开始,在阎宏《java与模式》一书中,有一个击鼓传花的例子。对责任链模式有很好的讲解,这里就不啰嗦了。通过书中例子,应该可以理解责任链的处理方式,这里主要看下tomcat中的使用。
          首先我们来看下tomcat的配置文件,在conf/server.xml里面。
  这个事原始配置文件:
  
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"   connectionTimeout="20000" redirectPort="8443"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
</Host>
</Engine>
</Service>

  按照需求更改后的<Engine></Engine>以内的配置文件,其他部分没有变动:



<Host name="wap.**.cc" appBase="d:/test/wapPortal" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
<Context crossContext="true" debug="5" displayName="tawap" docBase="d:/test/wapPortal" path="" reloadable="true">
<Resource auth="Container" name="jdbc/wapportal" type="javax.sql.DataSource" maxWait="10000" maxIdle="30" maxActive="100" username="root" password="admin" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/onetouch?useUnicode=true&characterEncoding=UTF-8"/>
<ResourceLink global="jdbc/wapportal" name="jdbc/wapportal" type="javax.sql.DataSource"/>
</Context>
</Host>
<Host name=www.**.com appBase="d:/test" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
<Context path="/" reloadable="true" docBase="d:/test">
</Context>
</Host>

  tomcat对这部分的处理,就是采用责任链的模式。接下来看下实现责任链的类关系,如图:

  那么从tomcat哪部分是配置文件呢?我们看下StandardService代码
  
public class StandardService implements Lifecycle, Service, MBeanRegistration

  依照责任链模式,显然和UML图中的不属于同一个类别,所以其他的部分不构成责任链。由责任链的定义,我们主要看下其上家和下家的定义。


  1.首先StandardEngine类:
  
//setParent方法(上家),可以看出是没有上家的
public void setParent(Container container) {
throw new IllegalArgumentException
(sm.getString("standardEngine.notParent"));
}
//addChild方法如下(下家),可以看到Host为其下家
public void addChild(Container child) {
if (!(child instanceof Host))
throw new IllegalArgumentException
(sm.getString("standardEngine.notHost"));
super.addChild(child);
}
//有必要设置默认值
public void setDefaultHost(String host) {
String oldDefaultHost = this.defaultHost;
if (host == null) {
this.defaultHost = null;
} else {
this.defaultHost = host.toLowerCase();
}
support.firePropertyChange("defaultHost", oldDefaultHost,
this.defaultHost);
}

  2.依照这个顺序,很明显接下来看下Host了。



//下家为Context
public void addChild(Container child) {
if (child instanceof Lifecycle) {
((Lifecycle) child).addLifecycleListener(
new MemoryLeakTrackingListener());
}
if (!(child instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardHost.notContext"));
super.addChild(child);
}

  3.Context的处理,standContext类中addChild方法,可以从代码中看出下家是Wrapper。
  
   Wrapper oldJspServlet = null;
if (!(child instanceof Wrapper)) {
throw new IllegalArgumentException
(sm.getString("standardContext.notWrapper"));
}
Wrapper wrapper = (Wrapper) child;



  4.Wrapper对应实现

//可以看到这层到了责任链处理的终点了
public void addChild(Container child) {
throw new IllegalStateException
(sm.getString("standardWrapper.notChild"));
}
//该类中还限制了其上家
public void setParent(Container container) {
if ((container != null) &&
!(container instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardWrapper.notContext"));
if (container instanceof StandardContext) {
swallowOutput = ((StandardContext)container).getSwallowOutput();
unloadDelay = ((StandardContext)container).getUnloadDelay();
}
super.setParent(container);
}

  tomcat启动的时候,是肯定要从配置文件读取数据启动的,那么是怎么来启动这个链的呢?接下来看下这部分的启动,
  首先是standardEngine类中的启动了。

// Standard container startup
super.start();
  那么看下父类中的启动代码了,即CantainerBase中start()如下:

Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children instanceof Lifecycle)
((Lifecycle) children).start();
}

  根据上面的代码addChild和这里的 findChildren(),维护了一个链。这样就可以按照配置文件的不同配置,来启动加载了。
  同时,我们可以看到的是责任链的整个部分都实现了观察者的Lifecycle接口,从而它们都是在观察者模式中处于主题对象的角色。观察者对象对其作相应的处理,启动前,启动,启动后,观察者会有不同的处理。在stop()中有关闭的处理,这里看下启动部分start()中观察者处理代码,。
  

//start中的部分代码
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
started = true;
//省略部分代码
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
// Start our thread
threadStart();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
页: [1]
查看完整版本: tomcat源码分析三