1. Externalized configuration (WebMvcConfigurer)
Brief introduction
To change or extend the default behavior of Spring MVC, usually through WebMvcConfigurer
configuration. It defines many classes for overriding methods, and by overriding the methods, we can achieve the effect we want.
Before Spring Boot 2.x, it WebMvcConfigurerAdapter
was WebMvcConfigurer
an abstract class that could be WebMvcConfigurerAdapter
configured by inheriting and overriding its methods. By the time of SpringBoot2.x, it was marked as @Deprecated
, by implementing the WebMvcConfigurer
interface, by overriding the default method for configuration. Overall, the two configurations are basically the same.
Main configuration instructions
addInterceptors to register interceptors
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(sleuthInterceptor);
registry.addInterceptor(idempotencyOperationInterceptor)
// 拦截所有请求
.addPathPatterns("/**")
// 排除掉查询类的 POST 请求
.excludePathPatterns("/**/search/**","xx");
}
This method is used to specifically register the interceptor (Interceptor). By registry#addInterceptor
registering the interceptor, the interceptor must be HandlerInterceptor
a subclass, which will be explained in detail in "Extending SpringMVC" below.
addArgumentResolvers add argument resolvers
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
resolvers.add(new PropertiesHandlerMethodArgumentResolver());
}
This method can be used to add an argument resolver (argumentResolver) by resolvers#add
adding an argument resolver. Note that the Resolver
priority added here will be lower than the built-in system Resolver
. If you want to add a priority higher than the built-in Resolver
, you can override it by requestMappingHandlerAdapter#setArgumentResolvers
method, which will be explained in detail in "Extending SpringMVC" below.
addReturnValueHandlers adds return value handlers
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
This is used for configuration. Note that this configuration is similar to the configuration of addArgumentResolvers
adding a parameter parser. The added custom Handler
priority will be lower than the built-in system Handler
. If you want to add a higher priority than the built-in Handler
, you need requestMappingHandlerAdapter#setReturnValueHandlers
to override it by method , which will be explained in detail in "Extending SpringMVC" below.
configureMessageConverters configure message converters
@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);
}
The message converter can convert the received or returned messages, such as solving Chinese garbled characters, loss of Long precision in json, etc. We can use the message converter provided by the system, or our custom converter, through this method converters#add
to register and use, The ones added here will converter
be placed in the highest priority (the head of the List). When there are multiple custom ones , you can change the order of each other, but they are all in front of converter
the built-in ones .converter
The usage scenarios of this configuration are relatively common, and will be described in detail in "Extending SpringMVC" below.
extendMessageConverters extends message converters
@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
}
This is configureMessageConverters
similar , the difference is that this method runs configureMessageConverters
after , and the built-in system converter
has been added. At this time, we can also change the processing order by changing converters
the implementation in the list .converter
addFormatters formatting configuration
@Override
public void addFormatters(FormatterRegistry registry) {
}
Type converters and formatters, some functions are similar to message converters. Unlike its source type must be one String
, the target type is a java type. It will be explained in "Extending SpringMVC" below.
addCorsMappings set cross domain
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
This is the setting for cross-domain issues
addResourceHandlers custom resource mapping
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//将请求映射到指定的位置
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
}
You can set the mapping of static resources, which is not used much in current development.
configurePathMatch configures path matching
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 表示不区分URL的最后一个字符是否是斜杠/
configurer.setUseSuffixPatternMatch(true);
}
Allows developers to customize the matching rules of URL paths according to their needs. Common setUseSuffixPatternMatch
methods are used to set the way to use suffix pattern matching. For example, if it is set to true (default is true), adding a slash after the URL does not affect path access. For example, "/user" is equivalent to "/user/. If you need to customize the process of path matching, you can provide your own customized PathMatcher
sum UrlPathHelper
, but this requirement is not common.
configureViewResolvers view resolvers
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
//请求视图文件的前缀地址
internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
//请求视图文件的后缀
internalResourceViewResolver.setSuffix(".jsp");
registry.viewResolver(internalResourceViewResolver);
}
This is very familiar to us. When configuring html and Jsp page views, you will use InternalResourceViewResolver
configuration classes, and then set preffix
and suffix
parameters to configure view file path prefixes and suffixes, but now all development in development has turned to front-end and back-end separation. This has been configured almost will not be encountered.
Second, extend SpringMVC
Brief introduction
Spring MVC provides many common functions, which can basically meet our daily use, but sometimes we have special needs, and Spring MVC does not have default support. At this time, we need to extend it. The common extension methods of MVC are described below.
Expansion point description:
-
HandlerMapping
(handling request mapping)The map that handles the request. To save the mapping relationship between the request URL and the specific method, we can write any
HandlerMapping
implementation class to determine the generation of a web request to anHandlerExecutionChain
object . Usually we don't need to extend it. -
HandlerAdapter
(handling adapter)The place to really call the Controller is actually to adapt to various Controllers. HandlerAdapter means that you can provide your own implementation class to handle handler objects, and we generally do not extend it.
-
HandlerInterceptor
(interface interceptor)With a custom interceptor, we can do anything we want at three time points before a request is actually processed, the request is processed but not output to the response, and the request has been output to the response. This is the extension method we use the most in Spring MVC.
-
HandlerMethodArgumentResolver
(processing method parameter interpreter)When the request parameters are received, the parameters will be processed through its different implementation classes. By extending it, we can implement custom operations on the parameters. Before, Brother Chao wrote a custom injection of Header parameters into the receiving class. The parameter interpreter, this is one of our common extension methods for Spring MVC.
-
HandlerMethodReturnValueHandler
(processing method return value handler)The return value after the program method runs is processed and converted into the format we need to write the return request.
-
Converter
(type converter)Converting data types is mainly used
HttpMessageConverter
(http message converter) to process request and response data. -
Formatter
(formatter)The processing and formatting of the received parameters can only be applied to the input data of type String.
-
ViewResolver
(view resolver)To complete
ModelAndView
the process from going to the real view, theViewResolver
interface isDispatcherServlet
called in the interface. WhenDispatcherServlet
the Controller is called, anModelAndView
object will be obtained, and thenDispatcherServlet
the render method will be called to render the view. In the current separation of front and back ends, we generally do not expand this. -
HandlerExceptionResolver
(exception handling)not used, slightly
HandlerInterceptor (interface interceptor)
HandlerInterceptor
It is an interface. After implementing this interface, we can implement a custom interceptor. This interface has three methods, as shown in the following figure:
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{
}
}
Finally, register the interceptor through WebMvcConfigurer
the addInterceptors
method to take effect.
HandlerMethodArgumentResolver (processing method parameter interpreter)
The processing method parameter interpreter can process the method parameters, and through it, you can obtain some information on the method parameters, such as annotation information in the method parameters, and process the parameter data according to the obtained information. By implementing this interface or inheriting its implementation class, you can implement a custom processing method parameter interpreter, and then register it Resolver
through WebMvcConfigurer
the addArgumentResolvers
method to take effect.
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;
}
In Spring MVC, the system has provided a variety of processing method parameter interpreters for different purposes. When a request comes, the system will first look for a suitable interpreter from the interpreter provided by the system Resolver
. If it matches, it will look for the registered one. Custom implementation Resolver
, so usually we have to create custom annotations on the parameters to be processed, so that it is convenient to use custom ones Resolver
for processing.
Select the process Resolver
for processing:
// 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;
}
If we really need to configure customizations higher than those built into the system Resolver
, we can configure them in the following ways:
In the WebConfig
configuration class, by @PostConstruct
annotating a method, the method can be automatically called after the Bean dependency injection is completed, and then the Resolver
set custom Resolver
priority can be upgraded to the built-in one. before.
@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);
}
}
Off-topic: You can automatically inject information such as Tid into parameters by implementing customization. The following is a simple demo to show:
Spring MVC extension handler that writes request header parameters to method parameters on demand
HandlerMethodReturnValueHandler (processing method return value handler)
Can beHandlerMethodReturnValueHandler
used to process the return value after the program method runs, convert it into the format we need, and return it.
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;
}
As with HandlerMethodArgumentResolver
, the system has provided a variety of different Handler
ones , and the custom ones Handler
will be added after the built-in ones Handler
. The conventional configuration method WebConfig
is . If you want to configure a custom that is higher than the built-in system Handler
, you can refer to the configuration method below, which is basically the same as the configuration method of the parameter interpreter.
@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);
}
}
Select the process Handler
for processing:
// class: HandlerMethodReturnValueHandlerComposite
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
//遍历Handler
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
//找到并返回
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
HttpMessageConverter (Http message converter)
It isHttpMessageConveter
used to process request and response data. We often use @RequestBody
and @ResponseBody
. Through these two annotations, you can directly use Java objects in the Controller as request parameters and return content, and the conversion between them is completed HttpMessageConverter
. Spring has a lot of built-in for us HttpMessageConverter
, such as , MappingJackson2HttpMessageConverter
, StringHttpMessageConverter
etc.
Common message converters are already included:
name | effect | Read supports MediaType | Write support for MediaType |
---|---|---|---|
MappingJackson2HttpMessageConverter | Convert Json data using Jackson's ObjectMapper | application/json | application/json |
StringHttpMessageConverter | Conversion between data and String type | text/* | text/plain |
ByteArrayHttpMessageConverter | Converting data to and from byte arrays | / | application/octet-stream |
The following is the HttpMessageConverter
interface, which must be implemented to implement Http message conversion. However, we usually do not implement this interface directly, but extend it by inheriting its subclasses. The default provided Converter
view is extended by inheriting its abstract class. .
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;
}
The abstract class that is usually inherited is AbstractHttpMessageConverter
, for example, MappingJackson2HttpMessageConverter
it is commonly used to inherit it, which can reduce the workload of writing processing methods. If we want to expand, we usually Converter
configure it on the existing system, and rarely inherit it. .
For the configuration method, configureMessageConverters
see extendMessageConverters
the descriptions of configuring message converters and extending message converters above.
Note: At present, in the current development, there are not many occasions to completely customize the requested message and response, and most of them are to MessageConverter
set and enhance the existing ones.
Formatter
Formatter
And Converter
similar, is to convert one type to another type, however, Formatter
the source type must be one String
and the target type is a java type. In SpringMVC, most of the input processed is textual input, therefore, choice is more appropriate Formatter
than choice .Converter
The structure of the Formatter interface is as follows:
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;
}
Here T represents the target type of the input string to be converted. The parse method uses the specified Locale to parse a String into the target type. The print method, in contrast, returns a string representation of the target object.
See above for configuration addFormatters
.
Project Recommendation: Based on Spring Cloud Alibaba technology, a distributed microservice development platform including basic functions, OA, payment, marketing and other modules