tomcat学习笔记之五:tomcat中的设计模式

      

     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看到了,然后完全不知为什么会把这个工厂类写成单例疑问

当然还有其他模式存在,但目前对于其他模式不甚了解,所以知道的小伙伴可以在下面评论留言告诉我,谢谢!

       最后,感谢你的阅读! ( ̄▽ ̄)~*       



     








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值