SpringMVC工作流程

SpringMVC工作流程

博客地址:SpringMVC工作流程


SpringMVC的核心在于它的流程,所以了解SpringMVC的工作流程对于我们使用SpringMVC非常重要,SpringMVC是一种基于Servlet的技术,它提供了核心控制器DispatcherServlet和相关组件。其流程图:

1.首先用户发送一个请求到服务器,在使用springMVC之前,我们应该在web.xml对DispatcherServlet进行配置过了:

 <!--使用Spring MVC,配置DispatcherServlet是第一步。DispatcherServlet是一个Servlet,,所以可以配置多个DispatcherServlet-->  
    <servlet>  
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <!--指明了配置文件的文件名,不使用默认配置文件名,而使用dispatcher-servlet.xml配置文件。-->  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>classpath:spring/dispatcher-servlet.xml</param-value>  
        </init-param>  
        <!--是启动顺序,让这个Servlet随Servletp容器一起启动。--> 
        <load-on-startup>1</load-on-startup> 
    </servlet>  
    <servlet-mapping>   
        <servlet-name>DispatcherServlet</servlet-name>  
		<!--会拦截URL中带“/”的请求。-->  
        <url-pattern>/</url-pattern> 
    </servlet-mapping>  
复制代码

如果使用springBoot,那么springBoot会帮我们注册一个默认的DispatcherServlet,而如果该请求与配置中的url-pattern匹配的话,该请求就会交由该DispatcherServlet来进行处理 2.DispatcherServlet根据url,遍历HandlerMappings,获得由处理器和拦截器组成的执行链返回给DispatcherServlet, 其源码如下,遍历HandlerMappings(初始化时获得),如果当中有一个HandlerMapping调用getHandler方法时返回值不为null,那么getHandler就会返回一个由拦截器和处理器组成的HandlerExecutionChain执行链。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        if (logger.isTraceEnabled()) {
            logger.trace(
                    "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}
复制代码

3.将获得的执行链返回给DispatcherServlet 4.根据获得的Handler调用HandlerAdapter,其实现也是类似,遍历HandlerAdapters,如果类型匹配则返回该HandlerAdapter,源码如下:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha;
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
复制代码

注意:此时会依次执行拦截器的preHandle方法,如果遇到一个拦截器return false就会停止 5.提取request中的数据模型,进行数据转换,数据格式化,数据验证等操作,然后执行Handler,调用具体的方法,其实现如下,其中handleInternal由具体的Adapter,比如@RequestMapping注解的handler会由RequestMappingHandlerAdapter来处理。这里就不继续分析源码了,想看源码详细分析的话这个大兄弟写得很好SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {

    private int order = Ordered.LOWEST_PRECEDENCE;
    @Override
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
        return handleInternal(request, response, (HandlerMethod) handler);
    }
        //抽象方法,由具体的Adapter实现
        protected abstract ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

}
复制代码

6.handleInternal的具体实现会执行一个invokeHandlerMethod ,执行后会返回一个ModelAndView视图,源码:

 protected ModelAndView invokeHandlerMethod(HttpServletRequest request,  
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {  
    .........  
    //执行Controller中的RequestMapping注释的方法  
    invocableMethod.invokeAndHandle(webRequest, mavContainer);  
      
    //返回ModelAndView视图  
    return getModelAndView(mavContainer, modelFactory, webRequest);  
    }  
复制代码

7.HandleAdapter返回ModelAndView给DispatcherServlet(注意:在下一步渲染视图之前会执行postHandle方法) 8~9.解析视图和渲染视图都是在render方法里面执行的, render方法如下:

    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Determine locale for request and apply it to the response.
    Locale locale = this.localeResolver.resolveLocale(request);
    response.setLocale(locale);
 
    View view;
    if (mv.isReference()) {
        // We need to resolve the view name.
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
        if (view == null) {
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                    "' in servlet with name '" + getServletName() + "'");
        }
    }
    else {
        // No need to lookup: the ModelAndView object contains the actual View object.
        view = mv.getView();
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                    "View object in servlet with name '" + getServletName() + "'");
        }
    }
 
    // Delegate to the View object for rendering.
    if (logger.isDebugEnabled()) {
        logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
    }
    try {
        view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                    getServletName() + "'", ex);
        }
        throw ex;
    }
}
复制代码

其中,

view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
复制代码

这个方法会遍历DispatcherServlet中的ViewResolver集合并返回对应的视图,对应流程图中的第八步和第九步 10.上面的render方法中的:

view.render(mv.getModelInternal(), request, response);
复制代码

则是渲染视图的方法(渲染视图后会执行拦截器的afterCompletion方法),其实现类源码如下:

public abstract class 

    AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {
        ... ...
        @Override
        public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
                HttpServletResponse response) throws Exception {
    
            if (logger.isTraceEnabled()) {
                logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
                    " and static attributes " + this.staticAttributes);
            }
            
            // 创建整合后需要返回给浏览器的Model
            Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
            // 设置response 报文头
            prepareResponse(request, response);
            // 渲染数据,通过模板方法由子类实现,如InternalResourceView
            renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
        }
        ... ...
    }
复制代码

11.上面view.render方法的最后一步:

 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
复制代码

其源码如下:

@Override
protected void renderMergedOutputModel(
        Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

    // 将model中的数据设置到request
    exposeModelAsRequestAttributes(model, request);

    // 本类中的此函数是空函数,留给子类比如JstlView去实现自定义逻辑
    exposeHelpers(request);

    // 跳转目的页面路径
    String dispatcherPath = prepareForRendering(request, response);

    // 获取跳转控制器RequestDispatcher  
    RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
    if (rd == null) {
        throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
                "]: Check that the corresponding file exists within your web application archive!");
    }

    // 直接返回用户资源 
    if (useInclude(request, response)) {
        response.setContentType(getContentType());
        if (logger.isDebugEnabled()) {
            logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
        }
        rd.include(request, response);
    }
    // 携带request和response跳转到另一个控制器方法
    else {
        // Note: The forwarded resource is supposed to determine the content type itself.
        if (logger.isDebugEnabled()) {
            logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
        }
        rd.forward(request, response);
    }
}
复制代码

可以看到,这个方法完成了最后把视图封装好和把结果返回给用户的操作

参考:DispatcherServlet来看spring mvc请求处理过程 SpringMVC之源码分析--View(一)

转载于:https://juejin.im/post/5c88ac835188257e3e4800bb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值