Tomcat是如何处理请求的


前言

今天我们来看看Tomcat是如何处理请求的。之前已经讲到了 adapter.service(request, response);了,我们就接着这里入手,我们知道adapter对应的是一个CoyoteAdapter。

CoyoteAdapter

service(request, response)

 /**
     * Service method.
     */
    @Override
    public void service(org.apache.coyote.Request req,
                        org.apache.coyote.Response res)
        throws Exception {
    //一开始就是将org.apache.coyote.Request/Response转为org.apache.catalina.connector.Request/Response
    //
        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);

            // Set as notes
            req.setNote(ADAPTER_NOTES, request);
            res.setNote(ADAPTER_NOTES, response);

            // Set query string encoding
            req.getParameters().setQueryStringEncoding
                (connector.getURIEncoding());

        }
            postParseSuccess = postParseRequest(req, request, res, response);


                // Calling the container
  //然后就是开始调用请求相应的Servlet的              connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);


            }
        } catch (IOException e) {
            // Ignore
        } finally {

            }
        }
    }

这个方法的作用:

1.将Request转化为继承了HttpServletRequest的org.apache.catalina.connector.Request

2.通过postParseRequest(req, request, res, response);得到对应的context和warpper

3.调用了相应的方法来处理请求。即connector.getService().getContainer()..getFirst().invoke(request, response);这是我们重点要关注的内容

第一条就不多说了

我们先说第二条

postParseRequest

 while (mapRequired) {
            // This will map the the latest version by default
            connector.getMapper().map(serverName, decodedURI, version,
                                      request.getMappingData());
            request.setContext((Context) request.getMappingData().context);
            request.setWrapper((Wrapper) request.getMappingData().wrapper);

            // If there is no context at this point, it is likely no ROOT context
            // has been deployed
            if (request.getContext() == null) {
                res.setStatus(404);
                res.setMessage("Not found");
                // No context, so use host
                Host host = request.getHost();
                // Make sure there is a host (might not be during shutdown)
                if (host != null) {
                    host.logAccess(request, response, 0, true);
                }
                return false;
            }

这里就给出了该请求的context和warpper,这里就涉及到了如何找到request请求对应的Servlet了。

我们需要关注

connector.getMapper().map(serverName, decodedURI, version, request.getMappingData());

通过这个方法找到对应的Servlet。

我们来分析一下这个方法:

connector.getMapper()调用的是Connector类的mapper属性


     /**
      * Mapper.
      */
     protected Mapper mapper = new Mapper();


     /**
      * Mapper listener.
      */
     protected MapperListener mapperListener = new MapperListener(mapper, this);

所以调用的是Mapper的mapper()方法

看到这个方法名,我们就可以想到,肯定是去到一个集合里面去匹配我们对应的Servlet和Context等信息。那么我们就必须要知道全部的Servlet等信息是何时放入Mapper的集合里面的。

到底是在哪里注册了这些信息到Mapper里呢。

我们要回到connector.start()

我们看到Connector的startInternal()方法,我们可以看到方法的最后

   @Override
    protected void startInternal() throws LifecycleException {

        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }

        setState(LifecycleState.STARTING);

        try {
            protocolHandler.start();
        } catch (Exception e) {
            String errPrefix = "";
            if(this.service != null) {
                errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
            }

            throw new LifecycleException
                (errPrefix + " " + sm.getString
                 ("coyoteConnector.protocolHandlerStartFailed"), e);
        }

        mapperListener.start();
    }

最后调用了mapperListener.start();

而mapperListener的初始化用到了Mapper

     protected MapperListener mapperListener = new MapperListener(mapper, this);

是的,注册信息到Mapper里就是通过mapperListener.start();

mapperListener.start()

@Override
    public void startInternal() throws LifecycleException {

        setState(LifecycleState.STARTING);

        // Find any components that have already been initialized since the
        // MBean listener won't be notified as those components will have
        // already registered their MBeans
        findDefaultHost();

        Engine engine = (Engine) connector.getService().getContainer();
        addListeners(engine);

        Container[] conHosts = engine.findChildren();
        for (Container conHost : conHosts) {
            Host host = (Host) conHost;
            if (!LifecycleState.NEW.equals(host.getState())) {
                // Registering the host will register the context and wrappers
                registerHost(host);
            }
        }
    }   
private void registerHost(Host host) {

        String[] aliases = host.findAliases();
        mapper.addHost(host.getName(), aliases, host);

        for (Container container : host.findChildren()) {
            if (container.getState().isAvailable()) {
                registerContext((Context) container);
            }
        }
        if(log.isDebugEnabled()) {
            log.debug(sm.getString("mapperListener.registerHost",
                    host.getName(), domain, connector));
        }
    }
 private void registerContext(Context context) {

        String contextPath = context.getPath();
        if ("/".equals(contextPath)) {
            contextPath = "";
        }
        Container host = context.getParent();

        javax.naming.Context resources = context.getResources();
        String[] welcomeFiles = context.findWelcomeFiles();
        List<WrapperMappingInfo> wrappers = new ArrayList<WrapperMappingInfo>();

        for (Container container : context.findChildren()) {
            prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);

            if(log.isDebugEnabled()) {
                log.debug(sm.getString("mapperListener.registerWrapper",
                        container.getName(), contextPath, connector));
            }
        }

        mapper.addContextVersion(host.getName(), host, contextPath,
                context.getWebappVersion(), context, welcomeFiles, resources,
                wrappers, context.getMapperContextRootRedirectEnabled(),
                context.getMapperDirectoryRedirectEnabled());

        if(log.isDebugEnabled()) {
            log.debug(sm.getString("mapperListener.registerContext",
                    contextPath, connector));
        }
    }

这段代码就是将Host,Context以及Warpper都注册到Mapper中,我们这里着重讲一下Warpper的注册。

registerContext

先通过prepareWrapperMappingInfo将Warpper包装成WrapperMappingInfo,然后再通过addContextVersion将WrapperMappingInfo注册到Mapper中

mapper.addContextVersion

  public void addContextVersion(String hostName, Object host, String path,
            String version, Object context, String[] welcomeResources,
            javax.naming.Context resources, Collection<WrapperMappingInfo> wrappers,
            boolean mapperContextRootRedirectEnabled, boolean mapperDirectoryRedirectEnabled) {

        Host mappedHost = exactFind(hosts, hostName);
//...省略了大段代码
        synchronized (mappedHost) {
            ContextVersion newContextVersion = new ContextVersion(version, context);
            newContextVersion.path = path;
            newContextVersion.slashCount = slashCount;
            newContextVersion.welcomeResources = welcomeResources;
            newContextVersion.resources = resources;
            newContextVersion.mapperContextRootRedirectEnabled = mapperContextRootRedirectEnabled;
            newContextVersion.mapperDirectoryRedirectEnabled = mapperDirectoryRedirectEnabled;

            if (wrappers != null) {
                addWrappers(newContextVersion, wrappers);
            }


    }

主要是addWrappers(newContextVersion, wrappers);完成了注册

  private void addWrappers(ContextVersion contextVersion,
            Collection<WrapperMappingInfo> wrappers) {
        for (WrapperMappingInfo wrapper : wrappers) {
            addWrapper(contextVersion, wrapper.getMapping(),
                    wrapper.getWrapper(), wrapper.isJspWildCard(),
                    wrapper.isResourceOnly());
        }
    }

终于到了最终注册的方法

addWrapper()

 protected void addWrapper(ContextVersion context, String path,
            Object wrapper, boolean jspWildCard, boolean resourceOnly) {

        synchronized (context) {
            if (path.endsWith("/*")) {
                // Wildcard wrapper
                String name = path.substring(0, path.length() - 2);
                Wrapper newWrapper = new Wrapper(name, wrapper, jspWildCard,
                        resourceOnly);
                Wrapper[] oldWrappers = context.wildcardWrappers;
                Wrapper[] newWrappers =
                    new Wrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.wildcardWrappers = newWrappers;
                    int slashCount = slashCount(newWrapper.name);
                    if (slashCount > context.nesting) {
                        context.nesting = slashCount;
                    }
                }
            } else if (path.startsWith("*.")) {
                // Extension wrapper
                String name = path.substring(2);
                Wrapper newWrapper = new Wrapper(name, wrapper, jspWildCard,
                        resourceOnly);
                Wrapper[] oldWrappers = context.extensionWrappers;
                Wrapper[] newWrappers =
                    new Wrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.extensionWrappers = newWrappers;
                }
            } else if (path.equals("/")) {
                // Default wrapper
                Wrapper newWrapper = new Wrapper("", wrapper, jspWildCard,
                        resourceOnly);
                context.defaultWrapper = newWrapper;
            } else {
                // Exact wrapper
                final String name;
                if (path.length() == 0) {
                    // Special case for the Context Root mapping which is
                    // treated as an exact match
                    name = "/";
                } else {
                    name = path;
                }
                Wrapper newWrapper = new Wrapper(name, wrapper, jspWildCard,
                        resourceOnly);
                Wrapper[] oldWrappers = context.exactWrappers;
                Wrapper[] newWrappers =
                    new Wrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.exactWrappers = newWrappers;
                }
            }
        }
    }

这个方法代码也非常多,他主要做了以下工作:

如果请求的uri是/*结尾的话就将Warpper插入到wildcardWrappers这个Warpper数组中

如果是以*.开始的话就将Warpper插入到extensionWrappers中

如果是/的话就将Warpper插入到defaultWrapper

如果是指定的url,没有使用通配符的话,就插入到exactWrappers中

到现在我们了解了Servlet,Context信息是如何注册到Mapper中了,现在可以回头来看Mapperd的mapper()方法了

mapper()

    public void map(MessageBytes host, MessageBytes uri, String version,
                    MappingData mappingData)
        throws Exception {

        if (host.isNull()) {
            host.getCharChunk().append(defaultHostName);
        }
        host.toChars();
        uri.toChars();
        internalMap(host.getCharChunk(), uri.getCharChunk(), version,
                mappingData);

    }

        private final void internalMap(CharChunk host, CharChunk uri,
            String version, MappingData mappingData) throws Exception {
            //...省略大段代码
       // Wrapper mapping
        if (!contextVersion.isPaused()) {
            internalMapWrapper(contextVersion, uri, mappingData);
        }
        }

我们主要关注查找匹配的Servlet的方法internalMapWrapper

internalMapWrapper

 /**
     * Wrapper mapping.
     */
    private final void internalMapWrapper(ContextVersion contextVersion,
                                          CharChunk path,
                                          MappingData mappingData)
        throws Exception {

        int pathOffset = path.getOffset();
        int pathEnd = path.getEnd();
        boolean noServletPath = false;

        int length = contextVersion.path.length();
        if (length == (pathEnd - pathOffset)) {
            noServletPath = true;
        }
        int servletPath = pathOffset + length;
        path.setOffset(servletPath);

        // Rule 1 -- Exact Match
        Wrapper[] exactWrappers = contextVersion.exactWrappers;
        internalMapExactWrapper(exactWrappers, path, mappingData);

        // Rule 2 -- Prefix Match
        boolean checkJspWelcomeFiles = false;
        Wrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
        if (mappingData.wrapper == null) {
            internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
                                       path, mappingData);
            if (mappingData.wrapper != null && mappingData.jspWildCard) {
                char[] buf = path.getBuffer();
                if (buf[pathEnd - 1] == '/') {
                    /*
                     * Path ending in '/' was mapped to JSP servlet based on
                     * wildcard match (e.g., as specified in url-pattern of a
                     * jsp-property-group.
                     * Force the context's welcome files, which are interpreted
                     * as JSP files (since they match the url-pattern), to be
                     * considered. See Bugzilla 27664.
                     */
                    mappingData.wrapper = null;
                    checkJspWelcomeFiles = true;
                } else {
                    // See Bugzilla 27704
                    mappingData.wrapperPath.setChars(buf, path.getStart(),
                                                     path.getLength());
                    mappingData.pathInfo.recycle();
                }
            }
        }

        if(mappingData.wrapper == null && noServletPath &&
                contextVersion.mapperContextRootRedirectEnabled) {
            // The path is empty, redirect to "/"
            path.append('/');
            pathEnd = path.getEnd();
            mappingData.redirectPath.setChars
                (path.getBuffer(), pathOffset, pathEnd - pathOffset);
            path.setEnd(pathEnd - 1);
            return;
        }

        // Rule 3 -- Extension Match
        Wrapper[] extensionWrappers = contextVersion.extensionWrappers;
        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
            internalMapExtensionWrapper(extensionWrappers, path, mappingData,
                    true);
        }

        // Rule 4 -- Welcome resources processing for servlets
        if (mappingData.wrapper == null) {
            boolean checkWelcomeFiles = checkJspWelcomeFiles;
            if (!checkWelcomeFiles) {
                char[] buf = path.getBuffer();
                checkWelcomeFiles = (buf[pathEnd - 1] == '/');
            }
            if (checkWelcomeFiles) {
                for (int i = 0; (i < contextVersion.welcomeResources.length)
                         && (mappingData.wrapper == null); i++) {
                    path.setOffset(pathOffset);
                    path.setEnd(pathEnd);
                    path.append(contextVersion.welcomeResources[i], 0,
                            contextVersion.welcomeResources[i].length());
                    path.setOffset(servletPath);

                    // Rule 4a -- Welcome resources processing for exact macth
                    internalMapExactWrapper(exactWrappers, path, mappingData);

                    // Rule 4b -- Welcome resources processing for prefix match
                    if (mappingData.wrapper == null) {
                        internalMapWildcardWrapper
                            (wildcardWrappers, contextVersion.nesting,
                             path, mappingData);
                    }

                    // Rule 4c -- Welcome resources processing
                    //            for physical folder
                    if (mappingData.wrapper == null
                        && contextVersion.resources != null) {
                        Object file = null;
                        String pathStr = path.toString();
                        try {
                            file = contextVersion.resources.lookup(pathStr);
                        } catch(NamingException nex) {
                            // Swallow not found, since this is normal
                        }
                        if (file != null && !(file instanceof DirContext) ) {
                            internalMapExtensionWrapper(extensionWrappers, path,
                                                        mappingData, true);
                            if (mappingData.wrapper == null
                                && contextVersion.defaultWrapper != null) {
                                mappingData.wrapper =
                                    contextVersion.defaultWrapper.object;
                                mappingData.requestPath.setChars
                                    (path.getBuffer(), path.getStart(),
                                     path.getLength());
                                mappingData.wrapperPath.setChars
                                    (path.getBuffer(), path.getStart(),
                                     path.getLength());
                                mappingData.requestPath.setString(pathStr);
                                mappingData.wrapperPath.setString(pathStr);
                            }
                        }
                    }
                }

                path.setOffset(servletPath);
                path.setEnd(pathEnd);
            }

        }

代码很长,其实就是规定了几个匹配的规则:

Rule1:先按照指定的url匹配,看是否有注册完整的相同url对应的Servlet。即在extartWappers里面找

Rule2:匹配/*通配符,在wildcardWrappers中查找

Rule3:按照后缀名匹配 .jsp ,.jspx等,即在extensionWrappers中查找。

Rule4:在指定欢迎页面中查找:

Rule7: 按照/匹配,即defaultWrapper

所以按照顺序 /*的优先级要比/高,这一点在讲解SpringMVC的时候会说,这一点很重要。

好了,在这里就把如何查找到request匹配的Servlet等信息讲解清楚了,将这些信息注册到request中之后,我们可以关注下一步操作了。

首先我们可以很容器的知道connector.getService().getContainer().得到的是StandardEngine实例,那我们先把目光转移到这个类上。

我们发现这个类并没有实现getPipeline()方法,这个方法的实现在其父类ContainerBase中

   public Pipeline getPipeline() {

        return (this.pipeline);

    }

就是得到这个类的pipeline属性。

那当前这个类的pipeline属性是在哪里注入的呢。答案是初始化的时候。

    public StandardEngine() {

        super();
        pipeline.setBasic(new StandardEngineValve());
        /* Set the jmvRoute using the system property jvmRoute */
        try {
            setJvmRoute(System.getProperty("jvmRoute"));
        } catch(Exception ex) {
            log.warn(sm.getString("standardEngine.jvmRouteFail"));
        }
        // By default, the engine will hold the reloading thread
        backgroundProcessorDelay = 10;

    }
    //StandardPipeline
    @Override
    public Valve getFirst() {
        if (first != null) {
            return first;
        }

        return basic;
    }

通过上面的代码我们可以得出connector.getService().getContainer()..getFirst()对应的是StandardEngineValve实例。

我们把目光转向StandardEngineValve的invoke()方法

  @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);

    }

我们发现没有做什么实际性的处理,而是将任务交由子容器Host处理。

我们看源码发现 host.getPipeline().getFirst()对应的是StandardHostValve,和上面一样,依然是把实际的处理交给子容器,这样的设计是不是让你想起了责任链模式呢,不慌,之后还有更流畅的责任链模式。

                    context.getPipeline().getFirst().invoke(request, response);

中间过程就不分析了,直接来到StandardWarpperValve的invoke()

      @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

   try {
            if (!unavailable) {
                servlet = wrapper.allocate();
            }


// Create the filter chain for this request
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);

        // Reset comet flag value after creating the filter chain
        request.setComet(false);

        // Call the filter chain for this request
        // NOTE: This also calls the servlet's service() method
        try {
            if ((servlet != null) && (filterChain != null)) {
                // Swallow output if needed
                if (context.getSwallowOutput()) {
                    try {
                        SystemLogHandler.startCapture();
                        if (request.isAsyncDispatching()) {
                            request.getAsyncContextInternal().doInternalDispatch();
                        } else if (comet) {
                            filterChain.doFilterEvent(request.getEvent());
                            request.setComet(true);
                        } else {
                            filterChain.doFilter(request.getRequest(),
                                    response.getResponse());
                        }
                    } finally {
                        String log = SystemLogHandler.stopCapture();
                        if (log != null && log.length() > 0) {
                            context.getLogger().info(log);
                        }
                    }
                } else {
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else if (comet) {
                        request.setComet(true);
                        filterChain.doFilterEvent(request.getEvent());
                    } else {
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
        } 
        }


    }


这个方法的代码非常长,我只拿了一小部分过来

这个方法的主要作用是:

1.通过 servlet = wrapper.allocate();得到servlet

2.创建一个FilterChain链实例

3.调用filterChain.doFilter (request.getRequest(), response.getResponse());

wrapper.allocate();

  @Override
    public Servlet allocate() throws ServletException {

        // If we are currently unloading this servlet, throw an exception
        if (unloading) {
            throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
        }

        boolean newInstance = false;

        // If not SingleThreadedModel, return the same instance every time
        if (!singleThreadModel) {
            // Load and initialize our instance if necessary
            if (instance == null || !instanceInitialized) {
                synchronized (this) {
                    if (instance == null) {
                        try {
                            if (log.isDebugEnabled()) {
                                log.debug("Allocating non-STM instance");
                            }

                            // Note: We don't know if the Servlet implements
                            // SingleThreadModel until we have loaded it.
                            instance = loadServlet();

                    if (!instanceInitialized) {
                        initServlet(instance);
                    }
                }
            }


    }

instance = loadServlet();完成了实例化Servlet的工作

loadServlet()

““java
public synchronized Servlet loadServlet() throws ServletException {

    Servlet servlet;

        InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
        try {
            servlet = (Servlet) instanceManager.newInstance(servletClass);
        } catch (ClassCastException e) {
            unavailable(null);
            // Restore the context ClassLoader

}
processServletSecurityAnnotation(servlet.getClass());

        initServlet(servlet);


    return servlet;

}

““

            servlet = (Servlet) instanceManager.newInstance(servletClass);

得到了Servlet实例,接着还有一个方法 initServlet(servlet);这个方法我们需要关注一下:

这个方法最终其实就是调用了servlet.init(ServletConfig config),这个方法,我们肯定不陌生,只要我们接触过Servlet就一定会了解这个方法的作用,我们可以在这个方法里面通过config得到我们在servlet节点里面配置的init-param的键值对,取出我们想要的值,可是,你在使用Servlet的init()方法时,有想过到底是怎么做到的吗,下面我们就来讲讲是怎样做到的。

““java
private synchronized void initServlet(Servlet servlet)
throws ServletException {

    if (instanceInitialized && !singleThreadModel) return;

    // Call the initialization method of this servlet
    try {
        instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
                                          servlet);

        if( Globals.IS_SECURITY_ENABLED) {
            boolean success = false;
            try {
                Object[] args = new Object[] { facade };
                SecurityUtil.doAsPrivilege("init",
                                           servlet,
                                           classType,
                                           args);
                success = true;
            } finally {
                if (!success) {
                    // destroy() will not be called, thus clear the reference now
                    SecurityUtil.remove(servlet);
                }
            }
        } else {
            servlet.init(facade);
        }

        instanceInitialized = true;

        instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                          servlet);
    } catch (UnavailableException f) {
        instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                          servlet, f);
        unavailable(f);
        throw f;
    } catch (ServletException f) {
        instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                          servlet, f);
        // If the servlet wanted to be unavailable it would have
        // said so, so do not call unavailable(null).
        throw f;
    } catch (Throwable f) {
        ExceptionUtils.handleThrowable(f);
        getServletContext().log("StandardWrapper.Throwable", f );
        instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                          servlet, f);
        // If the servlet wanted to be unavailable it would have
        // said so, so do not call unavailable(null).
        throw new ServletException
            (sm.getString("standardWrapper.initException", getName()), f);
    }
}

““

虽然代码很长,我们需要关注的只有这一行 servlet.init(facade);

servlet.init(facade);

很明显我们的注意力要集中到facade上,很明显是相关的配置参数信息已经注册到了facade中了,这样我们才能在程序中能够获取得到相关参数的值。

我们来看这个facade对应的实现类到底是什么,参看StandardWarpper的源码可知

    protected StandardWrapperFacade facade =
        new StandardWrapperFacade(this);

facade是StandardWarpperFacade实例,并且实例化的时候注入了StandardWarpper对象。

我们进入StandardWarpperFacade,查看他对ServletConfig接口方法的实现

public final class StandardWrapperFacade
    implements ServletConfig {


    // ----------------------------------------------------------- Constructors


    /**
     * Create a new facade around a StandardWrapper.
     */
    public StandardWrapperFacade(StandardWrapper config) {

        super();
        this.config = config;

    }


    // ----------------------------------------------------- Instance Variables


    /**
     * Wrapped config.
     */
    private ServletConfig config = null;


    /**
     * Wrapped context (facade).
     */
    private ServletContext context = null;


    // -------------------------------------------------- ServletConfig Methods


    @Override
    public String getServletName() {
        return config.getServletName();
    }


    @Override
    public ServletContext getServletContext() {
        if (context == null) {
            context = config.getServletContext();
            if ((context != null) && (context instanceof ApplicationContext))
                context = ((ApplicationContext) context).getFacade();
        }
        return (context);
    }


    @Override
    public String getInitParameter(String name) {
        return config.getInitParameter(name);
    }


    @Override
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }


}

我们会发现,其实都是调用的StandardWarpper中的方法,只是做了一下封装而已,StandardWarpper也实现了ServletConfig接口。

首先看StandardWarpper的getServletContext(),看到底是怎样得到ServletContext接口的实现类的,这个号称是Servlet上下文的类到底是什么呢。


    /**
     * Return the servlet context with which this servlet is associated.
     */
    @Override
    public ServletContext getServletContext() {

        if (parent == null)
            return (null);
        else if (!(parent instanceof Context))
            return (null);
        else
            return (((Context) parent).getServletContext());

    }

我们发现是调用的父容器StandardContext的getServletContext()方法,这样就对了嘛,要不然怎么能称得上是上下文呢,如果在Servlet这一层就直接取到了,那只能在单个的Servlet中使用了。

StandardContext.getServletContext()

    @Override
    public ServletContext getServletContext() {

        if (context == null) {
            context = new ApplicationContext(this);
            if (altDDName != null)
                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
        }
        return (context.getFacade());

    }

代码很好理解,我们终于知道了代表Servlet上下文的类是ApplicationContext,在实例化的时候注入了StandardContext,显然我们可以吧该类当做StandardContext的一个包装类,而在这个类里面有包装了一个ApplicationContextFacade,我们得到的ServletContext的实现类实际上就是ApplicationContextFacade实例。其实都是封装,Java就是这样,层层封装,有兴趣的可以看看这两个类的具体实现。

上面讲了getServeltContext的实现,下面也要讲一个getInitParameter的实现

 //StandardWarpperFacade
    @Override
    public String getInitParameter(String name) {
    //实际调用的StandardWarpper的方法
        return config.getInitParameter(name);
    }
//StandardWarpper
 @Override
    public String getInitParameter(String name) {

        return (findInitParameter(name));

    }
    protected HashMap<String, String> parameters = new HashMap<String, String>();

@Override
    public String findInitParameter(String name) {

        try {
            parametersLock.readLock().lock();
            return parameters.get(name);
        } finally {
            parametersLock.readLock().unlock();
        }

    }
        @Override
    public void addInitParameter(String name, String value) {

        try {
            parametersLock.writeLock().lock();
            parameters.put(name, value);
        } finally {
            parametersLock.writeLock().unlock();
        }
        fireContainerEvent("addInitParameter", name);

    }

还记得解析WEB.xml配置文件吗,当我们把WebXML类的信息注册到StadardContext中的时候,servlet的参数配置就是通过addInitParameter添加进去的,讲到这里你就应该为什么可以取到参数了吧。

通过上面的分析,我们知道了ServletConfig的实现类是StandardWarpperFacade,是对StandardWarpper的一个封装。

ServletContext的实现类是ApplicationContextFacade,这是对StandardContext的封装。

创建FilterChain实例

终于到创建FilterChain过滤连实例了。

     ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);


public ApplicationFilterChain createFilterChain
        (ServletRequest request, Wrapper wrapper, Servlet servlet) {

        // get the dispatcher type
        DispatcherType dispatcher = null; 
        if (request.getAttribute(Globals.DISPATCHER_TYPE_ATTR) != null) {
            dispatcher = (DispatcherType) request.getAttribute(
                    Globals.DISPATCHER_TYPE_ATTR);
        }
        String requestPath = null;
        Object attribute = request.getAttribute(
                Globals.DISPATCHER_REQUEST_PATH_ATTR);

        if (attribute != null){
            requestPath = attribute.toString();
        }

        // If there is no servlet to execute, return null
        if (servlet == null)
            return (null);

        boolean comet = false;

        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            comet = req.isComet();
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
              //创建一个FilterChain实例
                filterChain = new ApplicationFilterChain();
                if (comet) {
                    req.setFilterChain(filterChain);
                }
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);

        filterChain.setSupport
            (((StandardWrapper)wrapper).getInstanceSupport());

        // Acquire the filter mappings for this Context
        StandardContext context = (StandardContext) wrapper.getParent();
  //找到所有的FilterMap
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return (filterChain);

        // Acquire the information we will need to match filter mappings
        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain
  //找到所有符合条件的Filter
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());


        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            boolean isCometFilter = false;
            if (comet) {
                try {
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;
                } catch (Exception e) {
                    // Note: The try catch is there because getFilter has a lot of 
                    // declared exceptions. However, the filter is allocated much
                    // earlier
                }
                if (isCometFilter) {
                    filterChain.addFilter(filterConfig);
                }
            } else {
                filterChain.addFilter(filterConfig);
            }
        }

        // Return the completed filter chain
        return (filterChain);

    }

这个方法也是巨长,他主要做了如下工作:

首先创建一个FilterChain实例ApplicationFilterChain

然后通过StandardContext得到所有的FilterMap,然后筛选出这次请求匹配的过滤器。

然后将这些过滤器添加到过滤链实例中。 filterChain.addFilter(filterConfig);

 void addFilter(ApplicationFilterConfig filterConfig) {

        // Prevent the same filter being added multiple times
        for(ApplicationFilterConfig filter:filters)
            if(filter==filterConfig)
                return;

        if (n == filters.length) {
            ApplicationFilterConfig[] newFilters =
                new ApplicationFilterConfig[n + INCREMENT];
            System.arraycopy(filters, 0, newFilters, 0, n);
            filters = newFilters;
        }
        filters[n++] = filterConfig;

    }

我们看到就是把过滤器对应的类ApplicationFilterConfig放入到ApplicationFilterChain的filters数组中。

最后我们要调用ApplicationFilterConfig的doFilter来处理请求。

  @Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
else {
            internalDoFilter(request,response);
        }
    }
  private void internalDoFilter(ServletRequest request, 
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            try {
                filter = filterConfig.getFilter();
                support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
                                          filter, request, response);

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                            Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal = 
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege
                        ("doFilter", filter, classType, args, principal);

                } else {  
                //
                    filter.doFilter(request, response, this);
                }

                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                          filter, request, response);
            } catch (IOException e) {
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw e;
            } catch (ServletException e) {
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw e;
            } catch (RuntimeException e) {
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw e;
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                if (filter != null)
                    support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                              filter, request, response, e);
                throw new ServletException
                  (sm.getString("filterChain.filter"), e);
            }
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(request);
                lastServicedResponse.set(response);
            }

            support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
                                      servlet, request, response);
            if (request.isAsyncSupported()
                    && !support.getWrapper().isAsyncSupported()) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                        Boolean.FALSE);
            }
            // Use potentially wrapped request from this point
            if ((request instanceof HttpServletRequest) &&
                (response instanceof HttpServletResponse)) {

                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal = 
                        ((HttpServletRequest) req).getUserPrincipal();
                    Object[] args = new Object[]{req, res};
                    SecurityUtil.doAsPrivilege("service",
                                               servlet,
                                               classTypeUsedInService, 
                                               args,
                                               principal);   
                } else {  
                    servlet.service(request, response);
                }
            } else {
                servlet.service(request, response);
            }

    }

这段代码不难理解,他使用了责任链的设计模式,这种设计模式,我们已经不陌生了,在Mybatis的Cache的设计中以及讲过。这里通过遍历数组来实现,具体的实现还是值得我们细细品味的。

当所有的有效过滤器都执行完后,我们会执行 servlet.service(request, response);

来处理我们的请求,我们把返回的数据放在response中,而response已经对socket.getOutputStream封装了,所以返回的数据能够成功返回。

End…

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值