springMvc执行流程

springMvc概念
springMvc请求流程
springMvc组件详解
springMvc常用注解
springMvc拦截器
springMvc异常处理


一、springMvc概念

springMvc是基于servlet的web框架,其简化了web程序的开发

二、springMvc请求流程

在阅读时不妨可以带着几个问题阅读:
1.我们通常在浏览器输入的接口怎么由DispatcherServlet调到具体的Handler的(就是我们自己开发的Controller类)
2.编写Controller的形式有哪几种?

springMvc重要组件:
之所以先说这几个组件,是因为只要了解了这几个组件后你便可以对springMvc请求流程有个大致的清晰认识了。(Handler就是我们写的Controller)
      HandlerMapping
      HandlerAdapter
      ViewResolver
      View
      HandlerExceptionResolver
      HandlerInterceptor
DisPatcher作为一个主流程入口,看一下DispatcherServlet结构

DispatcherServlet为了简洁,省去了很多方法跟属性

public class DispatcherServlet extends FrameworkServlet {
//定义了默认策略名字
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
private static final Properties defaultStrategies;
static {
            //从Properties文件中加载默认策略实现
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
private List<HandlerMapping> handlerMappings;
private List<HandlerAdapter> handlerAdapters;
private List<HandlerExceptionResolver> handlerExceptionResolvers;
private List<ViewResolver> viewResolvers;
//初始化策略
protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                                //检查是否文件上传
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                //通过HandlerMapping 获取Handler(返回的是HandlerExecutionChain)
               /**public class HandlerExecutionChain {
                       //HandlerExecutionChain封装了Handler(就是我们编写的Controller)跟interceptors
                       private final Object handler;
                       private HandlerInterceptor[] interceptors;
                **/
                HandlerExecutionChain mappedHandler = getHandler(processedRequest);
                //获取的Handler是Object类型,需要通过HandlerAdapter获取Handler真实类型
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                //调用Handlerinterceptor PreHandle(前置拦截器),返回false则停止执行
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 调用真实handler逻辑并返回ModelAndView
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                                //将ModelAndView返回值DispatcherServlet后
                applyDefaultViewName(processedRequest, mv);
                                //调用Handlerinterceptor PostHandle(后置拦截器)    
                    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) {
                                      //调用Handlerinterceptor afterCompletion
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }}

通过上面的DispatcherServlet大概分析可以得知几个组件的作用:
HandlerMapping:
根据请求url地址得到具体Handler(Controller)
HandlerAdapter
根据Handler得到Handler适配器
ViewResolver
试图仓库:根据ViewName得到View
View
具体解析视图
HandlerExceptionResolver
异常捕捕捉器
HandlerInterceptor
拦截器

三、springMvc处理流程图解:(引用他人流程图)

springMvc处理

简略图:


springMvc处理

四、各组件详解:

1.HandlerMapping
HandlerMapping方法:

HandlerMapping方法

HandlerMapping子类:
HandlerMapping

从上图看一看出常用的Mapping有三类:SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping、RequestMappingHandlerMapping。

RequestMappingHandlerMapping就是我们最长使用的方式基于注解@RequestMapping方式的url映射。
SimpleUrlHandlerMapping 基于手动配置 url 与control 映射

    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello">helloController</prop>
            </props>
        </property>
    </bean>
    <bean id="helloController" class="com.ckd.controller.HelloController"/>
-----------------------------------------------------------------------------------------------------------------------
import org.springframework.web.servlet.mvc.Controller;
public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView("index");
        mv.addObject("hello","world");
        return mv;
    }
}

BeanNameUrlHandlerMapping是配置IOC bean时id以"/"开头与Controller的映射

<bean id="/beanname" class="com.ckd.controller.BeanNameController"/>
//需要继承HttpRequestHandler 此时可以把/beanname映射到handleRequest方法上
public class BeanNameControl implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        request.getRequestDispatcher("/WEB-INF/page/userView.jsp").forward(request, response);
    }
}

通过DisPatcherServlet.getHandler()获得Handler的类型时Object,此时无法调用,因此需要HandlerAdapter根据Handler获取具体的适配器
2.HandlerAdapter
springmvc采用适配器的模式适配调用Handler,根据handler不同调用不同的适配器,Handler与HandlerAdapter对用关系如下:

Handler类别 对应适配器 描述
Controller SimpleControllerHandlerAdapter 标准控制器,返回ModelAndView
HttpRequestHandler HttpRequestHandlerAdapter 业务自行处理 请求,不需要通过modelAndView 转到视图
Servlet SimpleServletHandlerAdapter 基于标准的servlet 处理
HandlerMethod RequestMappingHandlerAdapter 基于@requestMapping对应方法处理
image.png

image.png

基于Servlet演示

<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
<bean id="/helloservlet" class="com.ckd.controller.HelloServlet"/>
----------------------------------------------------------------------------------------------------------------------------
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("hello Servlet");
    }
}

HelloServlet注册IOC容器中时,会由BeanNameUrlHandlerMapping找到对应的Handler,由SimpleServletHandlerAdapter找到对应的适配器,调用业务逻辑Controller,并返回ModelAndView。
3.ViewResolver View
DispatcherServlet#getHandlerAdapter().handle()后返回ModelAndView,
调用resolveViewName() 去viewResolvers试图列表中查找对应的View并返回View.交由View解析生成html并返回

ViewResolver

ViewResolver

View

4.HandlerExceptionResolver
该组件用于指示 当出现异常时 mvc 该如何处理。 dispatcherServlet 会调用org.springframework.web.servlet.DispatcherServlet#processHandlerException() 方法,遍历 handlerExceptionResolvers 处理异常,处理完成之后返回errorView 跳转到异常视图。
- [ ] 演示自定义异常捕捉

<bean class="com.ckd.controller.ExceptionHandlerSample"/>
public class ExceptionHandlerSample implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return new ModelAndView("error");
    }
}

image.png

5.HandlerInterceptor

  • [ ] 演示HandlerInterceptor
<mvc:interceptors>
        <mvc:interceptor>
            <!-- 拦截所有的请求,这个必须写在前面-->
            <mvc:mapping path="/**" />            
            <bean class="com.ckd.controller.SimpleHandlerInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>
-----------------------------------------------------------------------------------------------------------------------------------------
public class SimpleHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}

其实现机制是基于 HandlerExecutionChain 分别在 doDispatch 方法中执行以下方法:

  • preHandle :业务处理前执行
  • postHandle:业务处理后(异常则不执行)
  • afterCompletion:视图处理后

具体逻辑源码参见:org.springframework.web.servlet.DispatcherServlet#doDispatch 方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,135评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,317评论 3 397
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,596评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,481评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,492评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,153评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,737评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,657评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,193评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,276评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,420评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,093评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,783评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,262评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,787评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,427评论 2 359

推荐阅读更多精彩内容