Spring MVC核心擴(kuò)展點(diǎn)及使用技巧總結(jié)
環(huán)境:SpringBoot2.7.12
1. 啟用Spring MVC功能
@Configuration
@EnableWebMvc
public class WebConfig {
}
2. 類型轉(zhuǎn)換配置
如需要自定義數(shù)據(jù)類型的轉(zhuǎn)換,可以通過(guò)如下方式注冊(cè)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new ConverterFactory<String, Number>() {
@Override
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
return new Converter<String, T>() {
public T convert(String source) {
return (T) Integer.valueOf(source) ;
}
} ;
}
});
}
}
以上添加了從String到Integer的轉(zhuǎn)換(這里只是舉例,系統(tǒng)默認(rèn)已經(jīng)有了從String到Number的轉(zhuǎn)換器)。每種轉(zhuǎn)換器最終被包裝成ConvertersForPair對(duì)象,該對(duì)象中有個(gè)隊(duì)列保存了所有的轉(zhuǎn)換器。后添加的添加到首位,如下:
private static class ConvertersForPair {
private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();
public void add(GenericConverter converter) {
this.converters.addFirst(converter);
}
}
所有如你有自定義的轉(zhuǎn)換器,自定義的優(yōu)先級(jí)比系統(tǒng)自帶的要高。
3. 數(shù)據(jù)驗(yàn)證
默認(rèn)情況下,如果類路徑上存在 Bean Validation(例如 Hibernate Validator),則 LocalValidatorFactoryBean 會(huì)被注冊(cè)為全局 Validator,與控制器方法參數(shù)上的 @Valid 和 Validated 一起使用。
@Configuration
public class WebConfig implements WebMvcConfigurer {
public Validator getValidator() {
return new LocalValidatorFactoryBean();
}
}
4. 請(qǐng)求攔截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (request.getHeader("token") == null) {
return false ;
}
return true ;
}
}).addPathPatterns("/**") ;
}
}
上面配置了一個(gè)攔截任意請(qǐng)求的攔截器,在請(qǐng)求到達(dá)時(shí)會(huì)先驗(yàn)證請(qǐng)求header中token是否為null。
攔截器并不適合作為安全層,因?yàn)樗锌赡芘c控制器Controller路徑匹配不匹配,而Controller路徑匹配還可以透明地匹配尾部斜線和路徑擴(kuò)展名以及其他路徑匹配選項(xiàng)。其中許多選項(xiàng)已被棄用,但仍有可能出現(xiàn)不匹配。一般情況下,我們建議使用 Spring Security,它包含一個(gè)專用的 MvcRequestMatcher,可與 Spring MVC 路徑匹配保持一致,還具有安全防火墻,可阻止 URL 路徑中許多不需要的字符。
5. 請(qǐng)求內(nèi)容類型
自定義Spring MVC 如何從請(qǐng)求中確定所請(qǐng)求的媒體類型(例如,接受頭、URL 路徑擴(kuò)展、查詢參數(shù)等)。
默認(rèn)情況下,只選中"Accept" header。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// 這樣配置后,視圖技術(shù)就能夠根據(jù)你請(qǐng)求的Accept輸出指定的文件內(nèi)容了
configurer.mediaType("yaml", new MediaType("application", "yaml")) ;
}
}
上面的配置最終是對(duì)ContentNegotiationManager對(duì)象進(jìn)行添加MappingMediaTypeFileExtensionResolver文件擴(kuò)展解析器。
@Bean
public ContentNegotiationManager mvcContentNegotiationManager() {
if (this.contentNegotiationManager == null) {
ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext);
configurer.mediaTypes(getDefaultMediaTypes());
configureContentNegotiation(configurer);
this.contentNegotiationManager = configurer.buildContentNegotiationManager();
}
return this.contentNegotiationManager;
}
protected ContentNegotiationManager buildContentNegotiationManager() {
this.factory.addMediaTypes(this.mediaTypes);
return this.factory.build();
}
部分代碼
public class ContentNegotiationManagerFactoryBean {
public ContentNegotiationManager build() {
if (!CollectionUtils.isEmpty(this.mediaTypes) && !this.favorPathExtension && !this.favorParameter) {
this.contentNegotiationManager.addFileExtensionResolvers(
new MappingMediaTypeFileExtensionResolver(this.mediaTypes));
}
}
}
有了MappingMediaTypeFileExtensionResolver解析器后,還需要Controller接口返回ModelAndView對(duì)象。如下接口
@GetMapping("/contentType")
public ModelAndView contentType() {
return new ModelAndView("test") ;
}
在classpath下新建test.yaml文件,內(nèi)容隨意。有了這些還不夠,我們需要能夠解析處理*.yaml的文件。所以還需要視圖解析器
@Component
public class YamlViewResolver implements ViewResolver {
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!viewName.endsWith(".yaml")) {
return null ;
}
return new View() {
// 支持的類型
public String getContentType() {
return "application/yaml" ;
};
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
ClassPathResource resource = new ClassPathResource(viewName) ;
InputStream is = resource.getInputStream() ;
OutputStream outputStream = response.getOutputStream();
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = is.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush() ;
is.close();
outputStream.close() ;
}
} ;
}
}
有了這些我們配置Spring MVC才能正確的輸出我們所需要的文件內(nèi)容。這個(gè)功能是不是太麻煩了,沒撒用??。
6. 自定義消息轉(zhuǎn)換器
現(xiàn)希望將對(duì)象轉(zhuǎn)換為YAML個(gè)數(shù)的數(shù)據(jù)進(jìn)行輸出,我們可以配置自定義的HttpMessageConverter進(jìn)行轉(zhuǎn)換輸出。
public class YamlHttpMessageConverter implements HttpMessageConverter<Object> {
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return User.class.isAssignableFrom(clazz) ;
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return Arrays.asList(new MediaType("application", "yaml")) ;
}
@Override
public void write(Object t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
StreamUtils.copy(new org.yaml.snakeyaml.Yaml().dump(t), StandardCharsets.UTF_8, outputMessage.getBody()) ;
}
}
注冊(cè)上面的轉(zhuǎn)換器
@Configuration
public class WebConfig implements WebMvcConfigurer {
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 注意這里已定義指定位置,不然就被json輸出了
converters.add(0, new YamlHttpMessageConverter()) ;
}
}
測(cè)試接口
@GetMapping("/yaml")
public Object yaml() {
return new User(10, "zhangsan") ;
}
輸出結(jié)果
圖片
7. 視圖控制器
一種快捷定義視圖Controller接口的方式
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 當(dāng)訪問(wèn)/index時(shí)將直接輸出test視圖內(nèi)容
registry.addViewController("/index").setViewName("test") ;
}
}
這里為了簡(jiǎn)單直接使用BeanNameViewReolver技術(shù),自定義一個(gè)以test為名的View Bean對(duì)象
@Component("test")
public class PackView implements View {
@Override
public String getContentType() {
return "text/html" ;
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().print("View Controllers") ;
}
}
輸出
圖片
8. 視圖解析器
可以通過(guò)上面案例5中定義的YamlViewResolver注冊(cè)方式,也可以通過(guò)如下方式注冊(cè)
@Configuration
public class WebConfig implements WebMvcConfigurer {
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(new YamlViewResolver()) ;
}
}
這樣注冊(cè)的解析器,都會(huì)添加到ViewResolverComposite這個(gè)解析器集合中。
9. 靜態(tài)資源配置
一種從基于資源的位置列表中提供靜態(tài)資源的便捷方法。如下如果請(qǐng)求以 /resources 開頭,則會(huì)使用相對(duì)路徑查找并提供網(wǎng)絡(luò)應(yīng)用程序根目錄下 /public 或類路徑中 /static 下的靜態(tài)資源。
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/public", "classpath:/static/");
}
}
以上是本篇文章的所有內(nèi)容,希望對(duì)你有幫助。
完畢!?。?/p>