3、DispatcherServlet分发器

一、作用

DispatcherServlet是前端控制器设计模式的实现,提供spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处 (设计模式一套被反复使用,代码设计方面的经验总结.使用设计模式的主要目的是为了能攻更好的获得可重用行,这包括体系结构和代码实现等方面,并能够让开发者开发出来的软件更容易被他人理解,同事保证系统结构的正确性和代码的可靠性)

二、 什么前端控制器设计模式

前端控制器模式(Front Controller Pattern)通俗来讲是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序比如可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。

  • 你想要避免重复控制逻辑
  • 你想要对多个请求采取共同的处理逻辑。
  • 你想把系统处理代码和视图分隔开(不要在视图中写控制代码)
  • 你想要把系统的访问点集中在一点
    前端控制器为处理请求提供了一个集中的入口点。因此能够集中控制逻辑,因此就减少了直接置入视图的代码量。

1、前端控制器模式

  1. 前端控制器(Front Controller) - 处理应用程序所有类型请求的单个处理程序,应用程序可以是基于 web 的应用程序,也可以是基于桌面的应用程序。
  2. 调度器(Dispatcher)- 前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。
  3. 视图(View)视图是为请求而创建的对象。

2、 操作步骤

  1. 前端控制器(Front Controller) - 处理应用程序所有类型请求的单个处理程序,应用程序可以是基于 web 的应用程序,也可以是基于桌面的应用程序。
  2. 调度器(Dispatcher) - 前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。
  3. 视图(View) - 视图是为请求而创建的对象。

3、栗子

  1. 创建视图对象
    public class IndexView {
        public void show() {
            System.out.println("欢迎进入首页");
        }
    }
    public class IndexView {
        public void show() {
            System.out.println("游客界面");
        }
    }
    
  2. 创建Dispatcher
    public class Dispatcher {
       private IndexView indexView;
       private GuestView guestView;
       public Dispatcher(){
          indexView = new IndexView();
          guestView = new GuestView();
       }
       public void dispatch(String request){
          if(request.equalsIgnoreCase("login")){
             indexView.show();
          }else{
             guestView.show();
          }
       }
    }
    
  3. 创建FrontController
    public class FrontController {
        private Dispatcher dispatcher;
        public FrontController(){
            dispatcher = new Dispatcher();
        }
        private boolean isLoginUser(){
            System.out.println();
            return true;
        }
        private void trackRequest(String request){
            System.out.println("页面: " + request);
        }
        public void dispatchRequest(String request){
            //记录请求
            trackRequest(request);
            if(isLoginUser()){
                dispatcher.dispatch(request);
            }
        }
    }
    
  4. 测试代码
      public static void main(String[] args) {
            FrontController controller = new FrontController();
            controller.dispatchRequest("1111");
        }
    

4、结构图

image

三、继承结构图

image

四、DispatcherServlet原理

1、说明

前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)

2、MVC结构

  1. M-Model 模型(完成业务逻辑:有javaBean构成,service+dao+entity)
  2. V-View 视图(做界面的展示 jsp,html)
  3. C-Controller 控制器(接收请求—>调用模型—>根据结果派发页面)

3、DIspatcher执行流程图

执行原理图
image
步骤说明

1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
2、 DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
5、 ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
6、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户

核心代码
//前端控制器分派方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        int interceptorIndex = -1;
        try {
            ModelAndView mv;
            boolean errorView = false;
            try {
                   //检查是否是请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析
                processedRequest = checkMultipart(request);
                   //步骤2、请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射
                mappedHandler = getHandler(processedRequest, false);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                   //步骤3、处理器适配,即将我们的处理器包装成相应的适配器(从而支持多种类型的处理器)
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                  // 304 Not Modified缓存支持
                //此处省略具体代码
                // 执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)
                //此处省略具体代码
                // 步骤4、由适配器执行处理器(调用处理器相应功能处理方法)
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                // Do we need view name translation?
                if (mv != null && !mv.hasView()) {
                    mv.setViewName(getDefaultViewName(request));
                }
                // 执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)
                //此处省略具体代码
            }
            catch (ModelAndViewDefiningException ex) {
                logger.debug("ModelAndViewDefiningException encountered", ex);
                mv = ex.getModelAndView();
            }
            catch (Exception ex) {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(processedRequest, response, handler, ex);
                errorView = (mv != null);
            }
            //步骤5 步骤6、解析视图并进行视图的渲染
            //步骤5 由ViewResolver解析View(viewResolver.resolveViewName(viewName, locale))
            //步骤6 视图在渲染时会把Model传入(view.render(mv.getModelInternal(), request, response);)
            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                            "': assuming HandlerAdapter completed request handling");
                }
            }
            // 执行处理器相关的拦截器的完成后处理(HandlerInterceptor.afterCompletion)
            //此处省略具体代码
        catch (Exception ex) {
            // Trigger after-completion for thrown exception.
            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
            throw ex;
        }
        catch (Error err) {
            ServletException ex = new NestedServletException("Handler processing failed", err);
            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
            throw ex;
        }
        finally {
            if (processedRequest != request) {
                cleanupMultipart(processedRequest);
            }
        }
    }

五、DispatcherServlet辅助类

1、说明

SpringMVC中的DispatcherServlet使用一些特殊的bean来处理request请求和渲染合适的视图。这些bean就是Spring MVC中的一部分。你能够通过在WebApplicationContext中的一个或多个配置来使用这些特殊的bean。但是,你不需要在Spring MVC在维护这些默认要使用的bean时,去把那些没有配置过的bean都去初始化一道。在下一部分中,首先让我们看看在DispatcherServlet依赖的那些特殊bean类型

2、属性

bean类型 说明
Controller 处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理
HandlerMapping 请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象;如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器
HandlerAdapter HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;如SimpleControllerHandlerAdapter将对实现了Controller接口的Bean进行适配,并且掉处理器的handleRequest方法进行功能处理
HandlerExceptionResolver处理器异常解析器 处理器异常解析,可以将异常映射到相应的统一错误界面,从而显示用户友好的界面(而不是给用户看到具体的错误信息)
ViewResolver视图解析器 ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;如InternalResourceViewResolver将逻辑视图名映射为jsp视图
LocaleResolver & LocaleContextResolver地区解析器和地区Context解析器 解析客户端中使用的地区和时区,用来提供不同的国际化的view视图。
ThemeResolver 主题解析器,解析web应用中能够使用的主题,比如提供个性化的网页布局。
MultipartResolver 多部件解析器,主要处理multi-part(多部件)request请求,例如:在HTML表格中处理文件上传。
FlashMapManager FlashMap管理器储存并检索在"input"和"output"的FlashMap中可以在request请求间(通常是通过重定向)传递属性的FlashMap,

spring-mvc专题

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

推荐阅读更多精彩内容