Spring-MVC configuration and extension

1. Externalized configuration (WebMvcConfigurer)

Brief introduction

​ To change or extend the default behavior of Spring MVC, usually through WebMvcConfigurerconfiguration. 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 WebMvcConfigurerAdapterwas WebMvcConfigureran abstract class that could be WebMvcConfigurerAdapterconfigured by inheriting and overriding its methods. By the time of SpringBoot2.x, it was marked as @Deprecated, by implementing the WebMvcConfigurerinterface, 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#addInterceptorregistering the interceptor, the interceptor must be HandlerInterceptora 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#addadding an argument resolver. Note that the Resolverpriority 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#setArgumentResolversmethod, 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 addArgumentResolversadding a parameter parser. The added custom Handlerpriority 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#setReturnValueHandlersto 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#addto register and use, The ones added here will converterbe 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 converterthe 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 configureMessageConverterssimilar , the difference is that this method runs configureMessageConvertersafter , and the built-in system converterhas been added. At this time, we can also change the processing order by changing convertersthe 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 setUseSuffixPatternMatchmethods 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 PathMatchersum 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 InternalResourceViewResolverconfiguration classes, and then set preffixand suffixparameters 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:

  1. 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 HandlerMappingimplementation class to determine the generation of a web request to an HandlerExecutionChainobject . Usually we don't need to extend it.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. Converter(type converter)

    Converting data types is mainly used HttpMessageConverter(http message converter) to process request and response data.

  7. Formatter(formatter)

    The processing and formatting of the received parameters can only be applied to the input data of type String.

  8. ViewResolver(view resolver)

    To complete ModelAndViewthe process from going to the real view, the ViewResolverinterface is DispatcherServletcalled in the interface. When DispatcherServletthe Controller is called, an ModelAndViewobject will be obtained, and then DispatcherServletthe render method will be called to render the view. In the current separation of front and back ends, we generally do not expand this.

  9. HandlerExceptionResolver(exception handling)

    not used, slightly

HandlerInterceptor (interface interceptor)

HandlerInterceptorIt 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 WebMvcConfigurerthe addInterceptorsmethod 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 Resolverthrough WebMvcConfigurerthe addArgumentResolversmethod 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 Resolverfor processing.

​ Select the process Resolverfor 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 WebConfigconfiguration class, by @PostConstructannotating a method, the method can be automatically called after the Bean dependency injection is completed, and then the Resolverset custom Resolverpriority 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 Handlerones , and the custom ones Handlerwill be added after the built-in ones Handler. The conventional configuration method WebConfigis . 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 Handlerfor 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 @RequestBodyand @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, StringHttpMessageConverteretc.

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 HttpMessageConverterinterface, 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 Converterview 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, MappingJackson2HttpMessageConverterit is commonly used to inherit it, which can reduce the workload of writing processing methods. If we want to expand, we usually Converterconfigure it on the existing system, and rarely inherit it. .

​ For the configuration method, configureMessageConverterssee extendMessageConvertersthe 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 MessageConverterset and enhance the existing ones.

Formatter

Formatter​And Convertersimilar, is to convert one type to another type, however, Formatterthe source type must be one Stringand the target type is a java type. In SpringMVC, most of the input processed is textual input, therefore, choice is more appropriate Formatterthan 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

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324114728&siteId=291194637