Spring Web MVC 文档简略翻译1:简介、Dispat

作者: GunnerAha | 来源:发表于2018-03-31 23:09 被阅读51次

    Spring MVC 文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc


    1. Spring Web MVC

    1.1. 简介

    Spring MVC是基于Servlet API和Spring框架构建的项目,同时,Spirng WebFlux是支持反应式web构建的框架。

    1.2. DispatcherServlet

    DispatcherServlet用于分发请求,实际的请求处理工作由其他组件处理。

    DispatcherServlet可以使用Java代码或者在web.xml中配置。其需要处理的工作有:请求映射、视图处理、异常处理等。

    如下代码是DispatcherServlet的注册和初始化,该类会自动被Servlet容器检测到。

    public class MyWebApplicationInitializer implements WebApplicationInitializer {
    
        @Override
        public void onStartup(ServletContext servletCxt) {
    
            // Load Spring web application configuration
            AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
            ac.register(AppConfig.class);
            ac.refresh();
    
            // Create and register the DispatcherServlet
            DispatcherServlet servlet = new DispatcherServlet(ac);
            ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
            registration.setLoadOnStartup(1);
            registration.addMapping("/app/*");
        }
    }
    

    如下是web.xml中DispatcherServlet的注册和初始化配置。

    <web-app>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/app-context.xml</param-value>
        </context-param>
    
        <servlet>
            <servlet-name>app</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value></param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>app</servlet-name>
            <url-pattern>/app/*</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
    1.2.1. Context层次结构

    DispatcherServlet依赖WebApplicationContext进行配置。多个DispatcherServlet可以共用一个WebAplicationContext实例。Root WebApplicationContext包括数据服务bean,业务服务bean等,而Servlet WebpplicationContext仅包含特定servlet处理bean。


    image

    如下是WebApplicationContext的配置示例:

    public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class<?[] { RootConfig.class };
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class<?[] { App1Config.class };
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[] { "/app1/*" };
        }
    }
    

    如果不需要应用上下文,只需要使用getRootConfigClasses()返回配置并且getServletConfigClasses()返回null即可。

    对应的web.xml如下:

    <web-app>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/root-context.xml</param-value>
        </context-param>
    
        <servlet>
            <servlet-name>app1</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/app1-context.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>app1</servlet-name>
            <url-pattern>/app1/*</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    如果应用不需要上下文,仅需要配置root上下文并设置contextConfigLocation参数为空。

    1.2.2. 特殊Bean类型

    DispatcherServlet委派特殊Bean处理请求并返回相应。如下是DispatcherHandler能够检测的特殊Bean:
    1.HandlerMapping:映射请求到Handler,有两个实现:RequestMappingHandlerMapping和SimpleUrlHandlerMapping。
    2.HandlerAdapter:用于帮助DispatcherServlet调动handler。
    3.HandlerExceptionResolver:解决映射异常,html错误视图等。
    4.VieResolver:主要作用是把一个逻辑上的视图名称解析为一个真正的视图,SpringMVC中用于把View对象呈现给客户端的是View对象本身,而ViewResolver只是把逻辑视图名称解析为对象的View对象。
    4.LocaleResolver、LocaleContextResolver:国际化资源处理。
    5.ThemeResolver:主题处理,可以个性化主题处理。
    6.MultipartResolver:处理multi-part request,如上传等。
    7.FlashMapManager:存储FlashMap,用于处理重定向问题。

    1.2.3. Web MVC配置

    应用可以声明上述特殊bean来处理对应的请求。DispatcherServlet会检查WebApplicationContext,如果没有发现特殊bean,将返回DispatcherServlet.properties中定义的默认类型bean。

    1.2.4. Servlet配置

    在Servlet 3.0+环境中,支持在代码中和web.xml中配置servlet。如下是注册DispacherServlet的例子:

    import org.springframework.web.WebApplicationInitializer;
    
    public class MyWebApplicationInitializer implements WebApplicationInitializer {
    
        @Override
        public void onStartup(ServletContext container) {
            XmlWebApplicationContext appContext = new XmlWebApplicationContext();
            appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
    
            ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
            registration.setLoadOnStartup(1);
            registration.addMapping("/");
        }
    }
    

    继承WebApplicationInitializer保证你的servlet被Servlet3容器加载。AbstractDispatcherServletInitializer是WebApplicationInitializer的抽象类,它通过重写方法来简化Servlet的配置。

    下面是推荐的基于Java的应用配置方法:

    public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return null;
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class<?>[] { MyWebConfig.class };
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[] { "/" };
        }
    }
    

    如果使用基于XML的配置,就应该直接扩展AbstractDispatcherServletInitializer。

    public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
    
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            return null;
        }
    
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            XmlWebApplicationContext cxt = new XmlWebApplicationContext();
            cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
            return cxt;
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[] { "/" };
        }
    }
    

    AbstractDispatcherServletInitializer也提供简单的方法添加Filer实例。

    public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
    
        // ...
    
        @Override
        protected Filter[] getServletFilters() {
            return new Filter[] {
                new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
        }
    }
    

    AbstractDispatcherServletInitializer的isAsyncSupported保护方法提供 异步支持,默认设置为true。

    如果想自定义DispatcherServlet,重写createDispatcherServlet方法。

    1.2.5. Processing

    DispatcherServlet处理请求流程如下:
    1.查找WebApplicationContext并将其作为属性和请求绑定以便使用。
    2.绑定local resolver到请求处理资源国际化,可以不使用。
    3.绑定theme resolver到请求处理主题,可以不使用。
    4.如果设定了multipart resolver并发送了multipart请求,请求的内容采用MultipartHttpServletRequest包装以便后续处理
    5.查找匹配的handler,找到后顺序执行:preprocessors/postprocessors/controllers。
    6.查找模型,找到则渲染视图,找不到则不渲染视图。

    WebApplicationContext中的HandlerExceptionResolver用于处理异常请求。支持自定义异常处理。

    DispatcherServlet也支持返回上次修改日期,其流程为:DispatcherServlet查找对应的映射处理器并检查该处理器是否实现LastModified接口,如果继承该接口,通过方法long getLastModified(request)返回上次修改时间值给客户端。

    你可以通过添加Servlet初始化参数(init-param)在web.xml中自定义DispatcherServlet。下面是支持的初始化参数:
    1.contexClass:继承自WebApplicationContext,实力化Servlet使用的上下文,默认使用XmlWebApplicationContext。
    2.contextConfigLocation:上下文配置文件路径,支持多个路径配置多个上下文,对于在不同路径配置文件中重复定义的bean,以最后的定义为准。
    3.namespace:WebApplicationContext的命名空间,默认[servlet-name]-servlet。

    1.2.6. 拦截器

    所有映射处理器都支持拦截器,用于对请求添加特殊处理。拦截器需要实现org.springframework.web.servlet包下的HandlerInterceptor接口的三个方法:
    1.preHandler(..)-handler执行前处理
    2.postHandler(..)-handler执行后处理
    3.afterCompletion(..)-handler处理完成之后执行
    preHandler(..)方法返回布尔值,你可以通过该函数打断或者继续handler的处理流程。true继续执行,false不继续执行接下来的拦截器或者handler。

    1.2.7. 异常

    如果在请求映射或者处理时发生异常,DispatcherServlet委托HandlerExceptionResolver解决异常,例如准备错误页面等。

    下面是HandlerExceptionResolver的实现类:
    1.SimpleMappingExceptionResolver:异常类与错误视图名称的映射,用于在浏览器中渲染错误页面。
    2.DefaultHandlerExceptionResolver:处理异常并将其映射成HTTP错误码。
    3.ResponseStatusExceptionResolver:使用@ResponseStatus注解处理异常并映射为HTTP错误码。
    4.ExceptionHandlerExceptionResolver:在使用@Controller和@ControllerAdvice注解的类种,通过@ExceptionHandler注解处理异常。

    异常处理过程:设置多个异常处理resolver,如果需要,设置resolver的优先级,优先级越高,执行的越晚。

    HandlerExceptionResolver约定返回内容如下:
    1.指向错误页面的ModeAndView。
    2.如果异常在resolver中被处理,返回空的ModeAndView。
    3.如果异常无法处理,返回null后继续下一个resolvers;如果均无法处理,异常抛给Servlet容器。

    异常处理的配置就是在spring配置中添加HandlerExceptionesolver。对于Spring MVC的异常,使用@ResponseStatus注解的异常和@ExceptionHandler方法注解的异常,MVC配置自动声明内置的异常处理器。

    容器中的错误页面
    如果HandlerExceptionResolver依然无法处理异常或者响应错误码是4xx,5xx,Servlet容器会呈现一个错误页面。自定义容器的错误页面,需要在web.xml 中进行如下定义。

    <error-page>
        <location>/error</location>
    </error-page>
    

    当由servlet容器处理异常或者错去码是4xx或者5xx时,servlet容器根据配置的 url(/error)分发错误。然后DispatcherServlet可以通过@Controller映射处理返回异常视图名称,如下:

    @RestController
    public class ErrorController {
    
        @RequestMapping(path = "/error")
        public Map<String, Object> handle(HttpServletRequest request) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("status", request.getAttribute("javax.servlet.error.status_code"));
            map.put("reason", request.getAttribute("javax.servlet.error.message"));
            return map;
        }
    }
    
    1.2.8. 视图

    Spring MVC定义ViewResolver和View放假鹅口来处理视图。ViewResolver定义名和实际视图的映射,View用于展示视图之前的数据准备。下面是ViewResolver的类层次结构:
    1.AbstractCachingViewResolvre:用于缓存处理的视图实例。缓存可以提升性能,可以将cache属性设置为false来关闭缓存。如果必须刷新缓存,可以调用removeFromCache(String viewName, Locale loc)方法。
    2.XmlViewResolver:实现ViewResolver接口,采用xml的配置方式,默认配置文件是/WEB-INF/view.xml。
    3.ResourceBundleViewResolver:实现ViewResolver接口,使用ResourceBundle读取配置文件中视图类和视图的映射关系。配置文件中配置方式为:[viewname].(class)指定视图类,[viewname].url指定视图。
    4.UrlBasedViewResolver:简单实现ViewResolver接口,关联逻辑视图名与url,不使用明确的视图定义。
    5.InternalResourceViewResolver:UrlBaseViewResolver的子类,支持InternalResourceView/JstlView和TilesView。
    6.FreeMarkerViewResolver:UrlBasedViewResolver的子类,支持FreeMarkerView视图处理。
    7.ContentNegotiatingViewResolver:实现ViewResolver接口,用于根据请求名或者Accep请求消息头类型处理视图。

    处理流程:可以设置多个resolver组成视图处理链,甚至可以设置处理的优先级。记住,优先级越高,越晚执行。

    ViewResolver返回null表示视图未找到。在处理jsp时,InternalResourceViewResolver一定要设置为最后处理的resolver。下文会介绍ViewResolver的配置。

    重定向:
    特殊的重定向:通过视图名的前缀进行重定向。UrlBaseViewResolver识别前缀然后将前缀后的内容作为重定向的地址。

    控制器Controller返回RedirectView也是重定向。例如,逻辑视图名:redirect:/myapp/some/resource基于当前上下文重定向相对路径,而redirect:http://myhost.com/some/arbitrary/path基于绝对路径进行重定向。

    如果控制器的方法使用@ResponseStatus注解,那么注解值优先于RedirectView设置的响应值。

    转发:UrlBasedViewResolver可以根据视图名处理转发。通过创建InternalResourceView执行RequestDispatcher.forward()。这样对于InternalResourceViewResolver和InternalResourceView而言前缀失效,不进行重定向而进行转发。

    内容协商:协商返回的数据格式。
    ContentNegotiatingViewResolver不直接处理view,而是委托给其他符合客户端请求格式的resolver。检测Accept消息头中的数据格式或者从参数(如/path?form=pdf)中提取格式。ContentNegotiatingViewResolver通过比较media type(就是Content-Type)来选取合适的视图处理。如果没有找到对应的,使用DefaultView。Accept消息头可以设置统配,例如text/*可以匹配到text/xml的视图处理。

    1.2.9. 国际化资源

    DispatcherServlet支持根据客户端地区自动消息转换,这是通过LocaleResolver实现的。

    请求来到时,DispatcherServlet查找对应的LocaleResolver处理国际化。使用RequestContext.getLocale()方法获取当前处理国际化resolver的地区。

    LocaleContextResolver提供LocaleContext,用以获取当前client的时区。请求的时区信息开通过RequestContext.getTmeZone获取。

    地区信息还可以使用Header Resolver从accept-language消息头中获取并处理
    地区信息还可以使用Cookie Resolver从cookie中获取并处理。CookieLocaleResolver例子如下:

    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
    
        <property name="cookieName" value="clientlanguage"/>
    
        <!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) -->
        <property name="cookieMaxAge" value="100000"/>
    
    </bean>
    

    CookieLocaleResolver属性如下:
    1.cookieName:cookie名称
    2.cookieMaxAge:客户端存储coodie的最大值,-1表示不存储cookie,客户端关闭browser时cookie清空。
    3.cookiePath:存储cookie的地址。

    SessionLocaleResolver也允许从session中获取Locale和TimeZone。该信息存储于Servlet容器的HttpSession中。

    地区拦截器:通过添加LocaleChangeInterceptor处理特定映射的地区。

    1.2.10. 主题

    主题是一系列静态资源,如样式表、图片等。Spring MVC支持主题的更改。

    定义主题:要想使用主题,必须实现org.springframework.ui.context.ThemeSource接口。WebApplicaionContext继承ThemeSource但将处理任务委派给另一个实现:org.springframework.ui.context.support.ResourceBundleThemeSource。它读取配置文件中主题的定义构建主题,配置如下:

    styleSheet=/themes/cool/style.css
    background=/themes/cool/img/coolBg.jpg
    

    对于JSP,使用spring:theme自定义标签,如下:

    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
    <html>
        <head>
            <link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/>
        </head>
        <body style="background=<spring:theme code='background'/>">
            ...
        </body>
    </html>
    

    主题处理器有:FixedThemeResolver、SessionThemeResolver和CookieThemeResolver。提供ThemeChangeInterceptor拦截器根据请求改变主题。

    1.2.11. Multipart resolver

    Multipart resolver用于处理诸如上传等multipart请求。使用时需要在DispatcherServlet中定义MultipartResolver,然后会监听content-type为multipart/form-data的请求,然后解析内容并将当前的HttpServletRequest打包为MultipartHttpServletRequest以便处理。

    Apache文件上传:通过配置CommonsMultipartResolver来使用Apache文件上传。需要依赖commons-fileupload包。

    在Servlet3.0中,添加multipart支持需要进行注册。在web.xml中添加<multipart-config>然后添加StandardServletMultipartResolver处理multipart类型请求。

    相关文章

      网友评论

        本文标题:Spring Web MVC 文档简略翻译1:简介、Dispat

        本文链接:https://www.haomeiwen.com/subject/jwqpcftx.html