Spring Web MVC 下

一、跨域

spring mvc允许你处理CORS(跨源资源共享)。 

出于安全原因,浏览器禁止AJAX调用当前源代码之外的资源。例如,你可以将你的银行帐户放在一个选项卡中,钓鱼网站在另一个。来自钓鱼网站的脚本不能使用你的凭证向你的银行API发出AJAX请求 - 例如从你的帐户中取款!

Cross-Origin Resource Sharing(CORS)是一个由大多数浏览器实现的W3C规范,它允许您指定哪些类型的跨域请求被授权,而不是使用基于IFRAME或JSONP的安全性和功能较弱的解决方案。

CORS是如何工作

为了启用跨源请求(也就是说,origin头存在并且与请求的主机不同),、你需要有一些显式声明的CORS配置。如果没有找到匹配的CORS配置,请求将被拒绝。没有CORS头被添加到简单和实际CORS请求的响应中,因此浏览器拒绝它们。

每个HandlerMapping 都可以使用基于URL模式的CorsConfiguration映射单独配置。在大多数情况下,应用程序使用MVC-Java配置或XML名称空间来声明这样的映射,这会导致将一个全局映射传递给所有HandlerMappping 实例。

你可以将HandlerMapping映射级别的全局CORS配置与更细粒度的处理程序级CORS配置结合起来。例如,带注解的控制器可以使用类或方法级@CrossOrigin注解(其他处理程序可以实现CorsConfigurationSource)。

要了解更多信息或进行高级自定义,请查看以下代码:

  • CorsConfiguration
  • CorsProcessor, DefaultCorsProcessor
  • AbstractHandlerMapping
@CrossOrigin

@CrossOrigin注解支持对带注解的控制器方法的跨源请求,如下例所示:

@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

默认情况下,@CrossOrigin允许:

  • 所有的源头。
  • 所有请求头。
  • 控制器方法映射到的所有HTTP方法。

allowedCredentials在默认情况下不启用,因为这会建立一个信任级别,该级别公开特定于用户的敏感信息(如cookies和CSRF令牌),并且只应在适当的情况下使用。

maxAge设置为30分钟。

@CrossOrigin在类级别也受支持,并且由所有方法继承,如下例所示:

@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

可以在类级别和方法级别使用@CrossOrigin,如下例所示:

@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin("https://domain2.com")
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}
全局配置

除了细粒度的控制器方法级配置之外,你可能还需要定义一些全局CORS配置。你可以在任何HandlerMapping上单独设置基于URL的CorsConfiguration映射。大多数应用程序都使用mvc-java配置或mvc-xnm名称空间来实现这一点。

默认情况下,全局配置启用以下功能:

  • 所有的源头。
  • 所有请求头。
  • GET、HEAD和POST方法。

allowedCredentials在默认情况下不启用,因为这会建立一个信任级别,该级别公开特定于用户的敏感信息(如cookies和CSRF令牌),并且只应在适当的情况下使用。

maxAge设置为30分钟。

Java配置

要在MVC Java配置中启用CORS,可以使用CorsRegistry回调,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/api/**")
            .allowedOrigins("https://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(true).maxAge(3600);

        // Add more mappings...
    }
}
XML配置

要在XML命名空间中启用CORS,可以使用<mvc:cors>元素,如下例所示:

<mvc:cors>

    <mvc:mapping path="/api/**"
        allowed-origins="https://domain1.com, https://domain2.com"
        allowed-methods="GET, PUT"
        allowed-headers="header1, header2, header3"
        exposed-headers="header1, header2" allow-credentials="true"
        max-age="123" />

    <mvc:mapping path="/resources/**"
        allowed-origins="https://domain1.com" />

</mvc:cors>
CORS过滤器

你可以通过内置的CORS过滤器应用CORS支持。

要配置过滤器,请将CorsConfigurationSource传递给其构造函数,如下例所示:

CorsConfiguration config = new CorsConfiguration();

// Possibly...
// config.applyPermitDefaultValues()

config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);

CorsFilter filter = new CorsFilter(source);

二、Http缓存

HTTP缓存可以显著提高web应用程序的性能。HTTP缓存围绕着Cache-Control响应头和随后的条件请求头(如Last-Modified和ETag)进行的。Cache Control就如何缓存和重用响应向私有(例如,浏览器)和公共(例如,代理)缓存提供建议。ETag头用于发出条件请求,如果内容没有更改,则可能导致304(NOT_MODIFIED)没有正文。ETag可以看作是上一个修改过的头文件的更复杂的继承者。

CacheControl

CacheControl支持配置与Cache Control头相关的设置,并在许多位置被接受为参数:

虽然RFC 7234描述了缓存控制响应头的所有可能的指令,但CacheControl类型采用了面向用例的方法,重点关注常见场景:

// Cache for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);

// Prevent caching - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();

// Cache for ten days in public and private caches,
// public caches should not transform the response
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();

WebContentGenerator还接受一个更简单的cachePeriod属性(以秒为单位定义),其工作方式如下:

  • -1值不生成缓存控制响应头。
  • 0值通过使用“Cache-Control:no-store”指令阻止缓存。
  • n>0值通过使用“Cache-Control:max age=n”指令将给定响应缓存n秒。
Controllers

控制器可以添加对HTTP缓存的显式支持。我们建议这样做,因为需要先计算资源的lastModified或ETag值,然后才能将其与条件请求头进行比较。控制器可以向ResponseEntity添加ETag头和缓存控制设置,如下例所示:

@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {

    Book book = findBook(id);
    String version = book.getVersion();

    return ResponseEntity
            .ok()
            .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
            .eTag(version) // lastModified is also available
            .body(book);
}

如果与条件请求头的比较表明内容没有更改,则前面的示例发送一个304(NOT_MODIFIED)响应,该响应的主体为空。否则,ETag和Cache-Control头将被添加到响应中。

你还可以在控制器中对条件请求头进行检查,如下例所示:

@RequestMapping
public String myHandleMethod(WebRequest webRequest, Model model) {

    long eTag = ... 

    if (request.checkNotModified(eTag)) {
        return null; 
    }

    model.addAttribute(...); 
    return "myViewName";
}

根据eTag值、lastModified值或两者检查条件请求有三种变体。对于有条件的GET和HEAD请求,可以将响应设置为304(NOT_MODIFIED)。对于条件POST、PUT和DELETE,可以将响应设置为409(PRECONDITION_FAILED),以防止并发修改。

静态资源

你应该为静态资源提供缓存控件和条件响应头,以获得最佳性能。

ETag 过滤器

你可以使用ShallowEtagHeaderFilter添加根据响应内容计算的“shallow”eTag值,从而节省带宽而不是CPU时间。

三、视图技术

spring mvc中视图技术的使用是可插拔的,无论你决定使用Thymeleaf、Groovy标记模板、jsp还是其他技术,主要是配置更改的问题。

Thymeleaf

Thymeleaf是一个现代的服务器端Java模板引擎,它强调可以通过双击在浏览器中预览的自然HTML模板,这对于独立处理UI模板(例如,设计者)非常有帮助,而不需要运行服务器。如果您想替换jsp,Thymeleaf提供了一组最广泛的特性来简化这种转换。

Thymeleaf与spring mvc的集成由Thymeleaf项目管理。配置涉及一些bean声明,比如ServletContextTemplateResolver、SpringTemplateEngine和ThymeleafViewResolver

FreeMarker

Apache FreeMarker是一个模板引擎,用于生成从HTML到电子邮件和其他内容的任何类型的文本输出。Spring框架有一个内置的集成,可以将spring mvc与FreeMarker模板一起使用。

View Configuration

以下示例显示如何将FreeMarker配置为视图技术:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.freemarker();
    }

    // Configure FreeMarker...

    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
        return configurer;
    }
}

下面的示例演示如何在XML中配置相同的内容:

<mvc:annotation-driven/>

<mvc:view-resolvers>
    <mvc:freemarker/>
</mvc:view-resolvers>

<!-- Configure FreeMarker... -->
<mvc:freemarker-configurer>
    <mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>

或者,您也可以声明FreeMarkerConfigurer来完全控制所有属性,如下例所示:

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>

模板需要存储在前面示例中显示的FreeMarkerConfigurer 指定的目录中。如果你的控制器返回一个视图名welcome,解析器将查找/WEB-INF/freemarker/welcome.ftl模板。

FreeMarker 配置

你可以通过在FreeMarkerConfigurer上设置适当的bean属性,将FreeMarker“Settings”和“SharedVariables”直接传递给FreeMarker配置对象(由Spring管理)。freemarkerSettings属性需要java.util.Properties属性对象,并且freemarkerVariables属性需要java.util.Map. 下面的示例演示如何执行此操作:

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
    <property name="freemarkerVariables">
        <map>
            <entry key="xml_escape" value-ref="fmXmlEscape"/>
        </map>
    </property>
</bean>

<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
Groovy标记

Groovy标记模板引擎主要用于生成类似XML的标记(XML、XHTML、HTML5等),但是你可以使用它来生成任何基于文本的内容。Spring框架有一个内置的集成,可以使用spring mvc和Groovy标记。

下面的示例演示如何配置Groovy标记模板引擎:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.groovy();
    }

    // Configure the Groovy Markup Template Engine...

    @Bean
    public GroovyMarkupConfigurer groovyMarkupConfigurer() {
        GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
        configurer.setResourceLoaderPath("/WEB-INF/");
        return configurer;
    }
}

下面的示例演示如何在XML中配置相同的内容:

<mvc:annotation-driven/>

<mvc:view-resolvers>
    <mvc:groovy/>
</mvc:view-resolvers>

<!-- Configure the Groovy Markup Template Engine... -->
<mvc:groovy-configurer resource-loader-path="/WEB-INF/"/>

与传统的模板引擎不同,Groovy标记依赖于使用构建器语法的DSL。以下示例显示了HTML页面的示例模板:

yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
    head {
        meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')
        title('My page')
    }
    body {
        p('This is an example of HTML contents')
    }
}
JSP and JSTL
视图解析

在使用jsp进行开发时,可以声明一个InternalResourceViewResolver或一个ResourceBundleViewResolver bean。

ResourceBundleViewResolver依赖属性文件来定义映射到类和URL的视图名称。使用ResourceBundleViewResolver,你可以只使用一个解析器来混合不同类型的视图,如下例所示:

<!-- the ResourceBundleViewResolver -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename" value="views"/>
</bean>

# And a sample properties file is used (views.properties in WEB-INF/classes):
welcome.(class)=org.springframework.web.servlet.view.JstlView
welcome.url=/WEB-INF/jsp/welcome.jsp

productList.(class)=org.springframework.web.servlet.view.JstlView
productList.url=/WEB-INF/jsp/productlist.jsp

InternalResourceViewResolver也可以用于JSP。作为一个最佳实践,我们强烈建议将你的JSP文件放在“WEB-INF”目录下的一个目录中,这样客户机就不能直接访问它了。

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>
jsp与JSTL

当使用JSP标准标记库(JSTL)时,你必须使用一个特殊的视图类JstlView,因为JSTL在I18N功能开始工作之前需要做一些准备。

Spring的JSP标记库

Spring提供了请求参数到命令对象的数据绑定,为了方便结合这些数据绑定特性来开发JSP页面,Spring提供了一些标记,使事情变得更加简单。所有Spring标记都有HTML转义特性,可以启用或禁用字符转义。

这个 spring.tld标记库描述符(TLD)包含在spring-webmvc.jar。

Spring的表单标记库

 从2.0版开始,Spring提供了一套全面的数据绑定标记,用于在使用JSP和spring web mvc时处理表单元素。每个标记都支持其对应的HTML标记对应的属性集,从而使标记熟悉且易于使用。标记生成的HTML与HTML 4.01/XHTML 1.0兼容。

与其他form/input标记库不同,Spring的表单标记库与spring web mvc集成,允许标记访问命令对象和控制器处理的引用数据。正如我们在下面的示例中所示,表单标记使jsp更易于开发、读取和维护。

我们浏览表单标记并查看每个标记如何使用的示例。我们包含了生成的HTML片段,其中某些标记需要进一步的注释。

配置

表单标记库在spring-webmvc.jar. 库描述为spring-form.tld。

要使用此库中的标记,请将以下指令添加到JSP页面的顶部:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

其中form是要用于此库中标记的标记名前缀。

PDF and Excel

Spring提供了返回输出而不是HTML的方法,包括PDF和Excel电子表格。

HTML页面并不总是用户查看模型输出的最佳方式,Spring使从模型数据动态生成PDF文档或Excel电子表格变得非常简单。文档是视图,从服务器以正确的内容类型流式传输,以便(希望)使客户机能够运行其电子表格或PDF查看器应用程序作为响应。

为了使用Excel视图,需要将Apache POI库添加到类路径中。为了生成PDF,你需要添加(最好)OpenPDF库。

如果可能,您应该使用底层文档生成库的最新版本。特别是,我们强烈建议使用OpenPDF(例如OpenPDF 1.2.12),而不是过时的原始iText 2.1.7,因为OpenPDF得到了积极维护,并修复了不可信PDF内容的一个重要漏洞。

PDF视图

单词列表的简单PDF视图可以扩展org.springframework.web.servlet.view.document.AbstractPdfView并实现buildPdfDocument()方法,如下例所示:

public class PdfWordList extends AbstractPdfView {

    protected void buildPdfDocument(Map<String, Object> model, Document doc, PdfWriter writer,
            HttpServletRequest request, HttpServletResponse response) throws Exception {

        List<String> words = (List<String>) model.get("wordList");
        for (String word : words) {
            doc.add(new Paragraph(word));
        }
    }
}

控制器可以从外部视图定义(按名称引用)或作为处理程序方法的视图实例返回此类视图。

Excel视图

从springframework4.2开始,org.springframework.web.servlet.view.document.AbstractXlsView是作为Excel视图的基类提供的。它基于Apache POI,有专门的子类(AbstractXlsxView和AbstractXlsxStreamingView)来取代过时的AbstractExcelView类。

该编程模型类似于AbstractPdfView,buildExcelDocument()作为中心模板方法,控制器能够从外部定义(按名称)或从处理程序方法返回视图实例。

Jackson

Spring提供了对jacksonjson库的支持。

基于Jackson的JSON MVC视图

MappingJackson2JsonView使用Jackson库的ObjectMapper将响应内容呈现为JSON。默认情况下,模型映射的全部内容(框架特定类除外)都被编码为JSON。对于需要过滤映射内容的情况,可以使用modelKeys属性指定要编码的特定模型属性集。还可以使用extractValueFromSingleKeyModel属性直接提取和序列化单键模型中的值,而不是作为模型属性的映射。

你可以根据需要使用Jackson提供的注释定制JSON映射。当需要进一步控制时,可以通过ObjectMapper属性注入自定义ObjectMapper,以防需要为特定类型提供自定义JSON序列化程序和反序列化程序。

基于Jackson的XML视图

MappingJackson2XmlView使用Jackson XML扩展的XmlMapper将响应内容呈现为XML。如果模型中包含多个项,则应使用该属性显式地将该项设置为序列化。如果模型包含一个条目,它将自动序列化。

你可以根据需要使用JAXB或Jackson提供的注释定制XML映射。当需要进一步控制时,可以通过ObjectMapper属性注入自定义XmlMapper,以防自定义XML需要为特定类型提供序列化器和反序列化器。

四、Java MVC配置

启用MVC配置 

在Java配置中,可以使用@EnableWebMvc注解来启用MVC配置,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig {
}

在XML配置中,可以使用<mvc:annotation-driven/>元素来启用MVC配置,如下例所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

</beans>
MVC配置API

在Java配置中,可以实现WebMvcConfigurer接口,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    // Implement configuration methods...
}
类型转换

默认情况下,Spring已安装了对Number和Date类型的格式化程序,包括对@NumberFormat和@DateTimeFormat注解的支持。如果类路径中存在Joda-Time,则还安装了对Joda时间格式库的完全支持。

在Java配置中,可以注册自定义格式化程序和转换器,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}

下面的示例演示如何在XML中实现相同的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven conversion-service="conversionService"/>

    <bean id="conversionService"
            class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="org.example.MyConverter"/>
            </set>
        </property>
        <property name="formatters">
            <set>
                <bean class="org.example.MyFormatter"/>
                <bean class="org.example.MyAnnotationFormatterFactory"/>
            </set>
        </property>
        <property name="formatterRegistrars">
            <set>
                <bean class="org.example.MyFormatterRegistrar"/>
            </set>
        </property>
    </bean>

</beans>

有关何时使用FormatterRegistrar实现的更多信息,请参阅FormatterRegistrar SPI和FormattingConversionServiceFactoryBean。

Validation(验证)

默认情况下,如果类路径上存在Bean验证(例如Hibernate Validator),则LocalValidatorFactoryBean注册为全局验证器,以便与@Valid和@Validated on@Controller方法参数一起使用。

在Java配置中,可以自定义全局验证器实例,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public Validator getValidator(); {
        // ...
    }
}

下面的示例演示如何在XML中实现相同的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven validator="globalValidator"/>

</beans>

请注意,你还可以在本地注册验证器实现,如下例所示:

@Controller
public class MyController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new FooValidator());
    }

}

如果需要在某个地方注入LocalValidatorFactoryBean ,请创建一个bean并用@Primary标记它,以避免与MVC配置中声明的bean冲突。

Interceptors(拦截器)

在Java配置中,可以注册拦截器以应用于传入请求,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor());
        registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }
}

下面的示例演示如何在XML中实现相同的配置:

<mvc:interceptors>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/admin/**"/>
        <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/secure/*"/>
        <bean class="org.example.SecurityInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
Content Types(内容类型)

你可以配置springmvc如何根据请求确定请求的媒体类型(例如,Accept header、URL路径扩展、查询参数等)。

默认情况下,首先检查URL路径扩展,json、xml、rss和atom注册为已知扩展(取决于类路径依赖关系)。然后选中Accept请求头。

考虑将这些默认值更改为Accept头,如果必须使用基于URL的内容类型解析,请考虑在路径扩展上使用查询参数策略。

在Java配置中,可以自定义请求的内容类型解析,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.mediaType("json", MediaType.APPLICATION_JSON);
        configurer.mediaType("xml", MediaType.APPLICATION_XML);
    }
}

下面的示例演示如何在XML中实现相同的配置:

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="mediaTypes">
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>
Message Converters(消息转换器)

你可以通过重写configureMessageConverters()(替换SpringMVC创建的默认转换器)或重写ExtendMessageConverter()(自定义默认转换器或向默认转换器添加其他转换器)来自定义HttpMessageConverter。

以下示例使用自定义的ObjectMapper而不是默认的ObjectMapper添加了XML和Jackson JSON转换器:

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                .modulesToInstall(new ParameterNamesModule());
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }
}

在前面的示例中,Jackson2ObjectMapperBuilder 用于为MappingJackson2HttpMessageConverter和MappingJackson2XmlHttpMessageConverter创建一个公共配置,其中MappingJackson2XmlHttpMessageConverter启用了缩进、自定义日期格式和jackson模块参数名的注册,在Java中增加了对访问参数的支持。

此生成器自定义Jackson的默认属性,如下所示:

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 已禁用
  • MapperFeature.DEFAULT_VIEW_INCLUSION 已禁用

如果在类路径上检测到以下已知模块,它还会自动注册它们:

  • jackson-datatype-jdk7:支持java7类型,例如java.nio.file文件.路径。
  • jackson-datatype-joda:对joda时间类型的支持。
  • jackson-datatype-jsr310:支持java8日期和时间API类型
  • jackson-datatype-jdk8:支持其他java8类型,比如可选的。

 除了jackson-dataformat-xml 之外,使用jacksonxml支持实现缩进还需要woodstox-core-asl依赖

还提供了其他有趣的Jackson模块: 

  • jackson-datatype-money:支持javax.money类型(非官方模块)
  • jackson-datatype-hibernate:支持hibernate特定的类型和属性(包括延迟加载方面)。

下面的示例演示如何在XML中实现相同的配置:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper" ref="objectMapper"/>
        </bean>
        <bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
            <property name="objectMapper" ref="xmlMapper"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
      p:indentOutput="true"
      p:simpleDateFormat="yyyy-MM-dd"
      p:modulesToInstall="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"/>

<bean id="xmlMapper" parent="objectMapper" p:createXmlMapper="true"/>
视图控制器

这是定义ParameterizableViewController的快捷方式,该控制器在调用时立即转发到视图。在视图生成响应之前,如果没有Java控制器逻辑要执行,则可以在静态情况下使用它。

下面的Java配置示例将请求转发到名为home的视图:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
    }
}

下面的示例实现了与前一个示例相同的功能,但是对于XML,通过使用<mvc:view-controller>要素:

<mvc:view-controller path="/" view-name="home"/>
视图解析器

MVC配置简化了视图解析器的注册。

以下Java配置示例使用JSP和Jackson作为JSON呈现的默认视图来配置内容协商视图解析:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        registry.jsp();
    }
}

下面的示例演示如何在XML中实现相同的配置:

<mvc:view-resolvers>
    <mvc:content-negotiation>
        <mvc:default-views>
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
        </mvc:default-views>
    </mvc:content-negotiation>
    <mvc:jsp/>
</mvc:view-resolvers>

但是请注意,FreeMarker、Tiles、Groovy标记和脚本模板也需要底层视图技术的配置。

MVC命名空间提供了专用元素。以下示例适用于FreeMarker:

<mvc:view-resolvers>
    <mvc:content-negotiation>
        <mvc:default-views>
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
        </mvc:default-views>
    </mvc:content-negotiation>
    <mvc:freemarker cache="false"/>
</mvc:view-resolvers>

<mvc:freemarker-configurer>
    <mvc:template-loader-path location="/freemarker"/>
</mvc:freemarker-configurer>

在Java配置中,可以添加相应的Configurer bean,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        registry.freeMarker().cache(false);
    }

    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/freemarker");
        return configurer;
    }
}
静态资源

此选项提供了一种从基于资源的位置列表中提供静态资源的方便方法。

在下一个示例中,给定一个以/resources开头的请求,相对路径用于查找和服务web应用程序根目录下的/public或/static下的类路径上的静态资源。这些资源的有效期为一年,以确保最大限度地利用浏览器缓存并减少浏览器发出的HTTP请求。最后修改的头也被计算,如果存在,则返回304状态代码。

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
            .addResourceLocations("/public", "classpath:/static/")
            .setCachePeriod(31556926);
    }
}

下面的示例演示如何在XML中实现相同的配置:

<mvc:resources mapping="/resources/**"
    location="/public, classpath:/static/"
    cache-period="31556926" />

资源处理程序还支持一系列ResourceResolver实现和ResourceTransformer实现,你可以使用它们创建一个工具链来处理优化的资源。

你可以根据内容、固定应用程序版本或其他内容计算的MD5哈希,对版本化的资源url使用VersionResourceResolver 。ContentVersionStrategy(MD5哈希)是一个不错的选择 ,但有一些明显的例外,比如与模块加载器一起使用的JavaScript资源。

以下示例说明如何在Java配置中使用VersionResourceResolver:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/public/")
                .resourceChain(true)
                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
    }
}

下面的示例演示如何在XML中实现相同的配置:

<mvc:resources mapping="/resources/**" location="/public/">
    <mvc:resource-chain resource-cache="true">
        <mvc:resolvers>
            <mvc:version-resolver>
                <mvc:content-version-strategy patterns="/**"/>
            </mvc:version-resolver>
        </mvc:resolvers>
    </mvc:resource-chain>
</mvc:resources>

然后可以使用ResourceUrlProvider重写URL,并应用解析器和转换器的完整链,例如插入版本。MVC配置提供了一个ResourceUrlProvider,以便可以将其注入其他bean中。你还可以使用针对Thymeleaf、jsp、FreeMarker和其他具有依赖HttpServletResponse#encodeURL.的URL标记的ResourceUrlEncodingFilter使重写变得透明。

请注意,当同时使用EncodedResourceResolver(例如,为gzip或brotli编码的资源提供服务)和VersionResourceResolver时,必须按以下顺序注册它们。这确保基于内容的版本始终基于未编码的文件可靠地计算。

Web Jars也通过WebJarsResourceResolver来支持,当org.webjars:webjars-locator-core核心库位于类路径上。解析器可以重写url以包含jar的版本,还可以与没有版本的传入url相匹配,例如from/jquery/jquery.min.js至/jquery/1.2.0/jquery.min.js.

默认的Servlet

spring mvc允许将DispatcherServlet映射到/(因此覆盖容器默认Servlet的映射),同时仍然允许静态资源请求由容器的默认Servlet处理。它配置了一个DefaultServletHttpRequestHandler,其URL映射为/**并且相对于其他URL映射具有最低优先级。

此处理程序将所有请求转发到默认Servlet。因此,它必须按照所有其他URL HandlerMappings的顺序保持在最后。如果你用<mvc:annotation-driven> 或者,如果你设置自己的自定义HandlerMapping实例,请确保将其order属性设置为低于DefaultServletHttpRequestHandler的值,即Integer.MAX_值。

以下示例显示如何使用默认设置启用该功能:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

下面的示例演示如何在XML中实现相同的配置:

<mvc:default-servlet-handler/>

重写 / Servlet映射必须按名称而不是路径检索默认Servlet的RequestDispatcher。DefaultServletHttpRequestHandler尝试在启动时自动检测容器的默认Servlet(主要的Servlet容器:Tomcat、Jetty、GlassFish、JBoss、Resin、WebLogic和WebSphere)。如果使用不同的名称自定义配置了默认的Servlet,或者在默认Servlet名称未知的情况下使用了不同的Servlet容器,则必须显式地提供默认Servlet的名称,如下例所示:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable("myCustomDefaultServlet");
    }

}

下面的示例演示如何在XML中实现相同的配置:

<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/>
路径匹配

你可以自定义与路径匹配和URL处理相关的选项。有关各个选项的详细信息,请参阅PathMatchConfigurer文档。

下面的示例演示如何在Java配置中自定义路径匹配:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer
            .setUseSuffixPatternMatch(true)
            .setUseTrailingSlashMatch(false)
            .setUseRegisteredSuffixPatternMatch(true)
            .setPathMatcher(antPathMatcher())
            .setUrlPathHelper(urlPathHelper())
            .addPathPrefix("/api",
                    HandlerTypePredicate.forAnnotation(RestController.class));
    }

    @Bean
    public UrlPathHelper urlPathHelper() {
        //...
    }

    @Bean
    public PathMatcher antPathMatcher() {
        //...
    }

}

下面的示例演示如何在XML中实现相同的配置:

<mvc:annotation-driven>
    <mvc:path-matching
        suffix-pattern="true"
        trailing-slash="false"
        registered-suffixes-only="true"
        path-helper="pathHelper"
        path-matcher="pathMatcher"/>
</mvc:annotation-driven>

<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>
高级Java配置

@EnableWebMvc 引入DelegatingWebMvcConfiguration:

  • 为spring mvc应用程序提供默认的Spring配置。
  • 检测并委托给WebMvcConfigurer实现以自定义该配置。

对于高级模式,可以删除@EnableWebMvc并直接从DelegatingWebMvcConfiguration扩展,而不是实现WebMvcConfigurer,如下例所示:

@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {

    // ...

}

你可以在WebConfig中保留现有的方法,但是现在还可以覆盖基类中的bean声明,并且类路径上仍然可以有任意数量的其他WebMvcConfigurer 实现。

高级XML配置

MVC命名空间没有高级模式。如果你需要在bean上定制一个不能更改的属性,那么可以使用Spring ApplicationContext的BeanPostProcessor生命周期钩子,如下例所示:

@Component
public class MyPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
        // ...
    }
}

注意,您需要将MyPostProcessor声明为一个bean,可以用XML显式地声明,也可以通过<component scan/>声明检测到它。

五、HTTP/2

Servlet 4容器需要支持HTTP/2, Spring Framework 5与Servlet API 4兼容。从编程模型的角度来看,应用程序不需要做任何特定的事情。但是,还有一些与服务器配置相关的注意事项。

Servlet API公开了一个与HTTP/2相关的构造。你可以使用javax.servlet.http.PushBuilder主动将资源推送到客户端,并且支持将其作为@RequestMapping方法的方法参数。

原文地址:https://www.cnblogs.com/myitnews/p/13424414.html