最近在读springBoot实战的书,然而读者却有点费力,一方面是他竟然用了kotlin我相对来说不太了解这个语言,但是也是把书给慢的啃下了,这里做一个简单地记录。其中所介绍内容与书中一致,尚未加入一些自己的想法。
Web MVC配置简介
Spring MVC为我们提供了一个WebMvcConfigurationSupport类和一个注解@Enable-WebMvc以帮助我们减少配置Bean的声明。WebMvcConfigurationSupport提供的常用配置方法如表8-1所示。
表8-1 WebMvcConfigurationSupport中常用的配置方法
另外,WebMvcConfigurerAdapter也可以配置MVC。WebMvcConfigurerAdapter是一个实现了WebMvcConfigurer接口的抽象类。对于这两种方式,Spring Boot都提供了默认的配置类,如果想具体了解配置可以去阅读一下源码。
下面我们简单说明一下如何自定义一个继承自WebMvcConfigurationSupport的配置类WebMvcConfig。
首先,使用@Configuration将WebMvcConfig类标注为Spring配置类,代码如下:
@Configuration
public class WebMvcConfig: WebMvcConfigurationSupport() {
//覆盖写配置方法
…
}
然后,通过重写以下配置方法:addCorsMappings()、addFormatters()、addInterceptors()、addViewControllers()实现自定义的配置。
当我们使用Java Config方式来启用Spring MVC配置,还需要启用WebMvc的配置,我们可以将@EnableWebMvc添加到SpringBootApplication入口类上,代码如下:
@SpringBootApplication
@ServletComponentScan(basePackages = ["com.easy.springboot.demo_spring_mvc.filter"])
@EnableAutoConfiguration(exclude = [ErrorMvcAutoConfiguration::class])
@EnableWebMvc // 启用Spring MVC配置
open class DemoSpringMvcApplication
fun main(args: Array) {
SpringApplicationBuilder().initializers(
beans {
bean {
ApplicationRunner {
initUser()
initCategory()
}
}
}
).sources(DemoSpringMvcApplication::class.java).run(*args)
}
这样我们就可以实现自定义的WebMvc配置了。
下面我们来具体介绍一下WebMvc配置中的静态资源、拦截器、跨域、视图控制器、消息转换器、数据格式化器、视图解析器等配置方法。
8.1.1 静态资源配置
Spring Boot中默认的静态资源配置,是把类路径下的/static、/public、/resources和/META-INF/resources文件夹的静态文件直接映射为/**。我们可以通过覆盖写addResourceHandlers来定制静态资源路径映射,使用注册类ResourceHandlerRegistry添加相应的ResourceHandler。示例代码如下:
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
super.addResourceHandlers(registry)
registry.addResourceHandler("/app/**")
.addResourceLocations("classpath:/static/app/")
registry.addResourceHandler("/bower_components/**")
.addResourceLocations("classpath:/static/bower_components/")
}
上面的代码把来自浏览器的静态资源请求/app/**、/bower_components/**映射到Web工程中的类路径下面的/static/app/、/static/bower_components/目录。
override fun addInterceptors(registry: InterceptorRegistry) {
super.addInterceptors(registry)
//注册自定义拦截器,添加拦截路径和排除拦截路径
registry.addInterceptor(loginSessionHandlerInterceptor).
addPathPatterns("/**").
excludePathPatterns(
"/index",
"/login",
"/doLogin",
"/logout",
"/register",
"/doRegister",
"/**/*.js",
"/**/*.css",
"/**/*.css.map",
"/**/*.jpeg",
"/**/*.ico",
"/**/*.jpg",
"/**/*.png",
"/**/*.woff",
"/**/*.woff2"
)
}
默认拦截所有请求/**,白名单在excludePathPatterns中配置。其中,loginSessionHan-dlerInterceptor是实现了HandlerInterceptor的拦截器。完整代码参考示例工程LoginSession-HandlerInterceptor.kt。
8.1.3 跨域配置
通过重写addCorsMappings方法实现跨域配置的支持,使用CorsRegistry注册类添加路径映射,代码示例如下:
override fun addCorsMappings(registry: CorsRegistry) {
super.addCorsMappings(registry)
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("PUT, POST, GET, DELETE, OPTIONS")
.allowedHeaders("*")
}
其中
❑addMapping:配置允许跨域的路径。
❑allowedMethods:配置允许访问该跨域资源服务器的请求方法,如:POST、GET、PUT、DELETE等。
❑allowedOrigins:配置允许访问的跨域资源的请求域名。
❑allowedHeaders:配置允许请求header的访问,如:X-TOKEN。
8.1.4 视图控制器配置
通过重写addViewControllers方法,使用ViewControllerRegistry实现视图控制器配置,代码示例如下:
override fun addViewControllers(registry: ViewControllerRegistry) {
super.addViewControllers(registry)
registry.addViewController("/").setViewName("/index")
registry.addViewController("/index").setViewName("/index")
registry.addViewController("/about").setViewName("/about")
registry.addViewController("/error/403").setViewName("/error/403")
registry.addViewController("/error/500").setViewName("/error/500")
}
上面的代码实现与下面的代码在逻辑上是一样的:
@GetMapping(value = ["/", "/index"])
fun index(): String {
return "index"
}
@GetMapping(value = ["/about"])
fun about(): String {
return "about"
}
@GetMapping(value = ["/error/403"])
fun error_403(): String {
return "error/403"
}
@GetMapping(value = ["/error/500"])
fun error_500(): String {
return "error/500"
}
8.1.5 消息转换器配置
HttpMessageConverters(消息转换器)是在HttpMessageConvertersAutoConfiguration类中自动注册的。与HttpMessageConverters(消息转换器)相关的类如下:
StringHttpMessageConverter是Spring Boot默认自动配置的HttpMessageConverter。除了默认的StringHttpMessageConverter,在HttpMessageConvertersAutoConfiguration的自动配置类里还使用了@Import注解,引入了JacksonHttpMessageConvertersConfiguration和Gs-onHttpMessageConverterConfiguration。自动配置的逻辑如下:
1)若jackson的jar包在类路径上,则Spring Boot通过JacksonHttpMessageConverterConfiguration增加MappingJackson2HttpMessage Converter和MappingJackson2XmlHttpMessageConverter。
2)若gson的jar包在类路径上,则Spring Boot通过GsonHttpMessageConverterConfiguration增加GsonHttpMessageConverter。
在Spring Boot中,如果要新增自定义的HttpMessageConverter,则只需定义一个自己的HttpMessageConverters的Bean,然后在此Bean中注册自定义HttpMessageConverter即可。通过覆盖重写configureMessageConverters方法来配置消息转换器。代码示例如下:
override fun configureMessageConverters(converters: MutableList Converter<*>>) { super.configureMessageConverters(converters) //创建fastjson消息转换器: FastJsonHttpMessageConverter val fastConverter = FastJsonHttpMessageConverter() //创建FastJsonConfig配置类 val fastJsonConfig = FastJsonConfig() //定制过滤JSON返回 fastJsonConfig.setSerializerFeatures( SerializerFeature.WriteNullNumberAsZero, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty ) fastConverter.setFastJsonConfig(fastJsonConfig) //将fastConverter添加到视图消息转换器列表内 converters.add(fastConverter) } 8.1.6 数据格式化器配置 通过覆盖重写addFormatters方法来添加数据格式化器。Spring MVC接收HTTP请求会把请求参数自动绑定映射到Controller请求参数上。Spring中没有默认配置将字符串转换为日期类型。这个时候,可以通过添加一个DateFormatter类来实现自动转换。 例如,把来自Controller请求参数中String类型的日期数据格式化为Date日期类型,代码如下: override fun addFormatters(registry: FormatterRegistry) { super.addFormatters(registry) registry.addFormatter(DateFormatter("yyyy-MM-dd")) } 8.1.7 视图解析器配置 Spring MVC提供了一个特殊的视图解析器协调类ContentNegotiatingViewResolver,它通过代理给不同的ViewResolver来处理不同的视图。通过覆盖重写configureView-Resolvers()方法来配置视图解析器。下面我们来配置一个FreeMarker的视图解析器,代码如下: override fun configureViewResolvers(registry: ViewResolverRegistry) { super.configureViewResolvers(registry) registry.viewResolver(freeMarkerViewResolver()) } 其中,freeMarkerViewResolver()是FreeMarker视图解析器配置,代码如下: /** * * FreeMarker视图解析器配置 * 配置了@Bean注解,该注解会将方法返回值加入Spring Ioc容器。 * @return */ @Bean open fun freeMarkerViewResolver(): FreeMarkerViewResolver { val viewResolver = FreeMarkerViewResolver() // freemarker本身配置了templateLoaderPath而在viewResolver中不需要配置prefix,且路 径前缀必须配置在templateLoaderPath中 viewResolver.setPrefix("") viewResolver.setSuffix(".ftl") viewResolver.isCache = false viewResolver.setContentType("text/html; charset=UTF-8") viewResolver.setRequestContextAttribute("requestContext") // 为模板调用时,调用request对象的变量名 viewResolver.order = 0 viewResolver.setExposeRequestAttributes(true); viewResolver.setExposeSessionAttributes(true); return viewResolver } 另外,我们还需要声明一个FreeMarkerConfigurer Bean,这个类是FreeMarker配置类。我们还可以在这里添加自定义的内置变量,以便在前端ftl模板代码中使用。代码如下:@Bean
open fun freemarkerConfig(): FreeMarkerConfigurer {
val freemarkerConfig = FreeMarkerConfigurer()
freemarkerConfig.setDefaultEncoding("UTF-8")
freemarkerConfig.setTemplateLoaderPath("classpath:/templates/")
var configuration: Configuration? = null
try {
configuration = freemarkerConfig.createConfiguration()
configuration.defaultEncoding = "UTF-8"
} catch (e: IOException) {
log.error("freemarker配置bean, IO异常: {}", e)
} catch (e: TemplateException) {
log.error("freemarker配置bean, TemplateException异常: {}", e)
}
val freemarkerVars = mutableMapOf