Connector、Container是Tomcat的核组件
Service:由一个Container和多个Connector作为一个整体而提供的功能或服务,一个Service可以有多个Connector,但只能有一个Container
容器。
Server:控制Tomcat的生命周期,即提供Service的生成环境,控制Service的生命周期
Service接口中的方法主要为了关联Connectors和Container。
Service接口并没有控制它的各组件的生命周期,所有组件的生命周期由lifeCycle接口定义
StandardService :Tomcat的中的Service接口的标准的实现类,实现了Service接口和lifeCycle接口。这就可以控制组件生命周期
清单 1. StandardService. SetContainer
public void setContainer(Container container) { Container oldContainer = this.container; if ((oldContainer != null) && (oldContainer instanceof Engine)) ((Engine) oldContainer).setService(null); this.container = container; if ((this.container != null) && (this.container instanceof Engine)) ((Engine) this.container).setService(this); if (started && (this.container != null) && (this.container instanceof Lifecycle)) { try { ((Lifecycle) this.container).start(); } catch (LifecycleException e) { ; } } synchronized (connectors) { for (int i = 0; i < connectors.length; i++) connectors[i].setContainer(this.container); } if (started && (oldContainer != null) && (oldContainer instanceof Lifecycle)) { try { ((Lifecycle) oldContainer).stop(); } catch (LifecycleException e) { ; } } support.firePropertyChange("container", oldContainer, this.container); }
这段代码很简单,其实就是先判断当前的这个 Service 有没有已经关联了 Container,如果已经关联了,那么去掉这个关联关系—— oldContainer.setService(null)。如果这个 oldContainer 已经被启动了,结束它的生命周期。然后再替换新的关联、再初始化并开始这个新的 Container 的生命周期。最后将这个过程通知感兴趣的事件监听程序。这里值得注意的地方就是,修改 Container 时要将新的 Container 关联到每个 Connector,还好 Container 和 Connector 没有双向关联,不然这个关联关系将会很难维护。
PropertyChangeSupport对象的firePropertyChange方法,会将一个事件发送给所有已经注册的监听器。该方法有三个参数:属性的名字、旧的值以及新的值。属性的值必须是对象,如果是简单数据类型,则必须进行包装。listeners.firePropertyChange("ourString", oldString, newString);
清单 2. StandardService. addConnector
public void addConnector(Connector connector) { synchronized (connectors) { connector.setContainer(this.container); connector.setService(this); Connector results[] = new Connector[connectors.length + 1]; System.arraycopy(connectors, 0, results, 0, connectors.length); results[connectors.length] = connector; connectors = results; if (initialized) { try { connector.initialize(); } catch (LifecycleException e) { e.printStackTrace(System.err); } } if (started && (connector instanceof Lifecycle)) { try { ((Lifecycle) connector).start(); } catch (LifecycleException e) { ; } } support.firePropertyChange("connector", null, connector); } }
上面是 addConnector 方法,这个方法也很简单,首先是设置关联关系,然后是初始化工作,开始新的生命周期。这里值得一提的是,注意 Connector 用的是数组而不是 List 集合,这个从性能角度考虑可以理解,有趣的是这里用了数组但是并没有向我们平常那样,一开始就分配一个固定大小的数组,它这里的实现机制是:重新创建一个当前大小的数组对象,然后将原来的数组对象 copy 到新的数组中,这种方式实现了类似的动态数组的功能,这种实现方式,值得我们以后拿来借鉴。
---------------------------------------------------------------------------------------------------------------------
以Server为居
Server :提供一个接口让其它程序能够访问到这个 Service 集合、
同时要维护它所包含的所有 Service 的生命周期,包括如何初始化、如何结束服务、如何找到别人要访问的 Service。
图 4. Server 的类结构图
它的标准实现类 StandardServer 实现了上面这些方法,同时也实现了 Lifecycle、MbeanRegistration 两个接口的所有方法,下面主要看一下 StandardServer 重要的一个方法 addService 的实现:
清单 3. StandardServer.addService
public void addService(Service service) { service.setServer(this); synchronized (services) { Service results[] = new Service[services.length + 1]; System.arraycopy(services, 0, results, 0, services.length); results[services.length] = service; services = results; if (initialized) { try { service.initialize(); } catch (LifecycleException e) { e.printStackTrace(System.err); } } if (started && (service instanceof Lifecycle)) { try { ((Lifecycle) service).start(); } catch (LifecycleException e) { ; } } support.firePropertyChange("service", null, service); } }
从上面第一句就知道了 Service 和 Server 是相互关联的,Server 也是和 Service 管理 Connector 一样管理它,也是将 Service 放在一个数组中,后面部分的代码也是管理这个新加进来的 Service 的生命周期。Tomcat6 中也是没有什么变化的。
组件的生命线“Lifecycle
前面一直在说 Service 和 Server 管理它下面组件的生命周期,那它们是如何管理的呢?
Tomcat 中组件的生命周期是通过 Lifecycle 接口来控制的,组件只要继承这个接口并实现其中的方法就可以统一被拥有它的组件控制了,这样一层一层的直到一个最高级的组件就可以控制 Tomcat 中所有组件的生命周期,这个最高的组件就是 Server,
控制 Server 的是 Startup,也就是您启动和关闭 Tomcat。
下面是 Lifecycle 接口的类结构图:
图 5. Lifecycle 类结构图
---------------------------------------------------------------------------------------------------------------------
A、Connector: 1、负责接收浏览器发过来的TCP请求,
2、创建一个Request和Response对象,用于和请求端交换数据
3、产生一个线程处理这个请求,并把Request和Response对象传给处理这个请求的线程,处理这个请求由Container负责
Connector 最重要的功能就是接收连接请求然后分配线程让 Container 来处理这个请求,所以这必然是多线程的,多线程的处理是 Connector 设计的核心。
Tomcat5 将这个过程更加细化,它将 Connector 划分成 Connector、Processor、Protocol, 另外 Coyote 也定义自己的 Request 和 Response 对象。
Connector处理一个请求的顺序图:
---------------------------------------------------------------------------------------------------------------------
B、Container
Servlet 容器“Container”
Container 是容器的父接口,所有子容器都必须实现这个接口,
Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine、Host、Context、Wrapper,
这四个组件不是平行的,而是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。
通常一个 Servlet class 对应一个 Wrapper,如果有多个 Servlet 就可以定义多个 Wrapper,
如果有多个 Wrapper 就要定义一个更高的 Container 了,
-----------------------------------------------------------------------------------------------------------------------------
【
PropertyChangeSupport类
最近看代码一直碰到这个类,先做一个总结。
先看PropertyChangeSupport类的官方文档解释:
This is a utility class that can be used by beans that support bound properties. You can use an instance of this class as a member field of your bean and delegate various work to it.
1.关联属性
在JavaBean的设计中,按照属性的不同作用又细分为四类:单值属性、索引属性、关联属性、限制属性。关联属性,也称之为绑定属性。绑定属性会在属性值发生变化时,通知所有相关的监听器。为了实现一个绑定属性,必须实现两个机制。
1) 无论何时,只要属性的值发生变化,该bean必须发送一个PropertyChange事件给所有已注册的监听器。
2) 为了使监听器能够注册,bean必须实现以下两个方法:void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangerListener(PropertyChangerListener listener);
2.使用PropertyChangeSupport管理监听器
可以通过java.bean包下的PropertyChangeSupport类来管理监听器。要使用这个类,bean必须有一个此类的数据域。private PropertyChangeSupport listeners = new PropertyChangeSupport(this);
这样可以将添加和移除监听器的任务交给这个对象。public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
listeners.removePropertyChangeListener(listener);
}
当bean的属性发生变化时,使用PropertyChangeSupport对象的firePropertyChange方法,会将一个事件发送给所有已经注册的监听器。该方法有三个参数:属性的名字、旧的值以及新的值。属性的值必须是对象,如果是简单数据类型,则必须进行包装。listeners.firePropertyChange("ourString", oldString, newString);
所有注册的监听器实现PropertyChangeListener接口,该接口中只有一个方法。public void propertyChange(PropertyChangeEvent e)
当bean的属性值发生变化时,该方法中的代码就会被触发。可以通过e.getOldValue();
e.getNewValue();
来得到changes.firePropertyChange("ourString", oldString, newString);中的oldString和newString。
3.为什么要使用PropertyChangeSupport
使用这个类管理监听器的好处是,它是线程安全的。如果使用一个循环体来set Bean的属性,则这个类可以保证所有监听器执行触发事件的有序。
还有一个好处是,这个类支持fire带索引的属性改变事件(详见java.bean.IndexedPropertyChangeEvent)。此时向注册的监听器发送一个PropertyChangeEvent的方法为:void fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue)
4.实例代码
public class SomeBean {
private String property;
private PropertyChangeSupport changeSupport;
public void setProperty(String newValue) {
String oldValue = property;
property = newValue;
changeSupport.firePropertyChange("property", oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
changeSupport.add(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
changeSupport.remove(l);
}
}
】