SpringMVC請求處理流程源碼

  咱們首先引用《Spring in Action》上的一張圖來了解Spring MVC 的核心組件和大體處理流程:css

  從上圖中看到①、DispatcherServlet 是SpringMVC 中的前端控制器(Front Controller),負責接收Request 並將Request 轉發給對應的處理組件。html

② 、HanlerMapping 是SpringMVC 中完成url 到Controller 映射的組件。DispatcherServlet 接收Request, 而後從HandlerMapping 查找處理Request 的Controller。前端

③、Controller 處理Request,並返回ModelAndView 對象,Controller 是SpringMVC中負責處理Request 的組件(相似於Struts2 中的Action),ModelAndView 是封裝結果視圖的組件。ios

④、⑤、⑥視圖解析器解析ModelAndView 對象並返回對應的視圖給客戶端。在前面的學習中咱們已經大體瞭解到,容器初始化時會創建全部url 和Controller 中的Method 的對應關係,保存到HandlerMapping 中,用戶請求是根據Request 請求的url 快速定位到Controller 中的某個方法。在Spring 中先將url 和Controller 的對應關係,保存到Map<url,Controller>中。Web 容器啓動時會通知Spring 初始化容器(加載Bean 的定義信息和初始化全部單例Bean),而後SpringMVC 會遍歷容器中的Bean,獲取每個Controller 中的全部方法訪問的url,而後將url 和Controller 保存到一個Map中;這樣就能夠根據Request 快速定位到Controller,由於最終處理Request 的是Controller 中的方法,Map 中只保留了url 和Controller 中的對應關係,因此要根據Request 的url 進一步確認Controller 中的Method,這一步工做的原理就是拼接Controller 的url(Controller 上@RequestMapping 的值) 和方法的url(Method 上@RequestMapping 的值),與request 的url 進行匹配,找到匹配的那個方法;肯定處理請求的Method 後,接下來的任務就是參數綁定,把Request 中參數綁定到方法的形式參數上,這一步是整個請求處理過程當中最複雜的一個步驟。web

Spring MVC 九大組件

MultipartResolver(多文件上傳組件)

  其實這是一個你們很熟悉的組件,MultipartResolver 用於處理上傳請求,經過將普通的Request 包裝成MultipartHttpServletRequest 來實現。MultipartHttpServletRequest能夠經過getFile() 直接得到文件,若是是多個文件上傳,還能夠經過調用getFileMap獲得Map<FileName, File> 這樣的結構。MultipartResolver 的做用就是用來封裝普通的request,使其擁有處理文件上傳的功能。json

LocaleResolver(本地語言環境組件)

  在上面咱們有看到ViewResolver 的resolveViewName()方法,須要兩個參數。那麼第二個參數Locale 是從哪來的呢,這就是LocaleResolver 要作的事了。LocaleResolver用於從request 中解析出Locale, 在中國大陸地區,Locale 固然就會是zh-CN 之類,用來表示一個區域。這個類也是i18n 的基礎。設計模式

ThemeResolver(模板主題處理組件)

  從名字即可看出,這個類是用來解析主題的。主題,就是樣式,圖片以及它們所造成的顯示效果的集合。Spring MVC 中一套主題對應一個properties 文件,裏面存放着跟當前主題相關的全部資源,如圖片,css 樣式等。建立主題很是簡單,只需準備好資源,而後新建一個"主題名.properties" 並將資源設置進去,放在classpath 下,即可以在頁面中使用了。Spring MVC 中跟主題有關的類有ThemeResolver, ThemeSource 和Theme。ThemeResolver 負責從request 中解析出主題名, ThemeSource 則根據主題名找到具體的主題, 其抽象也就是Theme, 經過Theme 來獲取主題和具體的資源。緩存

HandlerMappings

  HandlerMapping 是用來查找Handler 的,也就是處理器,具體的表現形式能夠是類也能夠是方法。好比,標註了@RequestMapping 的每一個method 均可以當作是一個Handler,由Handler 來負責實際的請求處理。HandlerMapping 在請求到達以後,它的做用即是找到請求相應的處理器Handler 和Interceptors。安全

HandlerAdapters (初始化攔截適配器)

  從名字上看,這是一個適配器。由於Spring MVC 中Handler 能夠是任意形式的,只要可以處理請求便行, 可是把請求交給Servlet 的時候,因爲Servlet 的方法結構都是如doService(HttpServletRequest req, HttpServletResponse resp) 這樣的形式,讓固定的Servlet 處理方法調用Handler 來進行處理,這一步工做即是HandlerAdapter 要作的事。app

HandlerExceptionResolvers(異常處理組件)

  從這個組件的名字上看,這個就是用來處理Handler 過程當中產生的異常狀況的組件。具體來講,此組件的做用是根據異常設置ModelAndView, 以後再交給render()方法進行渲染, 而render() 便將ModelAndView 渲染成頁面。不過有一點,HandlerExceptionResolver 只是用於解析對請求作處理階段產生的異常,而渲染階段的異常則不歸他管了,這也是Spring MVC 組件設計的一大原則分工明確互不干涉。

RequestToViewNameTranslator(視圖預處理器組件)

  這個組件的做用,在於從Request 中獲取viewName. 由於ViewResolver 是根據ViewName 查找View, 但有的Handler 處理完成以後,沒有設置View 也沒有設置ViewName, 便要經過這個組件來從Request 中查找viewName。

ViewResolvers(試圖轉換器)

  視圖解析器,相信你們對這個應該都很熟悉了。由於一般在SpringMVC 的配置文件中,都會配上一個該接口的實現類來進行視圖的解析。這個組件的主要做用,即是將String類型的視圖名和Locale 解析爲View 類型的視圖。這個接口只有一個resolveViewName()方法。從方法的定義就能夠看出,Controller 層返回的String 類型的視圖名viewName,最終會在這裏被解析成爲View。View 是用來渲染頁面的,也就是說,它會將程序返回的參數和數據填入模板中,最終生成html 文件。ViewResolver 在這個過程當中,主要作兩件大事,即,ViewResolver 會找到渲染所用的模板(使用什麼模板來渲染?)和所用的技術(其實也就是視圖的類型,如JSP 啊仍是其餘什麼Blabla 的)填入參數。默認狀況下,Spring MVC 會爲咱們自動配置一個InternalResourceViewResolver,這個是針對JSP 類型視圖的。

FlashMapManager()

  說到FlashMapManager,就得先提一下FlashMap。FlashMap 用於重定向Redirect 時的參數數據傳遞,好比,在處理用戶訂單提交時,爲了避免重複提交,能夠處理完post 請求後redirect 到一個get 請求,這個get 請求能夠用來顯示訂單詳情之類的信息。這樣作雖然能夠規避用戶刷新從新提交表單的問題,但是在這個頁面上要顯示訂單的信息,那這些數據從哪裏去獲取呢,由於redirect 重定向是沒有傳遞參數這一功能的,若是不想把參數寫進url(其實也不推薦這麼作,url 有長度限制不說,把參數都直接暴露,感受也不安全), 那麼就能夠經過flashMap 來傳遞。只須要在redirect 以前, 將要傳遞的數據寫入request ( 能夠經過ServletRequestAttributes.getRequest() 得到) 的屬性OUTPUT_FLASH_MAP_ATTRIBUTE 中,這樣在redirect 以後的handler 中Spring 就會自動將其設置到Model 中,在顯示訂單信息的頁面上,就能夠直接從Model 中取得數據了。而FlashMapManager 就是用來管理FlashMap 的。

Spring MVC 源碼分析

  根據上面分析的Spring MVC 工做機制,從三個部分來分析Spring MVC 的源代碼。

  1. ApplicationContext 初始化時用Map 保存全部url 和Controller 類的對應關係;
  2. 根據請求url 找到對應的Controller,並從Controller 中找處處理請求的方法;
  3. Request 參數綁定到方法的形參,執行方法處理請求,並返回結果視圖。

初始化階段

  咱們首先找到DispatcherServlet 這個類,必然是尋找init()方法。而後,咱們發現其init方法其實在父類HttpServletBean 中,其源碼以下:

public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        //從初始化參數設置bean屬性。
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                //定位資源
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                //加載配置信息
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }

        // Let subclasses do whatever initialization they like.
        //模板方法,子類能夠去自定義
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

  咱們看到在這段代碼中, 又調用了一個重要的initServletBean() 方法。進入initServletBean()方法看到如下源碼:

protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

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

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }

  這段代碼中 initWebApplicationContext() 最主要的邏輯就是初始化IOC 容器,最終會調用refresh()方法,前面的IOC 容器的初始化細節咱們已經詳細掌握,在此再也不贅述。

private boolean refreshEventReceived = false;
protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

    。。。。。。。。if (wac == null) {
            // No context instance is defined for this servlet -> create a local one
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            onRefresh(wac);
        }
    。。。。。。。。return wac;
    }

  咱們看到上面的代碼中,IOC 容器初始化以後,最後有調用了onRefresh()方法。這個方法最終是在DisptcherServlet 中實現,來看源碼:

@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);
        //初始化本地語言環境
        initLocaleResolver(context);
        //初始化模板處理器
        initThemeResolver(context);
        //handlerMapping
        initHandlerMappings(context);
        //初始化攔截適配器
        initHandlerAdapters(context);
        //初始化異常攔截器
        initHandlerExceptionResolvers(context);
        //初始化視圖預處理器
        initRequestToViewNameTranslator(context);
        //初始化視圖轉換器
        initViewResolvers(context);
        //FlashMap 管理器
        initFlashMapManager(context);
}

  到這一步就完成了Spring MVC 的九大組件的初始化。接下來,咱們來看url 和Controller的關係是如何創建的呢?HandlerMapping是個接口。先來看一下HandlerMapping 的實現類,會看到一個AbstractDetectingUrlHandlerMapping,能夠看到其實現了ApplicationContextAware,在Spring容器會檢測容器中的全部Bean,若是發現某個Bean實現了ApplicationContextAware接口,Spring容器會在建立該Bean以後,自動調用該Bean的setApplicationContextAware()方法。看看他的類圖再慢慢去尋找這個觸發點。

  最後咱們找啊找,會在ApplicationObjectSupport 發現了這個方法,繼而調用到了HandlerMapping 的子類AbstractDetectingUrlHandlerMapping 中的initApplicationContext()方法,因此咱們直接看子類中的初始化容器方法:

//創建當前ApplicationContext 中的全部Controller 和url 的對應關係
protected void detectHandlers() throws BeansException {
        ApplicationContext applicationContext = obtainApplicationContext();
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for URL mappings in application context: " + applicationContext);
        }
        // 獲取ApplicationContext 容器中全部bean 的Name
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
                applicationContext.getBeanNamesForType(Object.class));

        // Take any bean name that we can determine URLs for.
        // 遍歷beanNames,並找到這些bean 對應的url
        for (String beanName : beanNames) {
            // 找bean 上的全部url(Controller 上的url+方法上的url),該方法由對應的子類實現
            String[] urls = determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
                // URL paths found: Let's consider it a handler.
                // 保存urls 和beanName 的對應關係,put it to Map<urls,beanName>,
                // 該方法在父類AbstractUrlHandlerMapping 中實現
                registerHandler(urls, beanName);
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
                }
            }
        }
}

    /** 獲取Controller 中全部方法的url,由子類實現,典型的模板模式**/
protected abstract String[] determineUrlsForHandler(String beanName);

  determineUrlsForHandler(String beanName)方法的做用是獲取每一個Controller 中的url,不一樣的子類有不一樣的實現,這是一個典型的模板設計模式。由於開發中咱們用的最多的就是用註解來配置Controller 中的url , BeanNameUrlHandlerMapping 是AbstractDetectingUrlHandlerMapping 的子類,處理註解形式的url 映射.因此咱們這裏以BeanNameUrlHandlerMapping 來進行分析。咱們看BeanNameUrlHandlerMapping 是如何查beanName 上全部映射的url。

protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList<>();
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }
        String[] aliases = obtainApplicationContext().getAliases(beanName);
        for (String alias : aliases) {
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }
        return StringUtils.toStringArray(urls);
}

  到這裏HandlerMapping 組件就已經創建全部url 和Controller 的對應關係。

運行調用階段

  這一步步是由請求觸發的,因此入口爲DispatcherServlet 的核心方法爲doService(),doService()中的核心邏輯由doDispatch()實現,源代碼以下:

    /** 中央控制器,控制請求的轉發**/
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                // 1.檢查是不是文件上傳的請求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                // 2.取得處理當前請求的Controller,這裏也稱爲hanlder,處理器,
                // 第一個步驟的意義就在這裏體現了.這裏並非直接返回Controller,
                // 而是返回的HandlerExecutionChain 請求處理器鏈對象,
                // 該對象封裝了handler 和interceptors.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //3. 獲取處理request 的處理器適配器handler adapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                // 處理last-modified 請求頭
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                // 4.實際的處理器處理請求,返回結果視圖對象
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                // 結果視圖對象的處理
                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception ex) {
                dispatchException = ex;
            } catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        } catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        } catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    // 請求成功響應以後的方法
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

  getHandler(processedRequest)方法實際上就是從HandlerMapping 中找到url 和Controller 的對應關係。也就是Map<url,Controller>。咱們知道,最終處理Request的是Controller 中的方法,咱們如今只是知道了Controller,咱們如何確認Controller中處理Request 的方法呢?繼續往下看。從Map<urls,beanName>中取得Controller 後,通過攔截器的預處理方法,再經過反射獲取該方法上的註解和參數,解析方法和參數上的註解,而後反射調用方法獲取ModelAndView 結果視圖。最後,調用的就是RequestMappingHandlerAdapter 的handle()中的核心邏輯由handleInternal(request, response, handler)實現。

/** 根據url 獲取處理請求的方法**/
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 若是請求url 爲,http://localhost:8080/web/hello.json, 則lookupPath=web/hello.json
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        if (logger.isDebugEnabled()) {
            logger.debug("Looking up handler method for path " + lookupPath);
        }
        this.mappingRegistry.acquireReadLock();
        try {
            // 遍歷Controller 上的全部方法,獲取url 匹配的方法
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            if (logger.isDebugEnabled()) {
                if (handlerMethod != null) {
                    logger.debug("Returning handler method [" + handlerMethod + "]");
                }
                else {
                    logger.debug("Did not find handler method for [" + lookupPath + "]");
                }
            }
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }

  經過上面的代碼分析,已經能夠找處處理Request 的Controller 中的方法了,如今看如何解析該方法上的參數,並反射調用該方法。

/** 獲取處理請求的方法,執行並返回結果視圖**/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                if (logger.isDebugEnabled()) {
                    logger.debug("Found concurrent result value [" + result + "]");
                }
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }

  invocableMethod.invokeAndHandle()最終要實現的目的就是:完成Request 中的參數和方法參數上數據的綁定。Spring MVC 中提供兩種Request 參數到方法中參數的綁定方式:

  1. 經過註解進行綁定,@RequestParam。
  2. 經過參數名稱進行綁定。

  使用註解進行綁定,咱們只要在方法參數前面聲明@RequestParam("name"),就能夠將request 中參數name 的值綁定到方法的該參數上。使用參數名稱進行綁定的前提是必需要獲取方法中參數的名稱,Java 反射只提供了獲取方法的參數的類型,並無提供獲取參數名稱的方法。SpringMVC 解決這個問題的方法是用asm 框架讀取字節碼文件,來獲取方法的參數名稱。asm 框架是一個字節碼操做框架,關於asm 更多介紹能夠參考其官網。我的建議,使用註解來完成參數綁定,這樣就能夠省去asm 框架的讀取字節碼的操做。

  到這裏,方法的參數值列表也獲取到了,就能夠直接進行方法的調用了。整個請求過程當中最複雜的一步就是在這裏了。到這裏整個請求處理過程的關鍵步驟都已瞭解。理解了Spring MVC 中的請求處理流程,整個代碼仍是比較清晰的。最後咱們再來梳理一下Spring MVC 核心組件的關聯關係(以下圖):

  時序圖:

Spring MVC 使用優化建議

上面咱們已經對SpringMVC 的工做原理和源碼進行了分析,在這個過程發現了幾個優化點:

一、Controller 若是能保持單例,儘可能使用單例這樣能夠減小建立對象和回收對象的開銷。也就是說,若是Controller 的類變量和實例變量能夠以方法形參聲明的儘可能以方法的形參聲明,不要以類變量和實例變量聲明,這樣能夠避免線程安全問題。

二、處理Request 的方法中的形參務必加上@RequestParam 註解這樣能夠避免Spring MVC 使用asm 框架讀取class 文件獲取方法參數名的過程。即使Spring MVC 對讀取出的方法參數名進行了緩存,若是不要讀取class 文件固然是更好。

三、緩存URL,閱讀源碼的過程當中,咱們發現Spring MVC 並無對處理url 的方法進行緩存,也就是說每次都要根據請求url 去匹配Controller 中的方法url,若是把url 和Method 的關係緩存起來,會不會帶來性能上的提高呢?有點噁心的是,負責解析url 和Method 對應關係的ServletHandlerMethodResolver 是一個private 的內部類,不能直接繼承該類加強代碼,必需要該代碼後從新編譯。固然,若是緩存起來,必需要考慮緩存的線程安全問題。

相關文章
相關標籤/搜索