参考网上博客内容整理而来
1. 工厂模式
在Tomcat启动的过程中,调用Connector组件的startInternal()方法:
//Connector.java
protocolHandler.start();
//AbstractProtocol.java
endpoint.start();
//AbstractEndpoint.java
bind();
//JIoEndpoint.java
public void bind() throws Exception {
// Initialize thread count defaults for acceptor
if (acceptorThreadCount == 0) {
acceptorThreadCount = 1;
}
// Initialize maxConnections
if (getMaxConnections() == 0) {
// User hasn't set a value - use the default
setMaxConnections(getMaxThreadsExecutor(true));
}
if (serverSocketFactory == null) {
if (isSSLEnabled()) {
//获取serverSocket的工厂类
serverSocketFactory =
handler.getSslImplementation().getServerSocketFactory(this);
} else {
//获取serverSocket的工厂类
serverSocketFactory = new DefaultServerSocketFactory(this);
}
}
if (serverSocket == null) {
try {
if (getAddress() == null) {
//调用工厂类创建实例
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog());
} else {
//调用工厂类创建实例
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog(), getAddress());
}
} catch (BindException orig) {
String msg;
if (getAddress() == null)
msg = orig.getMessage() + " <null>:" + getPort();
else
msg = orig.getMessage() + " " +
getAddress().toString() + ":" + getPort();
BindException be = new BindException(msg);
be.initCause(orig);
throw be;
}
}
}
2. 模版模式
在讲解Tomcat中组件的生命周期管理的相关文章中,我们了解到Lifecycle接口的相关类图如下:
相关文章链接:http://www.cnblogs.com/coldridgeValley/p/5816406.html
LifecycleBase类中的start()方法:
@Override
public final synchronized void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
setStateInternal(LifecycleState.STARTING_PREP, null, false);
try {
//抽象方法,子类具体实现
startInternal();
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
}
可以看到首先每个组件都会调用start()方法,都会调用到上面贴出来的LifecycleBase类中的start()方法。前面调用一些通用的方法,例如日志的记录,组件状态的检查。但是每个子组件又会根据启动做一些自己特殊的动作,调用了startInternal()方法。我们查看startInternal()方法:
protected abstract void startInternal() throws LifecycleException;
这个是一个抽象方法,是需要子类去实现的,所以子类调用start()方法,最终调用的子类的实现的startInternal()方法。最后也都是一些公共的方法。所以我们来总结Tomcat中的模版设计模式就是把公共的动作抽象到父类中,在父类的方法中调用一些抽象方法,而这些抽象方法留给子类去实现,从而完成公共动作和特殊动作的分离。
3. 观察者模式
//LifecycleBase.java
setStateInternal(LifecycleState.STARTED, null, false);
...
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
if (log.isDebugEnabled()) {
log.debug(sm.getString("lifecycleBase.setState", this, state));
}
if (check) {
// Must have been triggered by one of the abstract methods (assume
// code in this class is correct)
// null is never a valid state
if (state == null) {
invalidTransition("null");
// Unreachable code - here to stop eclipse complaining about
// a possible NPE further down the method
return;
}
// Any method can transition to failed
// startInternal() permits STARTING_PREP to STARTING
// stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
// STOPPING
if (!(state == LifecycleState.FAILED ||
(this.state == LifecycleState.STARTING_PREP &&
state == LifecycleState.STARTING) ||
(this.state == LifecycleState.STOPPING_PREP &&
state == LifecycleState.STOPPING) ||
(this.state == LifecycleState.FAILED &&
state == LifecycleState.STOPPING))) {
// No other transition permitted
invalidTransition(state.name());
}
}
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}
...
protected void fireLifecycleEvent(String type, Object data) {
lifecycle.fireLifecycleEvent(type, data);
}
...
//LifecycleSupport.java
/**
* Notify all lifecycle event listeners that a particular event has
* occurred for this Container. The default implementation performs
* this notification synchronously using the calling thread.
*
* @param type Event type
* @param data Event data
*/
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
for (LifecycleListener listener : listeners) {
listener.lifecycleEvent(event);
}
}
也就是在Tomcat的组件生命周期状态只要一变化,Tomcat就会通知改组件的所有的观察者,把状态变化通知到所有的观察者,看是否有观察者对相关组件的状态变化感兴趣。
4. 责任链模式
讲解Tomcat内部的Pipeline 机制: http://www.cnblogs.com/coldridgeValley/p/5816414.html
关于Tomcat处理Http请求的文章(http://www.cnblogs.com/coldridgeValley/p/6234629.html)
整个Http请求被处理的流程:
请求被Connector组件接收,创建Request和Response对象。
Connector将Request和Response交给Container,先通过Engine的pipeline组件流经内部的每个Valve。
请求流转到Host的pipeline组件中,并且经过内部Valve的过滤。
请求流转到Context的pipeline组件中,并且经过内部的Valve的过滤。
请求流转到Wrapper的pipeline组件中,并且经过内部的Valve的过滤。
Wrapper内部的WrapperValve创建FilterChain实例,调用指定的Servlet实例处理请求。
返回
可以从以上流程中看到这个是一个标准的责任链模式,请求经过一个组件过滤后到下一个组件,每个组件只处理自己相关的事务。
5. 外观模式
外观设计模式又叫门面设计模式,外观模式主要功能是封装了子系统的具体实现,提供统一的外观类给外部系统,这样当子系统内部实现发生变化的时候,不会影响到外部系统。
Tomcat内部使用了大量的外观设计模式。
从上图可以看到,request,response,session等常用类都使用了外观设计模式。我们以我们常用的session类说明。
我们都知道我们在写servlet的时候,使用session的方式是request.getSession(),首先我们这里的request具体的类是RequestFacade,我们查看getSession()方法:
//RequestFacade.java
@Override
public HttpSession getSession() {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
return getSession(true);
}
//Request.java
@Override
public HttpSession getSession(boolean create) {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
if (SecurityUtil.isPackageProtectionEnabled()){
return AccessController.
doPrivileged(new GetSessionPrivilegedAction(create));
} else {
//委托给了request,而request是Request的实例
return request.getSession(create);
}
}
/**
* Return the session associated with this Request, creating one
* if necessary and requested.
*
* @param create Create a new session if one does not exist
*/
@Override
public HttpSession getSession(boolean create) {
Session session = doGetSession(create);
if (session == null) {
return null;
}
return session.getSession();
}
//StandardSession.java
/**
* Return the <code>HttpSession</code> for which this object
* is the facade.
*/
@Override
public HttpSession getSession() {
if (facade == null){
if (SecurityUtil.isPackageProtectionEnabled()){
final StandardSession fsession = this;
facade = AccessController.doPrivileged(
new PrivilegedAction<StandardSessionFacade>(){
@Override
public StandardSessionFacade run(){
return new StandardSessionFacade(fsession);
}
});
} else {
facade = new StandardSessionFacade(this);
}
}
return (facade);
}
/**
* Construct a new session facade.
*
* @param session The session instance to wrap
*/
public StandardSessionFacade(HttpSession session) {
this.session = session;
}
Tomcat使用外观设计模式主要是为了保证主要类的安全,防止程序员使用核心类的不需要暴露出去的功能。
6. 命令模式
定义
命令模式将请求封装为一个命令,将命令发送者和命令接受者解耦,并且所有命令对客户端来说都有统一的调用接口,使用命令模式还可以支持命令的撤销操作,在很多GUI程序中大量使用了此模式。
接下来我们来说一个场景大家感受下,我们有时候可能会遇到接口方法参数过多的问题,这样的接口不仅看起来丑陋而且不方便阅读,对客户端不友好。遇到这种情况我们可能选择将各种参数打包为一个参数对象,接口只需要一个参数对象即可,但是在具体的接口实现中,我们又要做条件判断根据参数值的不同做出不同的响应操作,这个时候其实就可以考虑将不同的逻辑实现和各种参数通过命令打包,然后提供一个命令工厂,客户端通过工厂生产出命令,然后直接调用即可。
其实在日常生活中,命令模式也很常见,比如公司老大给你分配了个任务,让你去做,他可能不关心你具体怎么做的,你做完了以后告诉他结果即可。
命令模式在Tomcat的应用
命令模式在Tomcat中主要是应用在对请求的处理过程中,Tomcat的实现中,根据它支持两种协议AJP和Http,而在具体的IO实现中,又分为Java同步阻赛IO,Java同步非祖塞IO,以及采用APRApache Portable Runtime 支持库,因此Tomcat统一了org.apache.coyote.Processor接口,根据协议和IO实现的不同通过不同的Process子类去实现,Connector作为客户端每次只需要根据具体的协议和IO实现创建对应的Process执行即可。下面我们来看一下命令模式在Tomcat中实现的相关类图:
通过上图我们可以清楚的看到,Tomcat首先根据协议的不同将Processor分为了Ajp和Http两组,然后又根据具体的IO实现方式的不同,将每一组都会实现同步祖塞IO,同步非祖塞IO,以及APR的Processor。 接下来我们再来看一个类图,我们就可以更加清楚的看到Tomcat中是如何利用命令模式来根据不同的协议以及IO实现方式来处理请求的。我们来看一下Tomcat中关于ProtocolHandler的类图。
通过上图我们可以看到针对每一种协议和IO实现方式的组合,都会有相应的协议处理类,而每个协议处理类都会有一个Handler,而每一个Handler在运行的时候就会创建出对应的Processor,比如AjpProtocol.AjpConnectionHandler创建AjpProcessor处理器,其它的类似。
通过上面的描述,我们可以看出Tomcat接受请求的处理流程如下:
Connector通过对应的Endpint监听Socket连接,当对应的端口有连接进来的时候,对应的Endpoint就会通过对应的Handler类处理,而Handler处理的时候,又会创建对应的Processor处理,而对应的Processor命令对象会解析Socket流的数据,然后生成Request和Response对象,最终通过上面说的责任链模式一步步的通过各个容器。