- 浏览: 23981 次
- 性别:
- 来自: 广州
最新评论
-
lh870003574:
liuyes 写道//以下是方法的重载,避免都调用getStr ...
StringManager与单例模式 -
liuyes:
//以下是方法的重载,避免都调用getString(Strin ...
StringManager与单例模式 -
sconio:
谢谢分享,值得学习一下
StringManager与单例模式
本文主要结合观察者模式,讲述Tomcat的生命周期管理。Tomcat的生命周期管理机制设计的非常优雅,在Tomcat启动时,只需要启动一个Server组件,就会启动所有的容器及对应的组件,并且触发这些容器的监听者,完成启动过程的设置。可以说是“一键式”启动的。停止过程也是一样。
本文首先简单介绍Tomcat中容器,组件及监听类的功能。因为Tomcat的生命周期管理应用了观察者模式,所以接下来会分析观察者模式,最后结合观察者模式及Tomcat源代码,详细说明Tomcat的生命周期管理。
一、几种基本的容器,组件及事件监听类(Listener)
1. Tomcat中有四种不同的容器:
-
Engine:代表整个Catalina servle引擎
-
Host:代表虚拟主机
-
Context:代表某个web应用
-
Wrapper:代表某个应用中的servlet
这些容器都扩展了Container接口(译为:容器,这也是为什么一般都称tomcat为容器而不是服务器的原因之一吧~)。更重要的是,这些容器都是父子的关系,Engine位于最顶层,一个Engine包含多个Host,一个Host(虚拟主机)包含多个Context(web应用),一个Context(web 应用)包含多个Wrapper(servlet),Wrapper位于最底层,没有孩子。当父容器启动时,相应的子容器也应该启动,子容器的子容器也启动。如此,只需要启动最顶层的容器,就会启动所有的子容器。
2. Tomcat的容器中有很多组件,如:
-
Logger:负责记录各种事件
-
Loader:负责加载类文件,如加载应用程序中的Servlet
-
Manager:负责管理session
-
Realm: 负责用户验证与授权
-
Pipeline:负责完成容器invoke方法的调用,对请求进行处理(责任链模式的经典应用)。
当tomcat容器启动时,这些组件也要启动,进行初始化。当容器停止时,这些组件也应该停止,进行相应的清理工作。比如管理用户session的Manager组件,在tomcat停止时,会将这些session序列化到sessions.ser文件中(位于%tomcat_home%/work/web appcation/sessions.ser)。下一次启动tomcat时,manager会将这个文件中的session反序列化到内存,将删除sessions.ser文件。这也是为什么重启tomcat时,正在访问网站的用户不用重新登录的原因。
3. 另外,还有一些类,它们并不像容器的基本组件(如Logger, Loader, Manager)一样,为容器的整个生命周期所调用,而仅仅对容器的某几个特定事件感兴趣。如:
-
ContextConfig: Context的收听者,在Context(web 应用)启动时,ContextConfig对web应用程序的配置文件web.xml进行分析,为Context生成Wrapper等对象,并与Context关联。在Context停止时,为Context清除这些关联的对象。
-
HostConfig: Host的收听者,在Host(虚拟主机)启动时,HostConfig会自动的为Host部署放置在webapps中的web应用程序。在Host停止时,为Host清除这些关联的对象。
-
EngineConfig:Engine的收听者,这个对比较简单,仅仅是在Engine启动与停止时做一些简单的记录。
这些监听类如何监听容器的特定事件呢?如何在特定事件发现时,调用监听类的特定方法以完成某些设置呢?如果理解了观察者模式,便能轻易的理解Tomcat的整个生命周期管理了。
二、观察者模式:
观察者模式又叫做发布-订阅(Publish/Subscribe)模式、源-监听(Source/Listener)模式。它定义了一种一对多的依赖关系,一个主题,多个观察者,当主题发生变化的时候,会主动的通知观察者,这样观察者便能针对主题发生的变化,执行某些对应的动作。观察者模式的应用非常广泛,如Java AWT事件模型,Servlet的监听器,Spring事件处理机制以及本文所讲述的Tomcat生命周期管理机制等等;应用如此的广泛,以至于Java直接提供API的支持,java.util.Observable和 java.util.Observer;
观察者模式的结构:
观察者模式包括以下角色:
抽象主题(Observable):定义了管理观察者的添加,删除和通知方法。
抽象观察者(Observer):定义了主题发生变化时,具体观察者必须执行的方法。
具体主题(Container):对观察者进行管理,并在自身发生变化时,通知观察者。
具体观察者(ContainerConfig):实现了当主题发生变化时,应该执行的动作。
下面是观察者模式的示例代码:
package com.scnulh.observer;
抽象主题:
/**
* 抽象主题
*/
public interface Observable {
//添加观察者
void addObserver(Observer observer);
//删除观察者
void deleteObserver(Observer observer);
//在事件发生时,通知观察者
void notifyObservers();
}
抽象观察者:
package com.scnulh.observer;
/**
* 抽象观察者
*/
public interface Observer {
/**
* 更新方法,观察者根据传入的主题对象获取主题的上下文
* 根据传入的Object对象判断发生了何种事件
* @param observable
* @param arg
*/
void update(Observable observable,Object arg);
}
具体主题:
package com.scnulh.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 具体主题,假设这是一个Tomcat的容器基类
* 有一个start方法,代表容器的启动,在启动过程中
* 通知所有观察者
*/
public class ContainerBase implements Observable{
//持有观察者的List
private List<Observer> observers=new ArrayList<Observer>();
//添加观察者
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
//删除观察者
@Override
public void deleteObserver(Observer observer) {
observers.remove(observer);
}
//通知所有观察者
@Override
public void notifyObservers() {
for(Observer observer:observers)
{
observer.update(this, "start");
}
}
//容器的启动方法,启动容器并调用notifyObservers方法通知所有观察者
public void start()
{
System.out.println("container start");
notifyObservers();
}
public static void main(String[] args) {
ContainerBase container=new ContainerBase();//声明一个容器
Observer observer=new ContainerConfig(); //声明一个监听类
container.addObserver(observer); //为容器添加监听类
container.start(); //启动容器
}
}
具体观察者:
package com.scnulh.observer;
/**
* 具体观察者,假设这是一个容器的监听类,
* 在tomcat容器启动时,处理tomcat的配置
*/
public class ContainerConfig implements Observer{
@Override
public void update(Observable observable, Object arg) {
String event=(String) arg;
if(event.equals("start"))
{
System.out.println("container starting, do container configs");
}
}
}
上述便是观察者模式的简单示例,之所以用ContainerBase和ContainerConfig作为具体主题和观察者,是因为后面要分析tomcat的容器(Container)和监听类(Config)的源代码,这里先模拟下他们的工作方式。
细心的读者很快就会发现,在具体主题ContainerBaser中,对观察者的管理方法其实是很固定的,无非就是声明一个Observer的集合,提供添加,删除,查找的方法。甚至连在主题发生变化时,通知观察者的方法也是固定的,即轮循的通知每一个观察者。如果每一个实现了主题接口的具体主题都要实现这些方法,无疑会造成重复,带来代码编写上的麻烦。为了消除重复,减少麻烦,可以提供一个类,实现主题对观察者的管理及通知。这正是java util包里Observable与Observer所做的。感兴趣的读者可以出看看,这里就不贴代码了。Tomcat没有直接使用这个Observable类,而是另外实现了一个LifecycleSupport类。
总的来说,观察者模式还是很好理解的,要让观察者模式用于实际,关键有两点,一点要提供主题与观察者的实现,第二是将观察者注册到具体主题中,这样主题发生变化时,才能通知到观察者。
三、Tomcat生命周期管理
理解了观察者模式,Tomcat的生命周期管理便很容易理解了。所涉及的类有:
-
Lifecycle:相当于抽象主题角色,所有的容器类与组件实现类都实现了这个接口。如StandardContext
-
LifecycleListener:相当于抽象观察者角色,具体的实现类有ContextConfig, HostConfig, EngineConfig类,它们在容器启动时与停止时触发。
-
LifecycleEvent:生命周期事件,对主题与发生的事件进行封装。
-
LifecycleSupport:生命周期管理的实用类,提供对观察者的添加,删除及通知观察者的方法。
-
LifecycleException:生命周期异常类。
Lifecycle接口
package com.apache.catalina;
import org.apache.catalina.LifecycleException;
public interface Lifecycle {
//生命周期内的六个事件
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
//观察者的管理与通知方法
public void addLifecycleListener(LifecycleListener listener);
public void removeLifecycleListener(LifecycleListener listener);
public LifecycleListener[] findLifecycleListeners();
//主题的启动与停止方法
public void start()throws LifecycleException;
public void stop()throws LifecycleException;
}
Lifecycle相当于观察者模式中的抽象主题角色(Observable),它定义了添加、删除及通知管理者的方法。
还定义了与生命周期相关的6个事件。Start和stop方法是Lifecycle最重要的两个方法,分别代表启动与停止。所有四种容器的标准实现类(StandardEngine, StandardHost, StandardContext,StandardWrapper)和基本组件(Logger,Loader,Manager等)的实现类都实现了Lifecycle接口,这意义着它们都是具体的观察者,具有启动和停止方法。容器启动时,主要做三件事:调用组件的启动方法,启动组件;调用子容器的启动方法,启动子容器;通知容器的观察者,使其执行相应的启动动作。子容器启动也做这三件事,这样整个Tomcat便启动了。Tomcat的停止也类似。
LifecycleListener接口:
package org.apache.catalina;
public interface LifecycleListener {
/**
* Acknowledge the occurrence of the specified event.
*
* @param event LifecycleEvent that has occurred
*/
public void lifecycleEvent(LifecycleEvent event);
}
LifecycleListener相当于观察者模式中的抽象观察者角色(Observer),可以看到它与Observer非常的类似,都只有一个更新自己的方法。不同的是,Observer 更新方法中,有两个参数:Observable与Object,而LifecycleListener中只有一个参数LifecycleEvent,这正是对前面两个参数的封装。
LifecycleEvent:
LifecycleEvent package org.apache.catalina;
import java.util.EventObject;
public final class LifecycleEvent
extends EventObject {
public LifecycleEvent(Lifecycle lifecycle, String type) {
this(lifecycle, type, null);
}
public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
super(lifecycle);
this.lifecycle = lifecycle;
this.type = type;
this.data = data;
}
private Object data = null;
private Lifecycle lifecycle = null;
private String type = null;
public Object getData() {
return (this.data);
}
public Lifecycle getLifecycle() {
return (this.lifecycle);
}
public String getType() {
return (this.type);
}
}
EventObject.
LifecycleSupport:
前面说过,抽象主题定义的添加,删除和通知观察者的方法都是很固定的,每个实现类实现起来都一样,这样就可以提供一个类来实现这些功能,具体的主题类直接调用便可以了。
package org.apache.catalina.util;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
public final class LifecycleSupport {
public LifecycleSupport(Lifecycle lifecycle) {
super();
this.lifecycle = lifecycle;
}
private Lifecycle lifecycle = null;
private LifecycleListener listeners[] = new LifecycleListener[0];
//添加一个观察者
public void addLifecycleListener(LifecycleListener listener) {
synchronized (listeners) {
LifecycleListener results[] =
new LifecycleListener[listeners.length + 1];
for (int i = 0; i < listeners.length; i++)
results[i] = listeners[i];
results[listeners.length] = listener;
listeners = results;
}
}
//找出所注册的观察者
public LifecycleListener[] findLifecycleListeners() {
return listeners;
}
//通知观察者
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = null;
synchronized (listeners) {
interested = (LifecycleListener[]) listeners.clone();
}
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
//删除一个观察者
public void removeLifecycleListener(LifecycleListener listener) {
synchronized (listeners) {
int n = -1;
for (int i = 0; i < listeners.length; i++) {
if (listeners[i] == listener) {
n = i;
break;
}
}
if (n < 0)
return;
LifecycleListener results[] =
new LifecycleListener[listeners.length - 1];
int j = 0;
for (int i = 0; i < listeners.length; i++) {
if (i != n)
results[j++] = listeners[i];
}
listeners = results;
}
}
}
这样,具体的主题实现抽象主题中对观察者的添加、删除与通知方法便非常简单了。
如在ContainerBase(容器的基本实现类)中:
protected LifecycleSupport lifecycle = new LifecycleSupport(this); public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } public LifecycleListener[] findLifecycleListeners() { return lifecycle.findLifecycleListeners(); } public void removeLifecycleListener(LifecycleListener listener) { lifecycle.removeLifecycleListener(listener); }
Lifecycle,LifecycleListener,LifecycleSupport,LifecycleEvent, LifecycleException及其具体的观察者与具体的主题之间的关系如下:
让我们再来看一下StandardContext的启动方法,StandartContext实现了Lifecycle,它的启动方法由上一级容器所调用。
public synchronized void start() throws LifecycleException {
//略过N多代码
//通知观察者,容器即将启动
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
//略过N多代码
// 启动loader,cluster,realm组件
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
//略过N多代码
//找出所有的子容器,并且启动
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).start();
}
//通知所有观察者,容器正在启动
lifecycle.fireLifecycleEvent(START_EVENT, null);
//启动Manager组件
if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start();
//通知所有观察者,容器已经启动
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
这里主要做三件事:调用组件的启动方法,启动组件;调用子容器的启动方法,启动子容器;通知容器的观察者,使其执行相应的启动动作。每一层次的容器都这样启动,最终整个Tomcat启动完毕。
因为源代码实在是太多了,没法全部贴出来,如果感兴趣,大家可以在附件上下下来研究。
推荐大家阅读How Tomcat Works这本书。就像书名一样,这本书详细的剖析了Tomcat运作机制,写的非常的好,在豆瓣这本书的评分是9.4分,而同样经典的Thinking in Java为9.1分,Effective Java为9.2分。Tomcat的源代码非常值得研究,里面用了很多的设计模式,如本文讲的观察者模式,还是上一篇所讲的单例模式,以及门面模式,责任链模式等等。
- HowTomcatWorksApps.zip (3.7 MB)
- 下载次数: 79
相关推荐
* 观察者模式:在 Tomcat 中,观察者模式被用于实现事件驱动的编程,例如 Lifecycle 事件的监听和处理。 * Singleton 模式:在 Tomcat 中,Singleton 模式被用于确保单例对象的唯一性,例如 Engine 和 Host 的实例。...
观察者模式在Tomcat中用于实现事件驱动的编程,例如在Servlet生命周期管理、Session管理和Container操作中。在这种模式中,Subject(抽象主题)管理多个Observer(观察者),当Subject的状态发生改变时,会通知所有...
Tomcat的架构设计中使用了多个设计模式,例如上述生命周期管理的实现运用了状态模式,事件监听机制则运用了观察者模式。这些设计模式的使用大大增强了Tomcat的扩展性、灵活性和可维护性。 总结来说,Tomcat的架构...
总之,Tomcat的系统架构基于一系列精心设计的模式,如模块化、可替换的Connector、容器化的请求处理、以及灵活的生命周期管理和事件监听。这些设计使得Tomcat能够在处理高并发请求的同时保持高效和可维护性。理解...
Lifecycle接口的引入是Tomcat设计中的一个重要决策,它提供了一种统一的方式来管理组件的生命周期,体现了设计模式中的一致性和可扩展性原则。 Tomcat的生命周期是由Server组件控制的,Server相当于整个Tomcat的...
在面试中,通常会考察你对单例、工厂、观察者、装饰器、适配器、代理、建造者、策略、模板方法、职责链、状态、访问者等经典设计模式的理解和应用。设计模式不仅关乎代码的可读性和可维护性,也反映了开发者解决问题...
\n\n- 事件监听:Tomcat利用观察者模式,实现组件间的通信和状态变更通知。例如,当添加或移除Connector时,Service会触发相应的事件,通知关联的组件进行更新。\n\n4. 总结\n\nTomcat的系统架构和设计模式体现了...
- **设计模式**:Tomcat广泛使用了工厂模式、装饰器模式和观察者模式。 - **线程模型**:Tomcat如何管理线程池,以及如何处理并发请求。 - **生命周期管理**:组件如何启动、停止,以及状态变化的处理。 - **配置...
了解其工作原理,如生命周期管理、线程模型、部署流程和性能优化是必要的。理解Context、Loader、Wrapper等组件的作用,以及如何配置和调整Tomcat以适应高并发场景。 2. **Mysql**:数据库知识是任何后端开发者的...
Tomcat的设计思想遵循了面向对象的原则,采用了工厂模式、观察者模式等多种设计模式,使得整个架构具有很好的扩展性和可维护性。开发者可以通过了解Tomcat内部的工作原理,更好地优化和调试基于Tomcat的应用程序。 ...
适合人群:具备一定编程基础,工作1-3年的研发人能学到什么:@工厂模式、策略模式、观察者模式等都是怎么在Spring中体现的: ②IOC, AOP、代理、切面、循环依赖都是如何设计和实现的。阅读建议:此资源以开发简化版Spring...
- **生命周期管理**:Tomcat如何启动、停止,以及容器内部对象的创建和销毁。 - **线程模型**:了解Tomcat如何处理并发请求,如Executor线程池的使用。 - **部署和加载机制**:学习如何解析并加载Web应用的配置...
6. **Servlet和JSP的生命周期**:如何加载、初始化、服务和卸载Servlet和JSP。 7. **安全性**:Tomcat的内置安全机制,如 Realm、Role、User和Access Control。 8. **部署和热部署**:如何自动检测和处理新的或更新...
- **观察者模式**:例如在`Lifecycle`接口和`LifecycleListener`接口中,用于监听容器状态变化并作出相应。 - **责任链模式**:`RequestDispatcher`的请求转发机制。 四、源码阅读指南 源码阅读可以从以下几个方面...