文档章节

Spring-MVC配置和扩展

x
 xxm1995
发布于 07/12 11:05
字数 3693
阅读 5
收藏 0

一、外部化配置(WebMvcConfigurer)

简绍

​ 对Spring MVC的默认行为进行变更,或者进行扩展,通常要通过WebMvcConfigurer进行配置。它定义了很多课供重写的方法,通过对方法进行重写,就可以实现我们想要的效果。

​ 在Spring Boot 2.x之前,WebMvcConfigurerAdapterWebMvcConfigurer的实现抽象类,可以通过继承WebMvcConfigurerAdapter重写它的方法进行配置。到SpringBoot2.x时代的时候, 被标记为了@Deprecated,通过实现WebMvcConfigurer接口,通过重写默认方法来进行配置。总体来说,这两种配置方式基本相同。

主要配置说明

addInterceptors注册拦截器

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(sleuthInterceptor);
        registry.addInterceptor(idempotencyOperationInterceptor)
            // 拦截所有请求    
            .addPathPatterns("/**")
            // 排除掉查询类的 POST 请求
            .excludePathPatterns("/**/search/**","xx");
}

​ 此方法用来专门注册拦截器(Interceptor),通过registry#addInterceptor进行拦截器的注册,拦截器必须是HandlerInterceptor的子类,在下面在 《对SpringMVC进行扩展 》会进行详细的说明。

addArgumentResolvers添加参数解析器

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
	resolvers.add(new PropertiesHandlerMethodArgumentResolver());
}

​ 此方法可以用来添加参数解析器(argumentResolver),通过resolvers#add进行添加参数解析器。注意,此处添加的Resolver优先级会低于系统内建的Resolver,如果想添加优先级高于内建的Resolver,可以通过requestMappingHandlerAdapter#setArgumentResolvers方法进行覆盖,在下面在 《对SpringMVC进行扩展 》会进行详细的说明。

addReturnValueHandlers添加返回值处理程序

@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {

}

​ 这个是用来配置,注意,这个配置和 addArgumentResolvers添加参数解析器 的配置类似,添加的自定义Handler优先级会低于系统内建的Handler,如果想添加优先级高于内建的Handler,需要通过requestMappingHandlerAdapter#setReturnValueHandlers方法进行覆盖,在下面在《对SpringMVC进行扩展 》会进行详细的说明。

configureMessageConverters配置消息转换器

@Override 
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
     MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = jsonConverter.getObjectMapper();
        // 解决 18位 Long 类型转换成 JSON 时造成的数据丢失
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        converters.add(jsonConverter);
}

​ 消息转换器可以对接收或返回的消息进行转换,比如解决中文乱码、json中Long精度丢失等,我们可以使用系统提供的消息转换器,或我们自定义转换器,通过这个方法converters#add进行注册使用,会把这里添加的converter依次放在最高优先级(List的头部)。有多个自定义的converter时,可以改变相互之间的顺序,但是都在内置的converter前面。

这个配置的使用场景比较常见,在下面在《对SpringMVC进行扩展 》会进行详细的说明。

extendMessageConverters扩展消息转换器

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
     MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = jsonConverter.getObjectMapper();
        // 解决 18位 Long 类型转换成 JSON 时造成的数据丢失
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        converters.add(jsonConverter);riverroad
}

​ 这个与上个 configureMessageConverters 类似,不同点在于这个方法在 configureMessageConverters 之后运行,这时系统内置的converter已经添加完毕,此时我们同样可以可以通过改变converters列表中的converter实现处理顺序的变更。

addFormatters格式化配置

@Override
public void addFormatters(FormatterRegistry registry) {
}

​ 类型转换器和格式化器,部分功能和消息转换器相似。不同它的源类型必须是一个String, 目标类型是java类型。在下面在《对SpringMVC进行扩展 》 会进行说明。

addCorsMappings设置跨域

@Override
public void addCorsMappings(CorsRegistry registry) {
     registry.addMapping("/**")
            .allowedOrigins("*")
            .allowCredentials(true)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .maxAge(3600);
}

这个是关于跨域问题的设置

addResourceHandlers自定义资源映射

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //将请求映射到指定的位置
    registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
}

可以设置静态资源的映射,这个在目前开发中用的不多。

configurePathMatch配置路径匹配

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    // 表示不区分URL的最后一个字符是否是斜杠/
    configurer.setUseSuffixPatternMatch(true);
}

​ 让开发人员可以根据需求定制URL路径的匹配规则,常用的setUseSuffixPatternMatch方法,用来设置使用后缀模式匹配的方式,比如设置为true的话(默认为true),URL后面加个斜杠并不影响路径访问,例如“/user”等同于“/user/。如果需要定制path匹配发生的过程,可以提供自己定制的PathMatcherUrlPathHelper,但是这种需求不常见。

configureViewResolvers视图解析器

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
    InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        //请求视图文件的前缀地址
        internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
        //请求视图文件的后缀
        internalResourceViewResolver.setSuffix(".jsp");
    	registry.viewResolver(internalResourceViewResolver);
}

​ 这个对我们来说很熟悉,配置html、Jsp页面视图时就会用到InternalResourceViewResolver配置类,然后设置preffixsuffix参数进行配置视图文件路径前缀与后缀,不过现在目前开发中开发全部转向了前后端分离方式,这个已经配置几乎不会在遇到了。

二,对SpringMVC进行扩展

简绍

Spring MVC提供众多的常用的功能,基本上能满足我们日常的使用,但有时候我们会有特殊的需求,而Spring MVC没有默认的支持,此时就是需要我们对它进行扩展,下面就对Spring MVC常见的扩展方式进下介绍说明。

扩展点说明:

  1. HandlerMapping(处理请求映射)

    处理请求的映射。保存请求URL到具体的方法的映射关系,我们可以编写任意的HandlerMapping实现类,依据任何策略来决定一个web请求到 HandlerExecutionChain 对象的生成。通常我们不需要对他进行扩展。

  2. HandlerAdapter(处理适配器)

    真正调用Controller的地方,其实就是适配各种Controller。HandlerAdapter就是你可以提供自己的实现类来处理handler对象,我们一般不会对他进行扩展。

  3. HandlerInterceptor (接口拦截器)

    通过自定义拦截器,我们可以在一个请求被真正处理之前、请求被处理但还没输出到响应中、请求已经被输出到响应中之后这三个时间点去做任何我们想要做的事情,这个是我们在Spring MVC中用到最多的一种扩展方式。

  4. HandlerMethodArgumentResolver(处理方法参数解释器)

    接收到请求参数的时候,会通过它的不同实现类对参数进行处理,通过对它进行扩展,能让我们实现对参数进行自定义操作,之前超哥写过一个自定注入Header参数到接收类的参数解释器,这个是我们对Spring MVC常用的扩展方式之一。

  5. HandlerMethodReturnValueHandler(处理方法返回值处理器)

    程序方法运行结束后的返回值进行处理,转换成我们所需要的格式写入返回请求。

  6. Converter(类型转换器)

    对数据类型进行转换,主要是用到的是 HttpMessageConverter( http消息转换器),用来对请求和响应的数据进行处理。

  7. Formatter(格式化器)

    对接收到的参数进行处理和格式化,只能应用于输入为String类型的数据。

  8. ViewResolver(视图解析器)

    完成从ModelAndView到真正的视图的过程,ViewResolver接口是在DispatcherServlet中进行调用的,当DispatcherServlet调用完Controller后,会得到一个ModelAndView对象,然后DispatcherServlet会调用render方法进行视图渲染。在目前前后端分离的情况下,这个我们一般不会进行扩展。

  9. HandlerExceptionResolver(异常处理)

    用不到,略

HandlerInterceptor (接口拦截器)

HandlerInterceptor 是一个接口,用过实现这个接口,我们可以实现出一个自定义的拦截器,这个接口有三个方法,如下图所示:

public interface HandlerInterceptor {
    //在业务处理器处理请求之前被调用,其返回值表示是否中断后续操作
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception{
    }
    // 在业务处理器处理请求完成之后,生成视图之前执行
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception{
    }
     // 在DispatcherServlet完全处理完请求之后被调用,可用于清理资源,日志打印
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception{
    }
}

最后将拦截器通过WebMvcConfigurer配置类中的 addInterceptors 方法进行注册,即可生效。

HandlerMethodArgumentResolver(处理方法参数解释器)

​ 处理方法参数解释器可以对方法参数进行处理,通过它可以获取该方法参数上的一些信息 ,如方法参数中的注解信息等,根据获取到的信息对参数数据进行处理。通过实现这个接口或继承它的实现类,就可以实现一个自定义的处理方法参数解释器,然后将Resolver通过WebMvcConfigurer配置类中的 addArgumentResolvers 方法进行注册,即可生效。

package org.springframework.web.method.support;
public interface HandlerMethodArgumentResolver {
	//判断是否使用这个组件
	boolean supportsParameter(MethodParameter parameter);
	//对参数进行处理
	Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

​ 在Spring MVC中,系统已经提供了多种不同用处的 处理方法参数解释器 ,当有请求到来时,系统首先会从系统提供的解释器中寻找合适的Resolver,如果匹配到,才会查找注册的自定义实现Resolver,所以通常我们要创建自定义注解放在要处理的参数上,方便使用自定义的Resolver进行处理。

​ 选择 Resolver 进行处理的流程:

// class:HandlerMethodArgumentResolverComposite
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    //先从缓存中查找
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    // 未找到默认的Resolver
    if (result == null) {
        //遍历Resolver
        for (HandlerMethodArgumentResolver methodArgumentResolver : 		this.argumentResolvers) {
            //判断是否支持此Resolver
            if (methodArgumentResolver.supportsParameter(parameter)) {
                result = methodArgumentResolver;
                //写入缓存
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
}

​ 如果我们确实需要配置高于系统内置的自定义Resolver的时候,可以通过如下的方式进行配置:

​ 在WebConfig配置类中,通过@PostConstruct注解在一个方法上,可以让这个方法在Bean依赖注入完成后被自动调用,然后在这个方法里对 Resolver 集合进行重新设置,就可以实现将自定义 Resolver 优先级提升到内置的之前了。

@Configuration
public class RestWebMvcConfigurer implements WebMvcConfigurer {

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @PostConstruct
    public void init() {
        // 获取当前 RequestMappingHandlerAdapter 所有的 Resolver 对象
        List<HandlerMethodArgumentResolver> resolvers = 
            requestMappingHandlerAdapter.getArgumentResolvers();
        List<HandlerMethodArgumentResolver> newResolvers = 
            new ArrayList<>(resolvers.size() + 1);
        // 添加 PropertiesHandlerMethodArgumentResolver 到集合首位
        newResolvers.add(new PropertiesHandlerMethodArgumentResolver());
        // 添加 已注册的 Resolver 对象集合
        newResolvers.addAll(resolvers);
        // 重新设置 Resolver 对象集合
        requestMappingHandlerAdapter.setArgumentResolvers(newResolvers);
    }
}

题外话:可以通过实现自定义自动把Tid一类信息注入到参数里,下面用一个简单的demo进行展示:

按需把请求头参数写入方法参数的Spring MVC扩展处理器

HandlerMethodReturnValueHandler(处理方法返回值处理程序)

HandlerMethodReturnValueHandler可以用来对程序方法运行结束后的返回值进行处理,转换成我们所需要的格式并返回。

package org.springframework.web.method.support;

public interface HandlerMethodReturnValueHandler {
	//检验是否支持本处理器处理
	boolean supportsReturnType(MethodParameter returnType);
	//具体处理方法
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) 
        throws Exception;
}

​ 与HandlerMethodArgumentResolver一样,系统已经提供了多种不同的 Handler 了,自定义的Handler 添加进去优先级都会在内置Handler 之后。常规配置的方式通过 WebConfig 进行配置,如果要配置高于系统内置的自定义 Handler 时,可以参照下方的配置方式,与参数解释器的配置方式基本相同。

@Configuration
public class RestWebMvcConfigurer implements WebMvcConfigurer {

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @PostConstruct
    public void init() {
        // 获取当前 HandlerMethodReturnValueHandler 所有的 Handler 对象
        List<HandlerMethodReturnValueHandler> handlers = 
            requestMappingHandlerAdapter.getReturnValueHandlers();
        List<HandlerMethodReturnValueHandler> newHandlers = 
            new ArrayList<>(handlers.size() + 1);
        // 添加 PropertiesHandlerMethodReturnValueHandler 到集合首位
        newHandlers.add(new PropertiesHandlerMethodReturnValueHandler());
        // 添加 已注册的 Handler 对象集合
        newHandlers.addAll(handlers);
        // 重新设置 Handler 对象集合
        requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);
    }
}

​ 选择 Handler 进行处理的流程:

// class: HandlerMethodReturnValueHandlerComposite
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
    //遍历Handler
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        //找到并返回
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

HttpMessageConverter(Http消息转换器)

HttpMessageConveter 是用来处理请求和响应数据的,我们经常会用到@RequestBody@ResponseBody,通过这两个注解,可以在 Controller 中直接使用 Java 对象作为请求参数和返回内容,而完成这之间转换作用的便是HttpMessageConverter。Spring 为我们内置了大量的HttpMessageConverter,例如, MappingJackson2HttpMessageConverterStringHttpMessageConverter 等。

已包含常用的消息转换器:

名称作用读支持MediaType写支持MediaType
MappingJackson2HttpMessageConverter使用Jackson的ObjectMapper转换Json数据application/jsonapplication/json
StringHttpMessageConverter数据与String类型的相互转换text/*text/plain
ByteArrayHttpMessageConverter数据与字节数组的相互转换/application/octet-stream

​ 下面是HttpMessageConverter 接口,实现Http消息转换就必须实现这个接口,不过我们通常不会直接实现这个接口,而是通过继承它的子类进行扩展处理,默认提供的Converter对视通过继承它的抽象类进行扩展。

public interface HttpMessageConverter<T> {
	//判断是否对接收请求进行处理
	boolean canRead(Class<?> clazz, MediaType mediaType);
	//判断是否对响应进行处理
	boolean canWrite(Class<?> clazz, MediaType mediaType);
	//返回此转换器支持的MediaType对象列表
	List<MediaType> getSupportedMediaTypes();
	//对接收的请求进行处理
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
	//对响应进行处理
	void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}

​ 通常被继承的抽象类是AbstractHttpMessageConverter,例如常用的MappingJackson2HttpMessageConverter就是对它的继承,这样能减少编写处理方法的工作量,如果我们要进行扩展,通常会在系统已有Converter上进行配置,少数时候会进行继承扩展。

​ 配置方式见上方 configureMessageConverters 配置消息转换器和 extendMessageConverters 扩展消息转换器的说明。

​ 注:目前在目前开发中的开发中,对请求的消息和响应进行完全自定义的场合不多,多数都是对已有的MessageConverter进行设置和增强。

Formatter(格式化器)

FormatterConverter类似, 是将一种类型转换成另一种类型, 但是, Formatter的源类型必须是一个String, 目标类型是java类型。在SpringMVC中,处理的多数输入都是文本方式的输入,因此, 选择Formatter比选择Converter更合适。

Formatter接口的结构如下:

package org.springframework.format;
public interface Formatter<T> extends Printer<T>, Parser<T> {
    
}
public interface Printer<T> {
    String print(T var1, Locale var2);
}
public interface Parser<T> {
    T parse(String var1, Locale var2) throws ParseException;
}

​ 这里的T表示输入字符串要转换的目标类型。parse方法利用指定的Locale将一个String解析成目标类型。print方法相反,它是返回目标对象的字符串表示法。

​ 配置方式见上方addFormatters

© 著作权归作者所有

x
粉丝 0
博文 24
码字总数 12971
作品 0
菏泽
程序员
私信 提问
spring boot与spring mvc的区别是什么?

spring boot与spring mvc的区别是什么? 转载:https://blog.csdn.net/u014590757/article/details/79602309 spring boot只是一个配置工具,整合工具,辅助工具. springmvc是框架,项目中实际运...

Elsa晓冰
2018/09/29
0
0
spring MVC工作机制与设计模式-读后小结(一)

昨天买了本淘宝大牛写的书-《深入分析java web 技术内幕》,从今天开始,记录下自己的总结。 由于本人是使用spring MVC开发的,所有就先看了下spring MVC 工作机制与设计模式这章,感觉看了之...

bosscheng
2013/05/09
3.8K
1
Spring4 + Spring MVC + MyBatis 整合思路

原文出处:斯武丶风晴 1、Spring框架的搭建 这个很简单,只需要web容器中注册org.springframework.web.context.ContextLoaderListener,并指定spring加载配置文件,那么spring容器搭建完成。...

斯武丶风晴
2017/09/21
0
0
Spring Boot中如何扩展XML请求和响应的支持

在之前的所有Spring Boot教程中,我们都只提到和用到了针对HTML和JSON格式的请求与响应处理。那么对于XML格式的请求要如何快速的在Controller中包装成对象,以及如何以XML的格式返回一个对象...

翟永超
2018/09/21
0
0
系列一、SpringMVC概念及测试案例

三层架构和MVC概念 一.、三层架构概念 咱们开发服务器端程序,一般都基于两种形式,一种C/S架构程序,一种B/S架构程序,使用Java语言基本上都是开发B/S架构的程序,B/S架构又分成了三层架构 ...

我叫小糖主
05/27
19
0

没有更多内容

加载失败,请刷新页面

加载更多

rime设置为默认简体

转载 https://github.com/ModerRAS/ModerRAS.github.io/blob/master/_posts/2018-11-07-rime%E8%AE%BE%E7%BD%AE%E4%B8%BA%E9%BB%98%E8%AE%A4%E7%AE%80%E4%BD%93.md 写在开始 我的Arch Linux上......

zhenruyan
今天
5
0
简述TCP的流量控制与拥塞控制

1. TCP流量控制 流量控制就是让发送方的发送速率不要太快,要让接收方来的及接收。 原理是通过确认报文中窗口字段来控制发送方的发送速率,发送方的发送窗口大小不能超过接收方给出窗口大小。...

鏡花水月
今天
10
0
OSChina 周日乱弹 —— 别问,问就是没空

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享容祖儿/彭羚的单曲《心淡》: 《心淡》- 容祖儿/彭羚 手机党少年们想听歌,请使劲儿戳(这里) @wqp0010 :周...

小小编辑
今天
1K
11
golang微服务框架go-micro 入门笔记2.1 micro工具之micro api

micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.shtml?id=6 本文全部代码https://idea.techidea8....

非正式解决方案
今天
5
0
Spring Context 你真的懂了吗

今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。 1. context 是什么 我们经常在编程中见到 context 这个单词,当...

Java知其所以然
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部