springboot內(nèi)嵌tomcat代碼走讀(三)

上篇文章走讀了springboot中消息處理進(jìn)入到servlet里了,這次,具體走讀了請(qǐng)求消息在servlet里是怎么處理的。這里主要補(bǔ)充servlet和springmvc相關(guān)的知識(shí)。

servlet相關(guān)知識(shí)

servlet生命周期

servlet生命周期在servlet的代碼里能很清楚的體現(xiàn)出來(lái)。代碼為:


public interface Servlet {

    //servlet 初始化,這里摘錄了部分注釋
    /**
     * The servlet container calls the <code>init</code> method exactly once
     * after instantiating the servlet. The <code>init</code> method must
     * complete successfully before the servlet can receive any requests.
    */
    public void init(ServletConfig config) throws ServletException;

    /**
     *
     * Returns a {@link ServletConfig} object, which contains initialization and
     * startup parameters for this servlet. The <code>ServletConfig</code>
     * object returned is the one passed to the <code>init</code> method.
     *
     * <p>
     * Implementations of this interface are responsible for storing the
     * <code>ServletConfig</code> object so that this method can return it. The
     * {@link GenericServlet} class, which implements this interface, already
     * does this.
     *
     * @return the <code>ServletConfig</code> object that initializes this
     *         servlet
     *
     * @see #init
     */
    public ServletConfig getServletConfig();

    /**
     * Called by the servlet container to allow the servlet to respond to a
     * request.
     *
     * <p>
     * This method is only called after the servlet's <code>init()</code> method
     * has completed successfully.
     *
     * <p>
     * The status code of the response always should be set for a servlet that
     * throws or sends an error.
     */
     //真正的處理請(qǐng)求
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    /**
     * Returns information about the servlet, such as author, version, and
     * copyright.
     *
     * <p>
     * The string that this method returns should be plain text and not markup
     * of any kind (such as HTML, XML, etc.).
     *
     * @return a <code>String</code> containing servlet information
     */
    public String getServletInfo();

    /**
     * Called by the servlet container to indicate to a servlet that the servlet
     * is being taken out of service. This method is only called once all
     * threads within the servlet's <code>service</code> method have exited or
     * after a timeout period has passed. After the servlet container calls this
     * method, it will not call the <code>service</code> method again on this
     * servlet.
     *
     * <p>
     * This method gives the servlet an opportunity to clean up any resources
     * that are being held (for example, memory, file handles, threads) and make
     * sure that any persistent state is synchronized with the servlet's current
     * state in memory.
     */
    public void destroy();
}

從代碼中可以看出,servlet生命周期是init-->service-->destory;

具體過程

1、加載與實(shí)例化(new)
servlet在啟動(dòng)時(shí)或第一次接收到請(qǐng)求時(shí),會(huì)到內(nèi)存中去查詢一下是否有servlet實(shí)例,有則取出來(lái),沒有則new一個(gè)出來(lái)。
2、初始化(init)
在創(chuàng)建servlet之后,會(huì)調(diào)用init方法,進(jìn)行初始化,該步主要是為接收處理請(qǐng)求做一些必要的準(zhǔn)備,只會(huì)執(zhí)行一次。
3、提供服務(wù)(service)
servlet實(shí)例接收客戶端的ServletRequest信息,通過request的信息,調(diào)用相應(yīng)的doXXX()方法,并返回response。
4、銷毀(destroy)
servlet容器在關(guān)閉時(shí),銷毀servlet實(shí)例。只會(huì)執(zhí)行一次

后面以SpringMVC為例來(lái)具體說(shuō)明servlet的生命周期。

SpringMVC相關(guān)知識(shí)

這里只簡(jiǎn)單的羅列一下SpringMVC中M,V,C 的關(guān)系。如圖:


springMVC框架圖.png

1、客戶端發(fā)送request請(qǐng)求進(jìn)入到分發(fā)器中,DispatcherServlet中。
2、分發(fā)器通過uri到控制器映射中去查詢相應(yīng)的處理器(HandlerMapping)。
3、分發(fā)器拿到對(duì)應(yīng)的處理器之后,調(diào)用處理器響應(yīng)的接口,返回?cái)?shù)據(jù)及對(duì)應(yīng)的視圖。
4、分發(fā)器拿到處理器返回的modelAndView后,查找相應(yīng)的視同解析器進(jìn)行渲染。
5、視圖將渲染好的結(jié)果返回給終端用戶進(jìn)行展示。

下面通過源碼的方式看springboot中關(guān)于springMVC和servlet相關(guān)的實(shí)現(xiàn)。

請(qǐng)求消息在SpringMVC中的流向

servlet的創(chuàng)建過程(new)

源碼閱讀接上篇文章,現(xiàn)在到了servlet的處理了。在這之前,看servlet在springboot中是如何完成實(shí)例化及初始化的。
首先看servlet是如何new出來(lái)的。springboot中,使用的是dispatcherServlet,DispatcherServlet的類的繼承關(guān)系圖如下

DispatcherServlet.png

該類使用自動(dòng)裝配的方式初始化一個(gè)beanName為dispatcherServlet的bean。類名為DispatcherServletAutoConfiguration,具體代碼如下:

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
            dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

這里就是直接new出來(lái)一個(gè)servlet,并初始化相關(guān)的配置。
這個(gè)servlet對(duì)象是如何注冊(cè)到tomcat容器里呢,這里就有下面一個(gè)名稱為dispatcherServletRegistration的bean來(lái)做的事情了,具體代碼為:

@Configuration(proxyBeanMethods = false)
    @Conditional(DispatcherServletRegistrationCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    @Import(DispatcherServletConfiguration.class)
    protected static class DispatcherServletRegistrationConfiguration {

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
                WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
                    webMvcProperties.getServlet().getPath());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }

    }

這里需要看一下DispatcherServletRegistrationBean類的繼承關(guān)系圖了。

DispatcherServletRegistrationBean.png

從圖中看出,DispatcherServletRegistrationBean實(shí)現(xiàn)了接口ServletContextInitializerSpringBoot內(nèi)嵌tomcat代碼走讀(一)中,有對(duì)這個(gè)接口的調(diào)用代碼。this.webServer = factory.getWebServer(getSelfInitializer());在獲取webServer時(shí),傳入想改接口的調(diào)用。
接下來(lái)我們看DispatcherServletRegistrationBean的OnStartup的實(shí)現(xiàn),該方法實(shí)現(xiàn)在其父類RegistrationBean中。

    @Override
    public final void onStartup(ServletContext servletContext) throws ServletException {
        String description = getDescription();
        if (!isEnabled()) {
            logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
            return;
        }
        register(description, servletContext);
    }

主要方法register的實(shí)現(xiàn)在其子類DynamicRegistrationBean中。

    @Override
    protected final void register(String description, ServletContext servletContext) {
        D registration = addRegistration(description, servletContext);
        if (registration == null) {
            logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
            return;
        }
        configure(registration);
    }

其主要方法addRegistration在其子類ServletRegistrationBean中實(shí)現(xiàn)。

    @Override
    protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
        String name = getServletName();
        return servletContext.addServlet(name, this.servlet);
    }

servletContextApplicationContextFacadeaddServlet代碼為:

    @Override
    public ServletRegistration.Dynamic addServlet(String servletName,
            Servlet servlet) {
        if (SecurityUtil.isPackageProtectionEnabled()) {
            return (ServletRegistration.Dynamic) doPrivileged("addServlet",
                    new Class[]{String.class, Servlet.class},
                    new Object[]{servletName, servlet});
        } else {
            return context.addServlet(servletName, servlet);
        }
    }

Tomcat的ApplicationContext里加入servlet。代碼為:

 private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
            Servlet servlet, Map<String,String> initParams) throws IllegalStateException {

        if (servletName == null || servletName.equals("")) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.invalidServletName", servletName));
        }

        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            //TODO Spec breaking enhancement to ignore this restriction
            throw new IllegalStateException(
                    sm.getString("applicationContext.addServlet.ise",
                            getContextPath()));
        }

        Wrapper wrapper = (Wrapper) context.findChild(servletName);

        // Assume a 'complete' ServletRegistration is one that has a class and
        // a name
        if (wrapper == null) {
            wrapper = context.createWrapper();
            wrapper.setName(servletName);
            context.addChild(wrapper);
        } else {
            if (wrapper.getName() != null &&
                    wrapper.getServletClass() != null) {
                if (wrapper.isOverridable()) {
                    wrapper.setOverridable(false);
                } else {
                    return null;
                }
            }
        }

        ServletSecurity annotation = null;
        if (servlet == null) {
            wrapper.setServletClass(servletClass);
            Class<?> clazz = Introspection.loadClass(context, servletClass);
            if (clazz != null) {
                annotation = clazz.getAnnotation(ServletSecurity.class);
            }
        } else {
            wrapper.setServletClass(servlet.getClass().getName());
            wrapper.setServlet(servlet);
            if (context.wasCreatedDynamicServlet(servlet)) {
                annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
            }
        }

        if (initParams != null) {
            for (Map.Entry<String, String> initParam: initParams.entrySet()) {
                wrapper.addInitParameter(initParam.getKey(), initParam.getValue());
            }
        }

        ServletRegistration.Dynamic registration =
                new ApplicationServletRegistration(wrapper, context);
        if (annotation != null) {
            registration.setServletSecurity(new ServletSecurityElement(annotation));
        }
        return registration;
    }

這里主要邏輯是向tomcat里添加servlet。前面我們知道tomcat的體系結(jié)構(gòu),為了提高擴(kuò)展性,tomcat里分層定義了很多組件,在最內(nèi)層組件為Wrapper,這里就是想warapper中添加servlet。這里用默認(rèn)的wrapper,StandardWrapper,具體代碼為:

    @Override
    public void setServlet(Servlet servlet) {
        instance = servlet;
    }

至此,tomcat的基本功能就有了,servlet也有了。但是,當(dāng)前的servlet還不能用,因?yàn)閺纳厦娴膕ervlet生命周期看,這只完成了第一步,還有init這一步?jīng)]有完成。那么,init在什么時(shí)候呢。繼續(xù)看代碼。
從代碼中可以知道,當(dāng)?shù)谝粋€(gè)請(qǐng)求過來(lái)時(shí),servlet才進(jìn)行init操作。接上篇文章的請(qǐng)求流程圖,這里看請(qǐng)求在servlet中的流向。這里流程圖從StandardWrapperValue的invoke方法開始進(jìn)行。
調(diào)用流程圖如下:


servlet流程圖.png

按照流程圖,我們走讀一下代碼,具體看看相關(guān)的處理邏輯。
首先看invoke方法里的代碼邏輯。

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

        // Initialize local variables we may need
        boolean unavailable = false;
        Throwable throwable = null;
        // This should be a Request attribute...
        long t1=System.currentTimeMillis();
        requestCount.incrementAndGet();
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        
        //去掉一些與主流程關(guān)系不太大的代碼
        .......

        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) {
                servlet = wrapper.allocate();
            }
        } catch (UnavailableException e) {
          。。。。。。

        MessageBytes requestPathMB = request.getRequestPathMB();
        DispatcherType dispatcherType = DispatcherType.REQUEST;
        if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
        request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
        request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
                requestPathMB);
        // Create the filter chain for this request
        //獲取一個(gè)ApplicationFilterChain
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

        // Call the filter chain for this request
        // NOTE: This also calls the servlet's service() method
        Container container = this.container;
        try {
            if ((servlet != null) && (filterChain != null)) {
               
               。。。。。。

                } else {
                    // 開始處理請(qǐng)求
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        // 開始處理請(qǐng)求
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
        } catch (ClientAbortException | CloseNowException e) {
            。。。。。。

        }
    }

上面代碼比較長(zhǎng),做了一下刪減。主要的功能是初始化servlet(init)和具體調(diào)用servlet的service方法。
servletinit方法在allocate()中調(diào)用。service方法在doFilter中調(diào)用。

servlet初始化過程(init)

具體看下allocate()的實(shí)現(xiàn)。

 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
        //該處并不知道singleThreadModel的值,使用默認(rèn)值,在loadServlet中會(huì)有確定的賦值,但SingleThreadedModel已經(jīng)被廢棄,這里也走不到該分支中,所以,springboot中的servlet是一個(gè)實(shí)例。
        //當(dāng)為SingleThreadedModel類型時(shí),容器中會(huì)有多個(gè)servlet實(shí)例,默認(rèn)最多20個(gè)。
        if (!singleThreadModel) {
            // Load and initialize our instance if necessary
            //前文提到了instance在bean初始化時(shí)已經(jīng)將instance賦值了,這里instance!=null,但instanceInitialized=false
            if (instance == null || !instanceInitialized) {
                synchronized (this) {
                    //該分支在該處不會(huì)走到。
                    if (instance == null) {
                       。。。。。。
                    }
                    if (!instanceInitialized) {
                        //init servlet
                        initServlet(instance);
                    }
                }
            }

            if (singleThreadModel) {
                if (newInstance) {
                    // Have to do this outside of the sync above to prevent a
                    // possible deadlock
                    synchronized (instancePool) {
                        instancePool.push(instance);
                        nInstances++;
                    }
                }
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("  Returning non-STM instance");
                }
                // For new instances, count will have been incremented at the
                // time of creation
                if (!newInstance) {
                    countAllocated.incrementAndGet();
                }
                return instance;
            }
        }

        synchronized (instancePool) {
            while (countAllocated.get() >= nInstances) {
                // Allocate a new instance if possible, or else wait
                if (nInstances < maxInstances) {
                    try {
                        instancePool.push(loadServlet());
                        nInstances++;
                    } catch (ServletException e) {
                        throw e;
                    } catch (Throwable e) {
                        ExceptionUtils.handleThrowable(e);
                        throw new ServletException(sm.getString("standardWrapper.allocate"), e);
                    }
                } else {
                    try {
                        instancePool.wait();
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }
            }
            if (log.isTraceEnabled()) {
                log.trace("  Returning allocated STM instance");
            }
            countAllocated.incrementAndGet();
            return instancePool.pop();
        }
    }

allocate方法中,instance !=null,singleThreadModel 一直都為false,因?yàn)椴粫?huì)走到loadServlet中,singleThreadModel的值一直為false。容器中servlet實(shí)例只有一個(gè)。提一句:如果singleThreadModel類型為SingleThreadModel時(shí),容器中可以有多個(gè)servlet實(shí)例,放在一個(gè)大小為20的棧中。SingleThreadModel已經(jīng)被廢棄掉了@deprecated As of Java Servlet API 2.4, with no direct replacement.

下面看initServlet()方法的處理邏輯。

    private synchronized void initServlet(Servlet servlet)
            throws ServletException {

        if (instanceInitialized && !singleThreadModel) return;

        // Call the initialization method of this servlet
        try {
            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;
        } catch (UnavailableException f) {
            unavailable(f);
            throw f;
        } catch (ServletException 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(sm.getString("standardWrapper.initException", getName()), 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);
        }
    }

這段代碼很簡(jiǎn)單,就是調(diào)用servlet的init方法。
具體的init方法實(shí)現(xiàn)在其父類GenericServlet中。

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

這里的init方法在子類HttpServletBean中。

        // Let subclasses do whatever initialization they like.
        initServletBean();
    }

initServletBean()方法在子類FrameworkServlet中。

@Override
    protected final void initServletBean() throws ServletException {
        
        long startTime = System.currentTimeMillis();

        try {
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        catch (ServletException | RuntimeException ex) {
            logger.error("Context initialization failed", ex);
            throw ex;
        }
    }

initFrameworkServlet()是留給子類實(shí)現(xiàn)的一個(gè)擴(kuò)展接口,默認(rèn)為空實(shí)現(xiàn),主要的邏輯在initWebApplicationContext()中,該方法主要是初始化一些策略,如文件解析器,國(guó)際化解析器,主題解析器,處理器映射器,處理器適配器,異常處理器,視圖解析器等。具體的實(shí)現(xiàn)在子類DispatcherServlet中。
代碼如下:

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        //文件解析器,沒有,返回為空。
        initMultipartResolver(context);
        //國(guó)際化解析,沒有,返回默認(rèn)值,AcceptHeaderLocaleResolver
        initLocaleResolver(context);
        //主題解析,沒有返回默認(rèn)值FixedThemeResolver
        initThemeResolver(context);
        //處理器映射,若沒有,返回默認(rèn)值BeanNameUrlHandlerMapping,RequestMappingHandlerMapping,RouterFunctionMapping
        initHandlerMappings(context);
        //處理器適配器,若沒有,返回默認(rèn)值HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter,HandlerFunctionAdapter
        initHandlerAdapters(context);
        //異常解析,若沒有,返回默認(rèn)值ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver
        initHandlerExceptionResolvers(context);
        //異常后,沒有返回視圖時(shí)的默認(rèn)處理DefaultRequestToViewNameTranslator
        initRequestToViewNameTranslator(context);
        //視圖解析,沒有返回InternalResourceViewResolver
        initViewResolvers(context);
        //默認(rèn)值SessionFlashMapManager
        initFlashMapManager(context);
    }

后面通過看具體的請(qǐng)求處理來(lái)看上面的處理器如何工作的。

到此,servlet的init方法正式處理完成,就可以提供服務(wù)了。

servlet提供服務(wù)過程(service)

下面,看service如何處理。
從前文中的代碼中可以知道,在調(diào)用service之前,需要獲取一個(gè)ApplicationFilterChain,先來(lái)看看ApplicationFilterFactorycreateFilterChain里做了哪些事情。從類名中可以猜到,這里主要是添加一些Filter相關(guān)的信息。具體代碼為:

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

        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
            } 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.setServletSupportsAsync(wrapper.isAsyncSupported());

        // Acquire the filter mappings for this Context
        StandardContext context = (StandardContext) wrapper.getParent();
        //獲取Filter列表
        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
        DispatcherType 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();
        }

        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain
        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMap, requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Add filters that match on servlet name second
        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMap, servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Return the completed filter chain
        return filterChain;
    }

其中比較重要的一行代碼FilterMap filterMaps[] = context.findFilterMaps();,獲取過濾器列表。

    @Override
    public FilterMap[] findFilterMaps() {
        return filterMaps.asArray();
    }

這里filterMaps是在什么時(shí)候進(jìn)行賦值的呢?此處又會(huì)回到ServletContextInitializer這個(gè)接口的onStartup方法里了。
前文里提到過,在createWebServer時(shí),有個(gè)方法引用getSelfInitializer。這里同樣走到這段代碼里。

Filter相關(guān)的準(zhǔn)備工作

我們?cè)谕ǔT趕pringboot里定義過濾器時(shí),可以使用如下代碼:

    @Bean
    public FilterRegistrationBean xxxFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        XxxFilter xxxFilter = new XxxFilter();
        registrationBean.addUrlPatterns("/*");
        registrationBean.setFilter(xxxFilter);
        return registrationBean;

    }

看下FilterRegistrationBean的類圖:

FilterRegistrationBean.png

FilterRegistrationBean同樣實(shí)現(xiàn)了ServletContextInitializer接口。
同時(shí),我們還可以進(jìn)行如下方式定義過濾器。

@Service
public class TestFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

這種方式?jīng)]有顯式的定義成一個(gè)FilterRegistrationBean,在代碼中事實(shí)上也封裝成一個(gè)FilterRegistrationBean。接下來(lái)看具體的代碼實(shí)現(xiàn)。
首先看ServletContextInitializeronStartup的調(diào)用處代碼。

    private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareWebApplicationContext(servletContext);
        registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }

getServletContextInitializerBeans()里從bean工廠里獲取ServletContextInitializer類型的bean,調(diào)用onStartup方法。

    protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
        return new ServletContextInitializerBeans(getBeanFactory());
    }
    public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
            Class<? extends ServletContextInitializer>... initializerTypes) {
        this.initializers = new LinkedMultiValueMap<>();
        this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
                : Collections.singletonList(ServletContextInitializer.class);
        //顯式定義為ServletContextInitializer的bean
        addServletContextInitializerBeans(beanFactory);
        //未顯式定義為ServletContextInitializer的bean
        addAdaptableBeans(beanFactory);
        List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
                .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
                .collect(Collectors.toList());
        this.sortedList = Collections.unmodifiableList(sortedInitializers);
        logMappings(this.initializers);
    }

顯式定義為ServletContextInitializer的bean的處理方法:

    private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
        for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
            for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
                    initializerType)) {
                addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
            }
        }
    }
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
            ListableBeanFactory beanFactory) {
        if (initializer instanceof ServletRegistrationBean) {
            Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
            addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof FilterRegistrationBean) {
            Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
            addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
            String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
            addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof ServletListenerRegistrationBean) {
            EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
            addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
        }
        else {
            addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
                    initializer);
        }
    }
    private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
            ListableBeanFactory beanFactory, Object source) {
        this.initializers.add(type, initializer);
        if (source != null) {
            // Mark the underlying source as seen in case it wraps an existing bean
            this.seen.add(source);
        }
        if (logger.isTraceEnabled()) {
            String resourceDescription = getResourceDescription(beanName, beanFactory);
            int order = getOrder(initializer);
            logger.trace("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order="
                    + order + ", resource=" + resourceDescription);
        }
    }

未顯式定義為ServletContextInitializer的bean

    protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
        MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
        addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
        addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
        for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
            addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
                    new ServletListenerRegistrationBeanAdapter());
        }
    }
    private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
            Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
        List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
        for (Entry<String, B> entry : entries) {
            String beanName = entry.getKey();
            B bean = entry.getValue();
            if (this.seen.add(bean)) {
                // One that we haven't already seen
                //通過適配器模式,進(jìn)行處理。
                RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
                int order = getOrder(bean);
                registration.setOrder(order);
                this.initializers.add(type, registration);
                if (logger.isTraceEnabled()) {
                    logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
                            + order + ", resource=" + getResourceDescription(beanName, beanFactory));
                }
            }
        }
    }
    private static class FilterRegistrationBeanAdapter implements RegistrationBeanAdapter<Filter> {

        @Override
        public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {
            FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(source);
            bean.setName(name);
            return bean;
        }

    }

代碼比較簡(jiǎn)單,這里只做代碼的羅列,不做詳細(xì)說(shuō)明。
接下來(lái)看FilterRegistrationBeanonStartup方法的處理邏輯,該方法的實(shí)現(xiàn)在其父類RegistrationBean中。

    @Override
    public final void onStartup(ServletContext servletContext) throws ServletException {
        String description = getDescription();
        if (!isEnabled()) {
            logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
            return;
        }
        register(description, servletContext);
    }

register方法在RegistrationBean子類DynamicRegistrationBean中。

    @Override
    protected final void register(String description, ServletContext servletContext) {
        D registration = addRegistration(description, servletContext);
        if (registration == null) {
            logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
            return;
        }
        configure(registration);
    }

addRegistration在其子類AbstractFilterRegistrationBean中實(shí)現(xiàn)。

    @Override
    protected Dynamic addRegistration(String description, ServletContext servletContext) {
        Filter filter = getFilter();
        return servletContext.addFilter(getOrDeduceName(filter), filter);
    }

addFilter代碼走到ApplicationContextFacade中。

    @Override
    public FilterRegistration.Dynamic addFilter(String filterName,
            Filter filter) {
        if (SecurityUtil.isPackageProtectionEnabled()) {
            return (FilterRegistration.Dynamic) doPrivileged("addFilter",
                    new Class[]{String.class, Filter.class},
                    new Object[]{filterName, filter});
        } else {
            return context.addFilter(filterName, filter);
        }
    }

最后走到ApplicationContext中。

 private FilterRegistration.Dynamic addFilter(String filterName,
            String filterClass, Filter filter) throws IllegalStateException {


        FilterDef filterDef = context.findFilterDef(filterName);

        if (filterDef == null) {
            filterDef = new FilterDef();
            filterDef.setFilterName(filterName);
            context.addFilterDef(filterDef);
        } else {
            if (filterDef.getFilterName() != null &&
                    filterDef.getFilterClass() != null) {
                return null;
            }
        }

     ......

        return new ApplicationFilterRegistration(filterDef, context);
    }

最后將FilterDef加入到StandradContextfilterDefs中。
回過頭來(lái),看DynamicRegistrationBean中register的第二個(gè)主要調(diào)用的方法configure()
對(duì)于過濾器來(lái)說(shuō),實(shí)現(xiàn)方法在AbstractFilterRegistrationBean中。

@Override
    protected void configure(FilterRegistration.Dynamic registration) {
        super.configure(registration);
        EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
        if (dispatcherTypes == null) {
            T filter = getFilter();
            if (ClassUtils.isPresent("org.springframework.web.filter.OncePerRequestFilter",
                    filter.getClass().getClassLoader()) && filter instanceof OncePerRequestFilter) {
                dispatcherTypes = EnumSet.allOf(DispatcherType.class);
            }
            else {
                dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
            }
        }
        Set<String> servletNames = new LinkedHashSet<>();
        for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
            servletNames.add(servletRegistrationBean.getServletName());
        }
        servletNames.addAll(this.servletNames);
        if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
            registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);
        }
        else {
            if (!servletNames.isEmpty()) {
                registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
                        StringUtils.toStringArray(servletNames));
            }
            if (!this.urlPatterns.isEmpty()) {
                registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
                        StringUtils.toStringArray(this.urlPatterns));
            }
        }
    }

registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);這里的方法將每個(gè)filter通過StandardContext的addFilterMapBefore方式,塞到filterMap中的arrays中。通過findFilterMaps就能得到所有的filter了。
到此,F(xiàn)ilter相關(guān)的準(zhǔn)備工作完成。

需要補(bǔ)充上面流程的流程圖

回到service

回到前文的ApplicationFilterChain的獲取上。ApplicationFilterFactory里通過context.findFilterMaps();獲取所有的過濾器,放入filterChain中。
StandardWrapperValue中代碼繼續(xù)往下走,調(diào)用了ApplicationFilterChain的doFilter方法。

 private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // 遍歷所有的filter,這里就是為啥我們?cè)诙xFilter時(shí),為了讓chain能順利的走完需要調(diào)用chain.doFilter()的原因。
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
               。。。。。。
                } else {
                    filter.doFilter(request, response, this);
                }
            } 
            。。。。。。
            }
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            。。。。。。
            if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse) &&
                    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);
            }
        } 。。。。。。
        。。。。。。
        }
    }

以上代碼就進(jìn)入到了servlet的另外一個(gè)生命周期了,提供service服務(wù)了。
下篇文章,我們具體看下service接口里的邏輯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。