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

上篇文章走讀了springboot中消息處理進入到servlet里了,這次娜搂,具體走讀了請求消息在servlet里是怎么處理的。這里主要補充servlet和springmvc相關的知識。

servlet相關知識

servlet生命周期

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


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.
     */
     //真正的處理請求
    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硼婿、加載與實例化(new)
servlet在啟動時或第一次接收到請求時,會到內(nèi)存中去查詢一下是否有servlet實例禽车,有則取出來寇漫,沒有則new一個出來刊殉。
2、初始化(init)
在創(chuàng)建servlet之后州胳,會調(diào)用init方法记焊,進行初始化,該步主要是為接收處理請求做一些必要的準備栓撞,只會執(zhí)行一次遍膜。
3、提供服務(service)
servlet實例接收客戶端的ServletRequest信息瓤湘,通過request的信息瓢颅,調(diào)用相應的doXXX()方法,并返回response弛说。
4挽懦、銷毀(destroy)
servlet容器在關閉時,銷毀servlet實例木人。只會執(zhí)行一次

后面以SpringMVC為例來具體說明servlet的生命周期信柿。

SpringMVC相關知識

這里只簡單的羅列一下SpringMVC中M,V虎囚,C 的關系角塑。如圖:


springMVC框架圖.png

1、客戶端發(fā)送request請求進入到分發(fā)器中淘讥,DispatcherServlet中圃伶。
2、分發(fā)器通過uri到控制器映射中去查詢相應的處理器(HandlerMapping)蒲列。
3窒朋、分發(fā)器拿到對應的處理器之后,調(diào)用處理器響應的接口蝗岖,返回數(shù)據(jù)及對應的視圖侥猩。
4、分發(fā)器拿到處理器返回的modelAndView后抵赢,查找相應的視同解析器進行渲染欺劳。
5、視圖將渲染好的結果返回給終端用戶進行展示铅鲤。

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

請求消息在SpringMVC中的流向

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

源碼閱讀接上篇文章,現(xiàn)在到了servlet的處理了邢享。在這之前鹏往,看servlet在springboot中是如何完成實例化及初始化的。
首先看servlet是如何new出來的骇塘。springboot中伊履,使用的是dispatcherServlet韩容,DispatcherServlet的類的繼承關系圖如下

DispatcherServlet.png

該類使用自動裝配的方式初始化一個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出來一個servlet唐瀑,并初始化相關的配置群凶。
這個servlet對象是如何注冊到tomcat容器里呢,這里就有下面一個名稱為dispatcherServletRegistration的bean來做的事情了介褥,具體代碼為:

@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類的繼承關系圖了座掘。

DispatcherServletRegistrationBean.png

從圖中看出,DispatcherServletRegistrationBean實現(xiàn)了接口ServletContextInitializer柔滔,SpringBoot內(nèi)嵌tomcat代碼走讀(一)中溢陪,有對這個接口的調(diào)用代碼。this.webServer = factory.getWebServer(getSelfInitializer());在獲取webServer時睛廊,傳入想改接口的調(diào)用形真。
接下來我們看DispatcherServletRegistrationBean的OnStartup的實現(xiàn),該方法實現(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的實現(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中實現(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的體系結構,為了提高擴展性疏遏,tomcat里分層定義了很多組件脉课,在最內(nèi)層組件為Wrapper,這里就是想warapper中添加servlet财异。這里用默認的wrapper倘零,StandardWrapper,具體代碼為:

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

至此戳寸,tomcat的基本功能就有了呈驶,servlet也有了。但是疫鹊,當前的servlet還不能用袖瞻,因為從上面的servlet生命周期看,這只完成了第一步拆吆,還有init這一步?jīng)]有完成虏辫。那么,init在什么時候呢锈拨。繼續(xù)看代碼。
從代碼中可以知道羹唠,當?shù)谝粋€請求過來時奕枢,servlet才進行init操作娄昆。接上篇文章的請求流程圖,這里看請求在servlet中的流向缝彬。這里流程圖從StandardWrapperValue的invoke方法開始進行萌焰。
調(diào)用流程圖如下:


servlet流程圖.png

按照流程圖,我們走讀一下代碼谷浅,具體看看相關的處理邏輯扒俯。
首先看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();
        
        //去掉一些與主流程關系不太大的代碼
        .......

        // 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
        //獲取一個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 {
                    // 開始處理請求
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        // 開始處理請求
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
        } catch (ClientAbortException | CloseNowException e) {
            斋配。孔飒。。许起。十偶。。

        }
    }

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

servlet初始化過程(init)

具體看下allocate()的實現(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的值鹿寻,使用默認值睦柴,在loadServlet中會有確定的賦值,但SingleThreadedModel已經(jīng)被廢棄毡熏,這里也走不到該分支中坦敌,所以,springboot中的servlet是一個實例。
        //當為SingleThreadedModel類型時狱窘,容器中會有多個servlet實例杜顺,默認最多20個。
        if (!singleThreadModel) {
            // Load and initialize our instance if necessary
            //前文提到了instance在bean初始化時已經(jīng)將instance賦值了蘸炸,這里instance躬络!=null,但instanceInitialized=false
            if (instance == null || !instanceInitialized) {
                synchronized (this) {
                    //該分支在該處不會走到。
                    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德撬,因為不會走到loadServlet中铲咨,singleThreadModel的值一直為false。容器中servlet實例只有一個蜓洪。提一句:如果singleThreadModel類型為SingleThreadModel時纤勒,容器中可以有多個servlet實例,放在一個大小為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);
        }
    }

這段代碼很簡單恐仑,就是調(diào)用servlet的init方法泉坐。
具體的init方法實現(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()是留給子類實現(xiàn)的一個擴展接口,默認為空實現(xiàn)歧斟,主要的邏輯在initWebApplicationContext()中纯丸,該方法主要是初始化一些策略,如文件解析器静袖,國際化解析器觉鼻,主題解析器,處理器映射器队橙,處理器適配器坠陈,異常處理器萨惑,視圖解析器等。具體的實現(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);
        //國際化解析,沒有倾鲫,返回默認值粗合,AcceptHeaderLocaleResolver
        initLocaleResolver(context);
        //主題解析,沒有返回默認值FixedThemeResolver
        initThemeResolver(context);
        //處理器映射乌昔,若沒有隙疚,返回默認值BeanNameUrlHandlerMapping,RequestMappingHandlerMapping磕道,RouterFunctionMapping
        initHandlerMappings(context);
        //處理器適配器供屉,若沒有,返回默認值HttpRequestHandlerAdapter溺蕉,SimpleControllerHandlerAdapter伶丐,RequestMappingHandlerAdapter,HandlerFunctionAdapter
        initHandlerAdapters(context);
        //異常解析疯特,若沒有哗魂,返回默認值ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver漓雅,DefaultHandlerExceptionResolver
        initHandlerExceptionResolvers(context);
        //異常后录别,沒有返回視圖時的默認處理DefaultRequestToViewNameTranslator
        initRequestToViewNameTranslator(context);
        //視圖解析,沒有返回InternalResourceViewResolver
        initViewResolvers(context);
        //默認值SessionFlashMapManager
        initFlashMapManager(context);
    }

后面通過看具體的請求處理來看上面的處理器如何工作的邻吞。

到此组题,servlet的init方法正式處理完成,就可以提供服務了抱冷。

servlet提供服務過程(service)

下面崔列,看service如何處理。
從前文中的代碼中可以知道徘层,在調(diào)用service之前峻呕,需要獲取一個ApplicationFilterChain,先來看看ApplicationFilterFactorycreateFilterChain里做了哪些事情趣效。從類名中可以猜到瘦癌,這里主要是添加一些Filter相關的信息。具體代碼為:

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是在什么時候進行賦值的呢?此處又會回到ServletContextInitializer這個接口的onStartup方法里了。
前文里提到過斤寇,在createWebServer時桶癣,有個方法引用getSelfInitializer。這里同樣走到這段代碼里娘锁。

Filter相關的準備工作

我們在通常在springboot里定義過濾器時牙寞,可以使用如下代碼:

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

    }

看下FilterRegistrationBean的類圖:

FilterRegistrationBean.png

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

@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)]有顯式的定義成一個FilterRegistrationBean,在代碼中事實上也封裝成一個FilterRegistrationBean镊屎。接下來看具體的代碼實現(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
                //通過適配器模式,進行處理用狱。
                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;
        }

    }

代碼比較簡單运怖,這里只做代碼的羅列,不做詳細說明齿拂。
接下來看FilterRegistrationBeanonStartup方法的處理邏輯驳规,該方法的實現(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中實現(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中捻勉。
回過頭來,看DynamicRegistrationBean中register的第二個主要調(diào)用的方法configure()
對于過濾器來說刀森,實現(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);這里的方法將每個filter通過StandardContext的addFilterMapBefore方式,塞到filterMap中的arrays中研底。通過findFilterMaps就能得到所有的filter了铸抑。
到此辆它,F(xiàn)ilter相關的準備工作完成钦铁。

需要補充上面流程的流程圖廓啊。

回到service

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

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

        // 遍歷所有的filter斩郎,這里就是為啥我們在定義Filter時脑融,為了讓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);
            }
        } 愈污。。轮傍。暂雹。。创夜。
        杭跪。。驰吓。涧尿。。檬贰。
        }
    }

以上代碼就進入到了servlet的另外一個生命周期了姑廉,提供service服務了。
下篇文章偎蘸,我們具體看下service接口里的邏輯庄蹋。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瞬内,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子限书,更是在濱河造成了極大的恐慌虫蝶,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倦西,死亡現(xiàn)場離奇詭異能真,居然都是意外死亡,警方通過查閱死者的電腦和手機扰柠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門粉铐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人卤档,你說我怎么就攤上這事蝙泼。” “怎么了劝枣?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵汤踏,是天一觀的道長。 經(jīng)常有香客問我舔腾,道長溪胶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任稳诚,我火速辦了婚禮哗脖,結果婚禮上,老公的妹妹穿的比我還像新娘扳还。我一直安慰自己才避,他們只是感情好,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布氨距。 她就那樣靜靜地躺著工扎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衔蹲。 梳的紋絲不亂的頭發(fā)上肢娘,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機與錄音舆驶,去河邊找鬼橱健。 笑死,一個胖子當著我的面吹牛沙廉,可吹牛的內(nèi)容都是我干的拘荡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼撬陵,長吁一口氣:“原來是場噩夢啊……” “哼珊皿!你這毒婦竟也來了网缝?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤蟋定,失蹤者是張志新(化名)和其女友劉穎粉臊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驶兜,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡扼仲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了抄淑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屠凶。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肆资,靈堂內(nèi)的尸體忽然破棺而出矗愧,到底是詐尸還是另有隱情,我是刑警寧澤郑原,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布贱枣,位于F島的核電站,受9級特大地震影響颤专,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钠乏,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一栖秕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晓避,春花似錦簇捍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锅必,卻和暖如春事格,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搞隐。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工驹愚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劣纲。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓逢捺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親癞季。 傳聞我的和親對象是個殘疾皇子劫瞳,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349