SpringMvc

springmvc運行流程

springmvc運行流程.jpg

springmvc請求流程

image.png

攔截器和過濾器的包含關系


image.png

啟動過程

  • 讀取xml文件

  • Spring容器初始化

  • DispatcherServlet初始化雕擂,添加各種handler援制、adapter等等

  • 啟動完成接收請求

  • 讀取xml文件

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  • ContextHandler 開啟初始化Context雪隧,調用ContextLoaderListener
//ContextHandler
    /* ------------------------------------------------------------ */
    protected void callContextInitialized (ServletContextListener l, ServletContextEvent e)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("contextInitialized: {}->{}",e,l);
        l.contextInitialized(e);
    }

//ContextLoaderListener
/**
     * Initialize the root web application context.
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }
//initWebApplicationContext 方法中
//執(zhí)行spring容器初始化
configureAndRefreshWebApplicationContext(cwac, servletContext); 
//bind root WebApplicationContext to on successful startup. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  • 初始化DispatcherServlet 初始化策略
    /**
     * 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);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

上面的初始化組件來自DispatcherServer.properties文件配置

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\  org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

關于ArgumentResovler

ArgumentResovler是springmvc為我們提供的一個處理controller請求參數的擴展點。有個場景是微服,我們將請求參數整理并封裝到一個POJO中驯用,省去解析請求中的參數的過程,代碼上只要設置一個POJO就可以直接接收到參數秦驯,直接使用,很方便挣棕。

<!--application.xml文件中配置一個注解-->
    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="com.xxx.converter.ParamBeanConverter" />
        </mvc:argument-resolvers>
    </mvc:annotation-driven>
編寫一個實現(xiàn) HandlerMethodArgumentResolver 接口的類译隘;

public abstract class AbstractConverter<T> implements HandlerMethodArgumentResolver {

    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {
        Object result = onResolve(parameter, webRequest, mavContainer, binderFactory);
       
        return result;
    }

    protected abstract Object onResolve(MethodParameter parameter,
                                        NativeWebRequest webRequest,
                                        ModelAndViewContainer mavContainer,
                                        WebDataBinderFactory binderFactory) throws Exception;
}

public class ParamBeanConverter extends AbstractConverter<JSONObject> {
    @Override
    protected JSONObject onResolve(MethodParameter parameter, NativeWebRequest webRequest, ModelAndViewContainer mavContainer, WebDataBinderFactory binderFactory) throws Exception {
        mavContainer.setRequestHandled(true);
        JSONObject json = new JSONObject();
                //獲取請求中的參數 然后設置大json中,轉成json
        for (Entry<String, String[]> e : webRequest.getParameterMap().entrySet()) {
            try
            {
                json.put(e.getKey(), URLDecoder.decode(e.getValue()[0], "utf-8"));
            }
            catch(IllegalArgumentException ie)
            {
                
            }
        }
        return json;
    }
}

通過以上配置穴张,就可以實現(xiàn)對Controller中的入參進行轉換细燎;

原理

springmvc jar包里的 spring.handlers文件,配置了mvc命名空間的解析配置類

http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler

/**
 * {@link NamespaceHandler} for Spring MVC configuration namespace.
 *
 * @author Keith Donald
 * @author Jeremy Grelle
 * @since 3.0
 */
public class MvcNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
        registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
        registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
        registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
    }

}

//將annotation-driven下配置的自定義類注冊到上下文類定義中
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);

if (argumentResolvers != null) {
            handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
        }
        
        String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);

初始化DispatcherServlet 時將適配器加載初始化

    /**
     * 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);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

在DispatcherServlet的doDispatch方法中:

/**
     * Process the actual dispatching to the handler.
     * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
     * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
     * to find the first that supports the handler class.
     * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
     * themselves to decide which methods are acceptable.
     * @param request current HTTP request
     * @param response current HTTP response
     * @throws Exception in case of any kind of processing failure
     */
    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 {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
//獲取當前mapping適用的handler
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
//獲取handler中的適配器
                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

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

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

                try {
//執(zhí)行適配器
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                }
                finally {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                }

                applyDefaultViewName(request, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                return;
            }
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }

getMethodArgumentValues 或得轉換后的參數,然后invoke 方法將參數傳遞給對應的controller method

public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
 
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            StringBuilder sb = new StringBuilder("Invoking [");
            sb.append(getBeanType().getSimpleName()).append(".");
            sb.append(getMethod().getName()).append("] method with arguments ");
            sb.append(Arrays.asList(args));
            logger.trace(sb.toString());
        }
        Object returnValue = invoke(args);
        if (logger.isTraceEnabled()) {
            logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
        }
        return returnValue;
    }
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末皂甘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子悼凑,更是在濱河造成了極大的恐慌偿枕,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件户辫,死亡現(xiàn)場離奇詭異渐夸,居然都是意外死亡,警方通過查閱死者的電腦和手機渔欢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門墓塌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奥额,你說我怎么就攤上這事苫幢。” “怎么了垫挨?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵韩肝,是天一觀的道長。 經常有香客問我九榔,道長哀峻,這世上最難降的妖魔是什么涡相? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮剩蟀,結果婚禮上催蝗,老公的妹妹穿的比我還像新娘。我一直安慰自己育特,他們只是感情好丙号,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著且预,像睡著了一般槽袄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锋谐,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天遍尺,我揣著相機與錄音,去河邊找鬼涮拗。 笑死乾戏,一個胖子當著我的面吹牛,可吹牛的內容都是我干的三热。 我是一名探鬼主播鼓择,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼就漾!你這毒婦竟也來了呐能?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤抑堡,失蹤者是張志新(化名)和其女友劉穎摆出,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體首妖,經...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡偎漫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了有缆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片象踊。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖棚壁,靈堂內的尸體忽然破棺而出杯矩,到底是詐尸還是另有隱情,我是刑警寧澤灌曙,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布菊碟,位于F島的核電站,受9級特大地震影響在刺,放射性物質發(fā)生泄漏逆害。R本人自食惡果不足惜头镊,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魄幕。 院中可真熱鬧相艇,春花似錦、人聲如沸纯陨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翼抠。三九已至咙轩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阴颖,已是汗流浹背活喊。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留量愧,地道東北人钾菊。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像偎肃,于是被迫代替她去往敵國和親煞烫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內容