zoukankan      html  css  js  c++  java
  • spring MVC 基础

    组件和概念

    处理器:Handler

    1,对应 MVC 三层中的 C,也就是控制器,找不到 handler 抛出 404
    2,具体表现形式很多,使用 @RequestMapping 注解是做常用的,还有实现 org.springframework.web.servlet.mvc.Controller 接口;实现 org.springframework.web.HttpRequestHandler 接口等
    3,只要能实际处理请求的就可以是 handler,正因为实现 handler 的方式不同,所以需要合适的适配器去执行 handler
    

    处理器执行链:HandlerExecutionChain

    1,处理器 + 拦截器列表(有顺序的,顺序执行,包括自定义的)
    2,其实不难理解,比如项目中配置了拦截器,执行请求时也会执行拦截器,所以当请求过来找到了处理器并不是直接九执行,还要找到 处理器执行链 再执行
    

    前端控制器:DispatcherServlet

    1,整个 springmvc 的核心,请求的响应和处理都是它完成的
    2,继承关系:DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet
    3,实际上就是一个 Servlet,所以 DispatcherServlet 初始化的时候会执行 HttpServletBean 的 init() 方法。这是 DispatcherServlet 初始化的入口
    

    DispatcherServlet 九大组件

    处理器映射器:HandlerMapping

    1,DispatcherServlet 初始化时完成配置,作用是然后根据 request 找到 HandlerExecutionChain
    2,HandlerMapping 是一个接口,很多子接口和实现类。其中获取 handler 的方式是模板方式,由子类完成查找 handler(源码是遍历所有子类去查找)
    3,RequestMappingHandlerMapping:存储 @RequestMapping 实现的 handler
    4,BeanNameUrlHandlerMapping:存储 Controller 类实现 Controller 或 HttpRequestHandler 接口实现的 handler
    

    处理器适配器:HandlerAdapter

    1,通过处理器映射器找到了 HandlerExecutionChain,其实也就找到了 Handler
    2,Handler 的实现方式是多种,所以要找到合适当前 handler 的执行工具,这个工具就是 HandlerAdapter
    3,RequestMappingHandlerAdapter 执行 @RequestMapping 注解实现的 handler
    

    处理器异常解析器:HandlerExceptionResolver

    1,当 handler 执行出现了异常时,DispatcherServlet 会调用 HandlerExceptionResolver 来处理。具体是由 DefaultHandlerExceptionResolver 处理,全局异常见下表格
    2,自定义全局异常有两种方式:@ControllerAdvice 结合 @ExceptionHandler;继承 HandlerExceptionResolver 并重写 resolveException 方法
    
    Exception HTTP Status Code
    HttpRequestMethodNotSupportedException 405 (SC_METHOD_NOT_ALLOWED)
    HttpMediaTypeNotSupportedException 415 (SC_UNSUPPORTED_MEDIA_TYPE)
    HttpMediaTypeNotAcceptableException 406 (SC_NOT_ACCEPTABLE)
    MissingPathVariableException 500 (SC_INTERNAL_SERVER_ERROR)
    MissingServletRequestParameterException 400 (SC_BAD_REQUEST)
    ServletRequestBindingException 400 (SC_BAD_REQUEST)
    ConversionNotSupportedException 500 (SC_INTERNAL_SERVER_ERROR)
    TypeMismatchException 400 (SC_BAD_REQUEST)
    HttpMessageNotReadableException 400 (SC_BAD_REQUEST)
    HttpMessageNotWritableException 500 (SC_INTERNAL_SERVER_ERROR)
    MethodArgumentNotValidException 400 (SC_BAD_REQUEST)
    MissingServletRequestPartException 400 (SC_BAD_REQUEST)
    BindException 400 (SC_BAD_REQUEST)
    NoHandlerFoundException 400 (SC_BAD_REQUEST)
    AsyncRequestTimeoutException 404 (SC_NOT_FOUND)
    AsyncRequestTimeoutException 503 (SC_SERVICE_UNAVAILABLE)

    试图解析器:ViewResolver

    ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。  
    这里就有两个关键问题:使用哪个模板?用什么技术(规则)填入参数?  
    这其实是ViewResolver主要要做的工作,ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成
    

    RequestToViewNameTranslator

     ViewName是根据ViewName查找View,但有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。  
     RequestToViewNameTranslator在Spring MVC容器里只可以配置一个,所以所有request到ViewName的转换规则都要在一个Translator里面全部实现
    

    LocaleResolver

    解析视图需要两个参数:一是视图名,另一个是Locale。  
    视图名是处理器返回的,Locale是从哪里来的?  
    这就是LocaleResolver要做的事情。LocaleResolver用于从request解析出Locale,Locale就是zh-cn之类,表示一个区域,有了这个就可以对不同区域的用户显示不同的结果。  
    SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候
    

    ThemeResolver

    用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、如图片、css样式等。  
    SpringMVC的主题也支持国际化,同一个主题不同区域也可以显示不同的风格。  
    SpringMVC中跟主题相关的类有 ThemeResolver、ThemeSource和Theme。  
    主题是通过一系列资源来具体体现的,要得到一个主题的资源,首先要得到资源的名称,这是ThemeResolver的工作。然后通过主题名称找到对应的主题(可以理解为一个配置)文件,这是ThemeSource的工作。最后从主题中获取资源就可以了
    

    MultipartResolver

    用于处理上传请求。  
    处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File  
    如果上传多个文件,还可以调用getFileMap得到FileName->File结构的Map。此组件中一共有三个方法,作用分别是判断是不是上传请求,将request包装成MultipartHttpServletRequest、处理完后清理上传过程中产生的临时资源
    

    FlashMapManager

    用来管理FlashMap的,FlashMap主要用在redirect中传递参数
    

    DispatcherServlet 初始化

    第一步:HttpServletBean 初始化

    HttpServletBean 作用是扩展 HttpServlet
    
    // DispatcherServlet 构造方法
    public DispatcherServlet() {
        super();
        setDispatchOptionsRequest(true);
    }
    
    // FrameworkServlet 构造方法
    public FrameworkServlet() {
        // 省略了 supper()
    }
    
    // HttpServletBean.init()
    public final void init() throws ServletException {
        // ... 这部分的作用是扩展 HttpServlet,具体不展开主要是下面这个方法,目的是创建 IOC 容器
        // 这个方法是个空方法,子类实现
        initServletBean();
    }
    

    第二步:创建 IOC 容器

    /**
     * FrameworkServlet 重写了 initServletBean
     * 作用就是创建 WebApplicationContext(应用上下文),其实就是 MVC 的 IOC 容器
     */
    @Override
    protected final void initServletBean() throws ServletException {
        try {
            // 创建 IOC 容器
            this.webApplicationContext = initWebApplicationContext();
            // 子类实现
            initFrameworkServlet();
        }
        catch (ServletException | RuntimeException ex) {
            logger.error("Context initialization failed", ex);
            throw ex;
        }
    }
    
    /**
     * 具体怎么创建 IOC 容器
     * 
     */
    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
    
        // 当前对象是否有 wac,有就用它
        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        // 如果 wac 不存在就到 ServletContext 查找是否有初始化过的 wac
        if (wac == null) {
            wac = findWebApplicationContext();
        }
        // 还是不存在就创建一个
        if (wac == null) {
            // No context instance is defined for this servlet -> create a local one
            wac = createWebApplicationContext(rootContext);
        }
        // 到这里有了 wac,如果没有初始化就初始化
        if (!this.refreshEventReceived) {
            synchronized (this.onRefreshMonitor) {
                onRefresh(wac);
            }
        }
    
        if (this.publishContext) {
            // 把 wac 设置到 ServletContext 的 attrbiute 里去
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }
    
        return wac;
    }
    

    第三步:配置 DispatcherServlet 属性

    1,这一步其实就是初始化九大对象,这一步走完就意味着 DispatcherServlet 初始化完成
    2,每个组件的初始化不一一展开,分析下处理器映射器的初始化
    
    @Override
    protected void onRefresh(ApplicationContext context) {
        // 调用了 initStrategies()
        initStrategies(context);
    }
    
    // 初始化九大组件
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
    
    • 处理器映射器初始化
    private void initHandlerMappings(ApplicationContext context) {
        // 初始化映射器为空
        this.handlerMappings = null;
        // 据属性 detectAllHandlerMappings 决定是检测所有的 HandlerMapping 对象还是使用指定名称的 HandlerMapping 对象
        if (this.detectAllHandlerMappings) {
            // 从容器及其祖先容器查找所有类型为 HandlerMapping 的 HandlerMapping 对象,记录到   handlerMappings 并排序
            Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // 进行定义时所使用的 order 属性,顺序属性很关键,因为它涉及到 HandlerMapping 使用时的优先级
                // 比如在根据请求查找 handler 的时候 @RequestMapping 实现的 handler 使用 RequestMappingHandlerMapping,而不是 BeanNameUrlHandlerMapping
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        } else {
            // 获取名称为  handlerMapping 的 HandlerMapping bean 并记录到 handlerMappings
            try {
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }
    
        // 如果 handlerMapping 为空就通过 DispatcherServlet.properties 配置文件构建
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        }
    
        for (HandlerMapping mapping : this.handlerMappings) {
            if (mapping.usesPathPatterns()) {
                this.parseRequestPath = true;
                break;
            }
        }
    }
    

    DispatcherServlet 请求处理流程

    进入 DispatcherServlet.doDispatch 方法

    • DispatcherServlet 接收到请求,先是调用 doService 来处理,doService 调用 doDispatch 来处理

    获取 HandlerExecutionChain(处理器执行链)

    /**
     * doDispatch 会调用 getHandler 方法来获取处理器执行链,子类实现
     */
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            // DispatcherServlet 初始化时后会准备好一批处理器映射器
            // 遍历映射器,找到 handler
            for (HandlerMapping mapping : this.handlerMappings) {
                // 由抽象子类 AbstractHandlerMapping 实现
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
    
    /**
     * AbstractHandlerMapping.getHandler
     */
    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 先根据 request 查找 handler
        // 断点可以看到是根据 uri @RequestMapping() 值去找的方法:HandlerMethod.getHandlerInternal
        Object handler = getHandlerInternal(request);
    
        // 省略...
        // 把 handler 和所有的拦截器封装成 HandlerExecutionChain(也可以自定义拦截器)
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
            CorsConfiguration config = getCorsConfiguration(handler, request);
            if (getCorsConfigurationSource() != null) {
                CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
                config = (globalConfig != null ? globalConfig.combine(config) : config);
            }
            if (config != null) {
                config.validateAllowCredentials();
            }
            // executionChain 虽然再进行改变,最终 handler 肯定是不变的,所以改变的是拦截器,这里很明显是添加了跨域相关的拦截器
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
    
        return executionChain;
    }
    

    寻找能处理 handler 的处理器适配器

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            // 遍历所有处理器适配器
            for (HandlerAdapter adapter : this.handlerAdapters) {
                // DispatcherServlet 初始化的时候维护了一批适配器
                // @RequestMapping 实现的 handler 都是 RequestMappingHandlerAdapter 这个适配器去执行
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
            // 找不到能执行的抛出异常
        }
    }
    

    处理器适配器执行 handler,返回一个 ModelView

    // ha 就是 HandlerAdapter 即 RequestMappingHandlerAdapter
    // 根据断点 handle 方法是由其子类 AbstractHandlerMethodAdapter 实现
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    // AbstractHandlerMethodAdapter.handle
    @Override
    @Nullable
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 又调用了 handleInternal 来处理,返回一个 ModelAndView
        return handleInternal(request, response, (HandlerMethod) handler);
    }
    
    // AbstractHandlerMethodAdapter.handleInternal
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ModelAndView mav;
        checkRequest(request);
    
        // Execute invokeHandlerMethod in synchronized block if required.
        // 默认是 false
        if (this.synchronizeOnSession) {
            // ...
        }
        else {
            // No synchronization on session demanded at all...
            // 执行处理器(就是执行哪个 controller 的具体的方法),结果是一个 ModelView
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    
        // ...
    
        return mav;
    }
    

    ViewReslover 解析 ModelView

    这一步根据 ModelView 解析出具体 View 和 Model
    

    DispatcherServlet 渲染 View

    DispatcherServlet 对 View 进行渲染,将 model 渲染到 view
    

    DispatcherServlet 响应用户

  • 相关阅读:
    php打印出10*10表格
    php打印出1到2000年之间所有的闰年
    借鉴一篇好文章
    女程序员的预备篇
    SQL存储过程删除数据库日志文件的方法
    Mongodb无法访问28107的问题
    使用 xsd.exe 命令工具将 xsd 架构生成 类(CS) 文件
    C# 用POST提交json数据
    WinForm 使用 HttpUtility
    Sql Server 分区之后增加新的分区
  • 原文地址:https://www.cnblogs.com/huanggy/p/15151418.html
Copyright © 2011-2022 走看看