tomcat中用到了许多设计模式,例如工厂、模板、观察者、单例、外观模式等。
1、工厂模式
工厂模式作为最简单的模式之一,同时也是最常用的模式之一,在tomcat也有着应用。下面是tomcat启动类的部分代码,通过工厂获取类加载器:
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
//默认情况下为空,直接返回parent,这儿的parent就是CommonLoader
if ((value == null) || (value.equals("")))
return parent;
value = replace(value);
List<Repository> repositories = new ArrayList<Repository>();
StringTokenizer tokenizer = new StringTokenizer(value, ",");
//这儿是对类加载器定义加载的路径解析
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken().trim();
if (repository.length() == 0) {
continue;
}
// 省略部分代码
}
//使用工厂创建类加载器
return ClassLoaderFactory.createClassLoader(repositories, parent);
}
还有Acceptor中的获取ServerSocket以及Socket,如以下代码:
//if we have reached max connections, wait
countUpOrAwaitConnection();
Socket socket = null;
try {
// Accept the next incoming connection from the server
// 通过工厂获取Socket
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
countDownConnection();
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
}
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;
}
}
此外还有创建Disgester的工厂、FilterChain的工厂,甚至源码中直接有一个包直接以factory命名。总之,tomcat大量用到了工厂模式。
2、模板方法模式
这个模式主要体现在容器类中,一个容器有启动、停止、初始化、销毁等生命周期,所以它们都间接实现了Lifecycle接口,Lifecycle接口定义了这些方法的定义,但从容器类中并找不到这些方法,这是因为在父类中已经覆盖了这个方法,父类LifecycleBase中init方法如下
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
//抽象方法,留给子类实现
protected abstract void initInternal() throws LifecycleException;
以下是StandardContext的initInternal方法:
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (processTlds) {
this.addLifecycleListener(new TldConfig());
}
// Register the naming resources
if (namingResources != null) {
namingResources.init();
}
// Send j2ee.object.created notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.object.created",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
}
所以你可以在Lifecycle的子类中找到大量的XXXInternal方法,但基本找不到init、start、destory等方法。因为大致的生命周期模板已经在父类中定义了。
3、观察者模式
处理监听事件的操作一般都要用到观察者模式,当然tomcat也不例外。在tomcat中,容器类以及其他类(Server、Service等等)在启动、停止、销毁时会触发Listener调用,翻开tomcat的各个容器类的xxxInternal方法,基本都有以下方法调用。
setStateInternal(LifecycleState.STARTED, null, false);
那看看setState方法里面都干了些什么,可以在该方法的最后几行看到以下代码:
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
而fireLifecycleEvent中代码又如下(中间使用了LifecycleSupport对象调用下面这个方法):
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);
}
循环遍历调用监听器的lifecycleEvent方法,这几句代码对于了解观察者模式的人来说应该比较熟悉。
4、责任链模式
这个模式主要体现在容器类里面,前面讲过,请求从接受然后委托为容器处理的过程说到了Pipeline以及Valve组件,父容器的Pipeline会调用父容器的Valve,父容器的Valve又会调用子容器(具体是哪一个子容器,根据请求判断)的Pipeline,而子容器又重复上述过程,直到最底层的Wrapper容器的Valve。下面是Engine容器类的Valve组件方法:
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
可以看到Engine容器调用了子容器Host的Valve,而Host又会在它的Valve中调用Context的Valve方法
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Context to be used for this Request
Context context = request.getContext();
if (context == null) {
response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
boolean asyncAtStart = request.isAsync();
boolean asyncDispatching = request.isAsyncDispatching();
if (asyncAtStart || context.fireRequestInitEvent(request.getRequest())) {
//调用context
try {
if (!asyncAtStart || asyncDispatching) {
context.getPipeline().getFirst().invoke(request, response);
} else {
// Make sure this request/response is here because an error
// report is required.
if (!response.isErrorReportRequired()) {
throw new IllegalStateException(sm.getString("standardHost.asyncStateError"));
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
container.getLogger().error("Exception Processing " + request.getRequestURI(), t);
// If a new error occurred while trying to report a previous
// error allow the original error to be reported.
if (!response.isErrorReportRequired()) {
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
throwable(request, response, t);
}
}
}
ps:由于这个方法较长,所以删掉了一些代码。
可以看到注释部分又调用了Context的Valve,从而形成了一条链,然后在Wrapper的Valve中又会生成一条Filter Chain来完成响应。
5、外观模式
在tomcat中也用到了外观模式,主要使用在包装request、response、session等对象上面。下面是CoyoteAdapter的service方法部分代码,这个方法会调用容器的Pipeline组件完成响应:
/**
* Service method.
*/
@Override
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);}
可以看到这个方法的参数虽然也是Request,但这个org.apache.coyote.Request并不是HttpServletRequest的子类,它也是一个final类。同理,Response也没有实现HttpServletResponse,在方法的头两句代码的Request以及Response才是分别实现了HttpServletRequest和HttpServletResponse的子类。可以看到在if条件块里将这两个对象设值给了Request以及Response,点开这两个类,你可以看到大多数方法都是通过调用coyoteRequest以及coyoteResponse来完成的。此外、虽然还可以看到RequestFacade以及ResponseFacade两个包装类,但这两个类基本没有使用了,被org.apache.catalina.connector包中对应的Request和Response代替了。这种方式可以防止程序员调用类中不想暴露的方法,保证类的安全。
6、策略模式
tomcat可以通过配置文件来改变tomcat的行为,这属于策略模式。比如通过改变server.xml中connector的配置来决定tomcat处理请求的方式(BIO,NIO,APR),来控制访问的最大连接数等。
虽然还可以在代码中看到其他模式的影子,比如单例模式(ps:单例模式我就在ApplicationFilterFactory看到了,然后完全不知为什么会把这个工厂类写成单例)
当然还有其他模式存在,但目前对于其他模式不甚了解,所以知道的小伙伴可以在下面评论留言告诉我,谢谢!
最后,感谢你的阅读! ( ̄▽ ̄)~*