摘要:本文主要介紹了Tomcat中使用的設計模式。
今天我們來聊聊Tomcat運用到的設計模式。通過閱讀之前的源碼我們了解到Tomcat中使用了很多設計模式,例如我們在看代碼中提到的工廠,模版等設計模式。今天這篇文章我們來總結下Tomcat中使用的常見的設計模式,通過學習Tomcat中使用設計模式的方式能給我們以后的程序設計中提供一定的借鑒作用。最后需要提一下,我們這篇文章只關注Tomcat內部對於設計模式的使用,並不會花大量筆墨講述設計模式的原理思想,讀者可自己查詢相關設計模式的資料。
工廠模式
工廠模式可以參考這篇文章:http://www.cnblogs.com/zhouqiang/archive/2012/07/20/2601365.html
示例一:
在Tomcat啟動的過程中,調用Connector
組件的startInternal()
方法:
//代碼清單1
protocolHandler.start();
繼續深入
//代碼清單2
endpoint.start();
繼續深入
//代碼清單3
bind();
我們查看bind()
方法:
//代碼清單4
@Override
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()) {
//11 獲取serverSocket的工廠類
serverSocketFactory =
handler.getSslImplementation().getServerSocketFactory(this);
} else {
//11 獲取serverSocket的工廠類
serverSocketFactory = new DefaultServerSocketFactory(this);
}
}
if (serverSocket == null) {
try {
if (getAddress() == null) {
//22 調用工廠類創建實例
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog());
} else {
//22 調用工廠類創建實例
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;
}
}
}
可以看到Tomcat在啟動的過程中關於SeverSocket
的生成使用的是工廠生成的而不是自己new出來的。實際上這些socket就是后期tomcat接收請求時的socket。我們再看一個例子。
示例二:
Tomcat在處理請求的時候,當請求到達StandardWrapperValve
的invoke()
方法中,在調用指定的servelt
實例之前,先創建對應servlet
的filterChain
,代碼如下:
//代碼清單5
//獲取工廠實例
ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
//使用工廠實例創建 filterChain實例
ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
模版模式
在講解Tomcat中組件的生命周期管理的相關文章中,我們了解到Lifecycle
接口的相關類圖如下:
相關文章鏈接:http://www.cnblogs.com/coldridgeValley/p/5816406.html
我們都知道Tomcat在啟動的過程中主要的兩個方法init()
和start()
,這里我們以start()
為例,LifecycleBase
類中的start()
方法:
//代碼清單6
/**
* {@inheritDoc}
*/
@Override
public final synchronized void start() throws LifecycleException {
//1111111
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 {
//2222222222
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);
}
//33333333
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 {
//4444444444
setStateInternal(LifecycleState.STARTED, null, false);
}
}
可以看到首先每個組件都會調用start()
方法,都會調用到上面貼出來的LifecycleBase
類中的start()
方法。在標注1到標注2的地方可以看出都是一些通用的方法,例如日志的記錄,組件狀態的檢查。但是每個子組件又會根據啟動做一些自己特殊的動作,所以在標注2的地方調用了startInternal()
方法。我們查看startInternal()
方法:
//代碼清單7
protected abstract void startInternal() throws LifecycleException;
這個是一個抽象方法,是需要子類去實現的,所以子類調用start()
方法,最終調用的子類的實現的startInternal()
方法。而標注3到最后也都是一些公共的方法。所以我們來總結Tomcat中的模版設計模式就是把公共的動作抽象到父類中,在父類的方法中調用一些抽象方法,而這些抽象方法留給子類去實現,從而完成公共動作和特殊動作的分離。
觀察者模式
我們繼續上一個模版設計模式的代碼繼續看,也就是代碼清單6標注4的地方的代碼:
//代碼清單8
//4444444444
setStateInternal(LifecycleState.STARTED, null, false);
繼續查看其代碼:
//代碼清單9
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) {
//1111111
fireLifecycleEvent(lifecycleEvent, data);
}
}
繼續查看標注1的地方:
//代碼清單10
protected void fireLifecycleEvent(String type, Object data) {
lifecycle.fireLifecycleEvent(type, data);
}
最終調用的是LifecycleSupport
類的fireLifecycleEvent()
方法:
//代碼清單11
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = listeners;
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
也就是在Tomcat的組件生命周期狀態只要一變化,Tomcat就會通知改組件的所有的觀察者,把狀態變化通知到所有的觀察者,看是否有觀察者對相關組件的狀態變化感興趣。
責任鏈模式
我們在講解Tomcat內部的Pipeline 機制的時候寫過這樣一篇文章: http://www.cnblogs.com/coldridgeValley/p/5816414.html
在這篇文章中,我們引用過如下一張圖片
當時我們講解Pipeline的時候使用這張圖片主要是為了介紹Valve
這個概念。其實如果讀過之前的關於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
實例處理請求。- 返回
可以從以上流程中看到這個是一個標准的責任鏈模式,請求經過一個組件過濾后到下一個組件,每個組件只處理自己相關的事務。
外觀模式
外觀設計模式又叫門面設計模式,外觀模式主要功能是封裝了子系統的具體實現,提供統一的外觀類給外部系統,這樣當子系統內部實現發生變化的時候,不會影響到外部系統。
Tomcat內部使用了大量的外觀設計模式。
從上圖可以看到,request
,response
,session
等常用類都使用了外觀設計模式。我們以我們常用的session
類說明。
我們都知道我們在寫servlet
的時候,使用session
的方式是request.getSession()
,首先我們這里的request
具體的類是RequestFacade
,我們查看getSession()
方法:
//代碼清單12
@Override
public HttpSession getSession() {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
return getSession(true);
}
/**
* The wrapped request.
*/
protected Request request = null;
@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 {
//111
return request.getSession(create);
}
}
可以在標注1的地方看到,方法最后是委托給了request
,而request
是Request
的實例。繼續查看Request
類的getSession()
方法。
//代碼清單13
@Override
public HttpSession getSession(boolean create) {
//1111
Session session = doGetSession(create);
if (session == null) {
return null;
}
//2222
return session.getSession();
}
doGetSession
方法我們就不查看了,之前在講解Session
相關的文章中查看過了,在標注1的地方,我們可以看到返回的是session
對象,而Session
是接口,他的默認標准實現類是StandardSession
,在標注2的地方又調用了該類的getSession()
方法,所以我們查看下StandardSession
類的getSession()
方法。
//代碼清單14
/**
* 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);
}
可以很清楚的看到,最后返回的是StandardSessionFacade
對象,我們查看下該類的構造函數:
//代碼清單15
/**
* Construct a new session facade.
*
* @param session The session instance to wrap
*/
public StandardSessionFacade(StandardSession session) {
super();
this.session = session;
}
/**
* Wrapped session object.
*/
private HttpSession session = null;
Tomcat使用外觀設計模式主要是為了保證主要類的安全,防止程序員使用核心類的不需要暴露出去的功能。