Spring Core 官方文档阅读笔记(十八)
1. CORS
跨域资源共享(Cross-Origin Resource Sharing,CORS)是大多数浏览器实现的W3C规范,它允许指定授权的跨域请求的类型,也就是用来解决我们经常会遇到的跨域问题。
Spring MVC的HandlerMapping实现提供了对CORS的内置支持,在成功地将请求映射到处理程序之后,HandlerMapping实现检查给定请求和处理程序的CORS配置并采取进一步的操作。
我们可以通过全局配置来实现CORS,或者使用@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) {
// ...
}
}
上面这段摘自Spring官方示例,此时,被@CrossOrigin注解的retrieve方法就允许跨域请求。@CrossOrigin默认允许所有Origin,以及任意的请求头和HttpMethod,但是不会启用allowedCredentials,因为会创建一个信任级别,并公开用户的一些敏感信息。不过,我们可以使用maxAge属性来设置有效期,默认是30分钟。@CrossOrigin也可以被标注在类上:
@CrossOrigin(origins = "http://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(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
- 全局配置
通常情况下,全局配置的CORS也支持全部Origin,任意的请求头,但是只支持GET、HEAD和POST请求。同样不会默认启用allowedCredentials,并且默认有效期是30分钟。
我们看一下具体怎么配置,先来看看怎么使用java bean来配置:
@Configuration
public class WebMvcAdapter implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/mvc/**")
.allowedHeaders("Content-Type")
.allowedMethods("PUT", "POST", "GET", "DELETE")
.allowedOrigins("*")
.allowCredentials(true)
.maxAge(36000);
}
}
那么相应的XML配置就可以这么来写:
<mvc:cors>
<mvc:mapping path="/mvc/**"
allowed-origins="*"
allowed-methods="GET, PUT, POST, DELETE"
allowed-headers="Content-Type"
exposed-headers="header1, header2"
allow-credentials="true"
max-age="36000" />
<mvc:mapping path="/resources/**"
allowed-origins="http://domain1.com" />
</mvc:cors>
除了上述两种方法外,还可以通过配置CORS过滤器来达到跨域效果:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors().and()
...
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
这个配置要在Spring Security中进行,后面整理到安全相关的内容时详细介绍。
也可以在SpringMVC中这样配置:
/**
* 跨域过滤器
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/app/**", buildConfig());
source.registerCorsConfiguration("/api/**", buildConfig());
return new CorsFilter(source);
}
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
2. 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();
也可以在Controller里直接设置:
@GetMapping("/getUserInfo")
public ResponseEntity<UserInfo> getUserInfo() {
UserInfo userInfo = new UserInfo();
// set userInfo ...
return ResponseEntity
.ok()
.cacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS))
.eTag(UUID.randomUUID().toString())
.body(userInfo);
}
3. Enable MVC Configuration
@EnableWebMvc注解可以启用MVC配置:
@Configuration
@EnableWebMvc
public class WebConfig{
}
上面的配置就等同于:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven/>
</beans>
4. MVC Config API
我们可以实现WebMvcConfigurer接口:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
// Implement configuration methods...
}
为什么要实现这个接口呢?我们看一下这个接口的javadoc:
/**
* Defines callback methods to customize the Java-based configuration for
* Spring MVC enabled via {@code @EnableWebMvc}.
*
* <p>{@code @EnableWebMvc}-annotated configuration classes may implement
* this interface to be called back and given a chance to customize the
* default configuration.
*
* @author Rossen Stoyanchev
* @author Keith Donald
* @author David Syer
* @since 3.1
*/
public interface WebMvcConfigurer {
...
}
简单说就是,这个接口定义了回调方法,为通过@EnableWebMvc启用的SpringMVC定制基于java的配置。可以理解为,只有实现了这个接口中的方法,我们才能自定义SpringMVC的配置。否则,就是使用默认配置。
5. Type Conversion
默认情况下,会安装数字和日期类型的格式化程序,包括对@NumberFormat和@DateTimeFormat注释的支持。如果类路径上存在Joda-time,则还会安装对Joda-time格式库的支持。
在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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://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>
6. Validation
默认情况下,如果类路径上存在Bean验证(例如,Hibernate Validator),则LocalValidatorFactoryBean注册为全局Validator,以便与Controller中标注@Valid和Valified的方法参数一起使用。
在Java配置中,可以自定义全局Validator实例,如下例所示:
@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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven validator="globalValidator"/>
</beans>
也可以通过@InitBinder来注册验证器:
@Controller
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new FooValidator());
}
}
7. Interceptors
添加拦截器,看代码:
@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>
8. Content Types
可以配置Spring MVC请求的媒体类型,默认先检查url path的后缀扩展名,其中与json、xml、atom和rss相关的,直接注册为相应的媒体类型,然后再检查Accept头部信息。
@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>
9. Message Converters
可以通过覆盖configureMessageConverters()(替换Spring MVC创建的默认转换器)或覆盖extendMessageConverters()(自定义默认转换器或向默认转换器添加其他转换器)来自定义Java配置中的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()));
}
}
等同于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"/>
10. View Controller
SpringMVC提供了ViewController功能,可以简化单纯的页面跳转的处理过程。我们先来看下常规的页面跳转:
@Controller
public class Simple {
@RequestMapping("/toIndex")
public String index() {
return "index";
}
}
上述代码中,toIndex请求只是为了单纯的页面跳转,而如果一个工程中这种跳转的需求很多的话,就会出现很多重复的代码,我们可以用ViewController来简化处理:
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/toIndex").setViewName("index");
}
}
也可以使用xml来配置:
<mvc:view-controller path="/" view-name="home"/>
11. View Resolvers
Spring MVC简化了视图解析器的配置过程,可以按照下面的方式来配置:
@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标记和脚本模板也需要配置底层视图技术。
@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;
}
}
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:freemarker cache="false"/>
</mvc:view-resolvers>
<mvc:freemarker-configurer>
<mvc:template-loader-path location="/freemarker"/>
</mvc:freemarker-configurer>
12. Static Resource
当需要访问静态资源时,可以按照下面的写法:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCachePeriod(31556926)
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
上面的写法等同于xml:
<mvc:resources mapping="/resources/**" location="/public/">
<mvc:resource-chain>
<mvc:resource-cache/>
<mvc:resolvers>
<mvc:version-resolver>
<mvc:content-version-strategy patterns="/**"/>
</mvc:version-resolver>
</mvc:resolvers>
</mvc:resource-chain>
</mvc:resources>
13. DefaultServlet
spring MVC允许将DispatcherServlet映射到/(从而覆盖容器的默认servlet的映射),同时仍然允许容器的默认servlet处理静态资源请求。它配置了一个DefaultServletHttpRequestHandler,其URL映射为/*,并且相对于其他URL映射的优先级最低。
此处理程序将所有请求转发到默认servlet。因此,它必须保持在所有其他URL HandlerMappings顺序的最后。如果使用<mvc:annotation-Driven>,就会出现这种情况。或者,如果您设置了自己的自定义HandlerMapping实例,请确保将其Order属性设置为低于DefaultServletHttpRequestHandler的值,即Integer.MAX_VALUE。
以下示例显示如何使用默认设置启用该功能:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
xml:
<mvc:default-servlet-handler/>
也可以指定具体某个servlet:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable("MyServlet");
}
}
xml:
<mvc:default-servlet-handler default-servlet-name="MyServlet"/>
14. Patch Matching
可以自定义与URL的路径匹配和处理相关的选项。
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@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"/>
15. Advanced Java Config
@EnableWebMvc会导入DelegatingWebMvcConfiguration:
- 为Spring MVC应用程序提供默认Spring配置。
- 检测并委派WebMvcConfigurer实现来自定义该配置。
对于高级模式,您可以删除@EnableWebMvc并直接从DelegatingWebMvcConfiguration扩展,而不是实现WebMvcConfigurer,如下例所示:
@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
// ...
}
可参照@EnableWebMvc的原理
以及Spring MVC 代理配置类 DelegatingWebMvcConfiguration

浙公网安备 33010602011771号