Abstract

The purpose of this article is to take a detailed look at how SpringMVC works and how you can extend SpringMVC as a developer. Because SpringMVC has been analyzed extensively, this article focuses on how to leverage SpringMVC’s extension points to implement our requirements.

What is Spring MVC

What does SpringMVC do? What problems need to be solved?

The following figure shows the interaction between a client and a server

HTTP Packet (2) – How does the Web container parse HTTP packets

The general part is divided into two parts: one is to establish the connection, and one is to transfer the content. So the servlet specification consists of two main parts. One is the servlet interface, which defines the specification for handling requests. Part of the servlet container is to manage the loading of servlet instances.

Lightweight servlet container have tomcat/jetty/undertow, servlet framework for SpringMVC/Struts/Webx these, this paper focuses on for SpringMVC

SpringMVC workflow

  1. DispatchServlet is the entry point, and the doDispatch method starts processing the request
  2. The controll consists of two parts, one is the URL processing map, which maps the URL to the specific processing bean. That is HandleMapping, and the other part is the specific Handle. Because different handles are needed, HandleAdapter is defined.
  3. The Model is simple, it’s basically a ModelView object,
  4. The View contains two parts, one is the resolution of the ViewName, and the other is the template engine corresponding to the ViewName, to render the final template engine.

Common extension points

Based on the above, Spring MVC provides multiple layers of extensions that make it easy for developers to implement customized functionality without requiring changes to the underlying code

A Filter.

The Filter is not really SpringMVC, it’s servlet, and the request hasn’t been sent to DispatchServlet yet. Filter allows unified customization of requests and responses, such as stream limiting, logging, and tracing.

Implement javax.servlet.Filter interface

Controll – HandleMapping, HandlerAdapter

HandleMapping belongs to the Controll level. We can write any HandlerMapping implementation class and then define policies to determine the generation of a Web request to the HandlerExecutionChain object.

Inherit the RequestMappingHandlerMapping class. For a concrete example, see Fredal’s blog – Developing microservices using Transparent RPC based on SpringMVC

Briefly, his RPC communication protocol is based on HTTP. So RPC calls are based on the same restful API as the server. Write to remove the common front-end, and then package a layer of RPC client. Convenient for clients to call. But that’s too cumbersome, for apis that don’t need to be exposed to the front end, just RPC calls between services. Let’s go through the servlet-SpringMVC again.

So he made a transformation based on the RequestMappingHandlerMapping. Instead of being based on SpringMVC, I defined a set of RPC paradigms for myself and then converted to SpringMVC.

Controll – Interceptor

Interceptor belongs to the Controll level. We can customize interceptors to do whatever we want before a request is actually processed, before the request is processed but not output to the response, and after the request is output to the response. It is widely used in Log,Session, and authentication scenarios.

Implement the HandlerInterceptor interface

4. The View – HandlerMethodArgumentResolver

Parsing method parameters can easily extend HTTP request parameters. Implement interface HandlerMethodArgumentResolver

For example, you need to process device information from HTTP headers

@Component
public class DeviceResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(final MethodParameter methodParameter) {
        returnmethodParameter.getParameterType().equals(DeviceInfo.class); } @Override public Object resolveArgument(final MethodParameter methodParameter, final ModelAndViewContainer modelAndViewContainer, final NativeWebRequest nativeWebRequest, final WebDataBinderFactory webDataBinderFactory) throws Exception { HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest(HttpServletRequest.class); String ID = request.getheader (String id = request.getheader ("x-device-id");
        if(id ! = null) { DeviceInfo deviceInfo = new DeviceInfo(); deviceInfo.setId("id");
            return deviceInfo;
        }
        returnnull; }}Copy the code

5. View-converter

Type converter, mainly related to serialization, parameter binding springMVC will convert the parameter from the front end to the type of the parameter defined by the method by some rule. Default implementation StringHttpMessageConverter, ByteArrayHttpMessageConverter etc., the default cannot satisfy the demand we can define the interface to realize their own type of transformation.

Inheritance AbstractHttpMessageConverter can.

6. The View – HandlerExceptionResolver

Exception handling, which usually requires defining global exceptions.

The @controllerAdvice note summed this up in a record of problems with the front end

7. Modify the requestBody content

When we need to unify the RequestBody content, because HandlerMethodArgumentResolver can only handle a specific type of, can’t do that.

Implement the RequestBodyAdvice interface. For example, I need to process the content in the RequestBody and convert the emoji input

@RestControllerAdvice public class EmojiReplaceAdvice implements RequestBodyAdvice { @Override public boolean supports(final MethodParameter methodParameter, final Type targetType, final Class<? extends HttpMessageConverter<? >> converterType) {returnmethodParameter.hasParameterAnnotation(EmojiReplace.class); } @Override public Object handleEmptyBody(final Object body, final HttpInputMessage inputMessage, final MethodParameter parameter, final Type targetType, final Class<? extends HttpMessageConverter<? >> converterType) {returnbody; } @Override public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, final MethodParameter parameter, final Type targetType, final Class<? extends HttpMessageConverter<? >> converterType) throws IOException {return new HttpInputMessage() {
            @Override
            public InputStream getBody() throws IOException {
                final String content = IOUtils.toString(inputMessage.getBody());
                final String emojiUnicodeToAlias = StringUtil.parseEmojiUnicodeToAlias(content);
                return new ByteArrayInputStream(
                        emojiUnicodeToAlias.getBytes(StandardCharsets.UTF_8));
            }

            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }
        };
    }

    @Override
    public Object afterBodyRead(final Object body, final HttpInputMessage inputMessage,
            final MethodParameter parameter, final Type targetType,
            final Class<? extends HttpMessageConverter<?>> converterType) {
        returnbody; }}Copy the code

conclusion

This article mainly systematically summarizes the working principle and various extension mechanisms of SpringMVC, which is highly generalized and lacks details. The specific implementation, pit, and application scenarios of each extension point need to be explained in future articles.

reference

Fredal. Xin/develop – wit…