Spring(五):SpringMVC执行流程及源码解析

在SpringMVC中主要是围绕着DispatcherServlet来设计,可以把它当做指挥中心。这里先说明一下SpringMVC文档给出的执行流程,然后是我们稍微具体的执行流程,最后是流程大致的源码跟踪。关于很很很详细的源码解析,这里暂先不做。

spring请求移动:

 

  1. 用户发送请求到DispatcherServlet(前端控制器)
  2. DispatcherServlet查询一个或多个处理器映射handlerMapping(根据xml配置、注解进行查找)
  3. 处理器映射handlerMapping向前端控制器返回handler,处理器映射会把请求映射成HandlerExecutionChain(包含一个Handler处理器对象,多个HandlerInterceptor拦截器对象)
  4. 前端控制器调用处理器适配器去执行Handler
  5. 处理器适配器HandlerAdapter将会根据适配结果去执行Handler
  6. Handler对数据处理完成后返回一个ModelAndView对象给前端控制器
  7. 前端控制器通过ViewResolver视图解析器将ModelAndView转化成真正的View视图
  8. 前端控制器将View渲染后返回给用户

SpringMVC的使用

依赖: <dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${spring.version}</version>

</dependency>

SpringBoot依赖:

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.0.0.RELEASE</version>

</parent>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

 

XML使用

Web.xml

<!--configure the setting of springmvcDispatcherServlet and configure the mapping-->
  <servlet>
      <servlet-name>springmvc</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!-- <load-on-startup>1</load-on-startup> -->
  </servlet>

  <servlet-mapping>
      <servlet-name>springmvc</servlet-name>
      <url-pattern>/</url-pattern>
  </servlet-mapping>
springmvc-servlet.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">                    

    <!-- scan the package and the sub package -->
    <context:component-scan base-package="test.SpringMVC"/>

    <!-- don't handle the static resource -->
    <mvc:default-servlet-handler />

    <!-- if you use annotation you must configure following setting -->
    <mvc:annotation-driven />
    
    <!-- configure the InternalResourceViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
            id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>


Controller返回文字@Controller文字   @RestController返回json
@Controller
@RequestMapping("/mvc")
public class mvcController {

    @RequestMapping("/hello")
    public String hello(){        
        return "hello";
    }
}

启动服务器,键入 http://localhost:8080/项目名/mvc/hello  
Controller返回页面
@Controller
@RequestMapping("/mvc")
public class mvcController {

    @RequestMapping("/hello")
    public String hello(){        
        return  new ModelAndView("hello");;
    }
}

键入 http://localhost:8080/项目名/mvc/hello -- >定位jsp文件到/WEB-INF/jsp/index.jsp

 

 

注解使用

@EnableWebMvc

前缀后缀

spring.mvc.view.prefix=/templates/

spring.mvc.view.suffix=.ftl

 

@RestController("/")

public class IndexController {

@RequestMapping("index")

public String index(){

return "Welcome to know Spring Boot !";

}

}

 

源码

ContextLoaderListener初始化,实例化IoC容器,并将此容器实例注册到ServletContext中。

DispatcherServlet初始化,建立自己的上下文,也注册到ServletContext中。

 

DispatcherServlet类

可以看到,DispatchServlet继承子FrameworkServlet,而FrameworkServlet继承子HttpServletBean;

 

1.HttpServletBean初始化

获取web.xml的参数,构造PropertyValues对象;这里的psv值通过debug可以观察到实际是servlet的名称和具体的配置路径;

之后设置DispatcherServlet的属性;其实这段代码的目的就是获取servlet的context参数,设置为DispatcherServlet的contextConfigLocation参数,构造spring mvc的容器上下文;最后initServletBean();该方法是占位方法,子类可以重写该方法,来做更多的事情;

2.FrameworkServlet初识化

首先判断WebApplicationContext是否为空,因为DispatcherServlet有一个构造函数webApplicationContext

所以要先进行判断,当使用该构造函数来生成DispatcherServlet的时候执行这段逻辑;

来创建上下文,具体的代码如下

设置跟上下文为父上下文,然后配置ServletConfig,ServletContext等实例到这个上下文中;

最后onRefresh(wac);模板方法,子类DispatcherServlet会覆盖这个方法;

之后将创建的容器上下文设置到ServletContext中;

3.DispatcherServlet初始化

DispatcherServlet覆盖了父类FrameworkServlet中的方法,主要初始化各种策略接口的实现类,比如常用的异常处理初始化initHandlerExceptionResolvers,视图处理初始化initViewResolvers,请求映射初始化方法initHandlerMappings;

 

总结:1.HttpServletBean 主要做一些初始化的事情,将web.xml中的配置参数设置到servlet中。比如 servlet标签的子标签init-param标签中的参数;

2.FrameworkServlet 将Servlet与Spring容器上下文关联;也就是初始化FarmeworkServlet属性webApplicationContext,这个属性代表springmvc上下文,它有个父类上下文;

3.DispatcherServlet 初始化各个功能的实现,比如异常处理,视图处理,请求映射等功能;

 

4.DispatcherServlet对http请求的处理过程

首先了解下HttpServlet对于http请求的处理过程

所有的请求都会调用HttpServlet的service方法

else if (method.equals(METHOD_POST)) {

doPost(req, resp);

} else if (method.equals(METHOD_PUT)) {

doPut(req, resp);

} else if (method.equals(METHOD_DELETE)) {

doDelete(req, resp);

} else if (method.equals(METHOD_OPTIONS)) {

doOptions(req,resp);

} else if (method.equals(METHOD_TRACE)) {

doTrace(req,resp);

}

FrameworkServlet子类继承子HttpServlet,并重写了上述方法

@Override

protected final void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

processRequest(request, response);

}

 

 在processRequest中会调用doService方法,这个doService方法就是在DispatcherServlet中实现的。下面就看下DispatcherServlet中的doService方法的实现。

请求到达DispatcherServlet doService方法:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
 
    //给request中的属性做一份快照
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        logger.debug("Taking snapshot of request attributes before include");
        attributesSnapshot = new HashMap<String, Object>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }
 
    //如果我们没有配置类似本地化或者主题的处理器之类的
    //SpringMVC会使用默认的值
    //默认配置文件是DispatcherServlet.properties
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
 
    FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
    if (inputFlashMap != null) {
        request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
    }
    request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
    request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
 
    try {
    	//开始处理
        doDispatch(request, response);
    }
    finally {
        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            return;
        }
        // Restore the original attribute snapshot, in case of an include.
        if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
        }
    }
}

DispatcherServlet开始真正的处理, doDispatch方法 :

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
	//SpringMVC中异步请求的相关知识,暂先不解释
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
    try {
        ModelAndView mv = null;
        Exception dispatchException = null;
 
        try {
        	//先检查是不是Multipart类型的,比如上传等
            //如果是Multipart类型的,则转换为MultipartHttpServletRequest类型
            processedRequest = checkMultipart(request);
            multipartRequestParsed = processedRequest != request;
 
            //获取当前请求的Handler
            mappedHandler = getHandler(processedRequest, false);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
 
            //获取当前请求的Handler适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
            // 对于header中last-modified的处理
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
			//拦截器的preHandle方法进行处理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
 
            try {
                //真正调用Handler的地方
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
            }
			//处理成默认视图名,就是添加前缀和后缀等
            applyDefaultViewName(request, mv);
            //拦截器postHandle方法进行处理
            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);
        }
    }
}

查找请求对应的Handler对象

 

对应着这句代码mappedHandler = getHandler(processedRequest, false);,看下具体的getHandler方法:

 

protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
    return getHandler(request);
}
继续往下看getHandler:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//遍历所有的handlerMappings进行处理
    //handlerMappings是在启动的时候预先注册好的
    for (HandlerMapping hm : this.handlerMappings) {
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}
继续往下看getHandler,在AbstractHandlerMapping类中:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//根据request获取handler
    Object handler = getHandlerInternal(request);
    if (handler == null) {
    	//如果没有找到就使用默认的handler
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    //如果Handler是String,表明是一个bean名称
    //需要超照对应bean
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }
    //封装Handler执行链
    return getHandlerExecutionChain(handler, request);
}

 

根据requrst获取handler

首先看下根据requrst获取handler步骤getHandlerInternal方法,在AbstractHandlerMethodMapping中:

 

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //获取request中的url,用来匹配handler
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    //根据路径寻找Handler
    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    //根据handlerMethod中的bean来实例化Handler并添加进HandlerMethod
    return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
}
看下根据路径寻找handler的方法lookupHandlerMethod:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
	//直接匹配
    List<T> directPathMatches = this.urlMap.get(lookupPath);
    //如果有匹配的,就添加进匹配列表中
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
	//还没有匹配的,就遍历所有的处理方法查找
    if (matches.isEmpty()) {
        // No choice but to go through all mappings
        addMatchingMappings(this.handlerMethods.keySet(), matches, request);
    }
	//找到了匹配的
    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        Collections.sort(matches, comparator);
		//排序之后,获取第一个
        Match bestMatch = matches.get(0);
        //如果有多个匹配的,会找到第二个最合适的进行比较一下
        if (matches.size() > 1) {
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
                        m1 + ", " + m2 + "}");
            }
        }
		//设置request参数
        handleMatch(bestMatch.mapping, lookupPath, request);
        //返回匹配的url的处理的方法
        return bestMatch.handlerMethod;
    }
    else {//最后还没有找到,返回null
        return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
    }
}

获取默认Handler

如果上面没有获取到Handler,就会获取默认的Handler。如果还获取不到就返回null。

处理String类型的Handler

如果上面处理完的Handler是String类型的,就会根据这个handlerName获取bean。

封装Handler执行链

上面获取完Handler,就开始封装执行链了,就是将我们配置的拦截器加入到执行链中去,getHandlerExecutionChain:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	//如果当前Handler不是执行链类型,就使用一个新的执行链实例封装起来
    HandlerExecutionChain chain =
        (handler instanceof HandlerExecutionChain) ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
	//先获取适配类型的拦截器添加进去拦截器链
    chain.addInterceptors(getAdaptedInterceptors());
	//当前的url
    String lookupPath = urlPathHelper.getLookupPathForRequest(request);
    //遍历拦截器,找到跟当前url对应的,添加进执行链中去
    for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
        if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
        }
    }
 
    return chain;
}

获取对应请求的Handler适配器getHandlerAdapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	//遍历所有的HandlerAdapter,找到和当前Handler匹配的就返回
    //我们这里会匹配到RequestMappingHandlerAdapter
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha;
        }
    }
}

缓存的处理

也就是对last-modified的处理

执行拦截器的preHandle方法

就是遍历所有的我们定义的interceptor,执行preHandle方法

使用Handler适配器执行当前的Handler

ha.handle执行当前Handler,我们这里使用的是RequestMappingHandlerAdapter,首先会进入AbstractHandlerMethodAdapter的handle方法:

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
}
handleInternal方法,在RequestMappingHandlerAdapter中:
protected final ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
 
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
        // Always prevent caching in case of session attribute management.
        checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    }
    else {
        // Uses configured default cacheSeconds setting.
        checkAndPrepare(request, response, true);
    }
 
    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                return invokeHandleMethod(request, response, handlerMethod);
            }
        }
    }
	//执行方法,封装ModelAndView
    return invokeHandleMethod(request, response, handlerMethod);
}

组装默认视图名称

前缀和后缀名都加上

执行拦截器的postHandle方法

遍历intercepter的postHandle方法。

处理最后的结果,渲染

processDispatchResult方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
 
    boolean errorView = false;
 
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }
 
    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
    	//渲染
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
    }
 
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }
 
    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}
重点看下render方法,进行渲染:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    //设置本地化
    Locale locale = this.localeResolver.resolveLocale(request);
    response.setLocale(locale);
 
    View view;
    if (mv.isReference()) {
        //解析视图名,得到视图
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
    }
    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() + "'");
        }
    }
 
    //委托给视图进行渲染
    view.render(mv.getModelInternal(), request, response);
}
 
view.render就是进行视图的渲染,然后跳转页面等处理。

 

本篇文章为总结而来,如有错误敬请指出

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值