We talked about the initialization process of SpringMVC earlier, and I believe you have a basic understanding of the initialization process of SpringMVC. Let’s take a look at the execution process of a request when it arrives. Of course, this process is quite long. Songge may share it with you in two articles.

The parent class of DispatcherServlet is FrameworkServlet, so let’s take a look at FrameworkServlet first. This helps us understand DispatcherServlet.

1.FrameworkServlet

FrameworkServlets inherit from HttpServletBeans, and HttpServletBeans inherit from HttpServlets. HttpServlets are part of JavaEE, and we won’t discuss them here. HttpServletBean is a framework thing to start with, but HttpServletBean is special in that it does not do any request processing, but participates in some initialization operations, which are relatively simple and which we analyzed in the last article. So instead of analyzing HttpServletBean, we will start directly with its FrameworkServlet subclass.

Like all servlets, FrameworkServlet starts processing requests from a service method. Let’s take a look at the frameworkServlet #service method:

@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); }}

It can be seen that in this method, the current request method is first obtained, and then extra attention is paid to patch request. All other types of requests are handled by super.service.

However, in HttpServlet, doGet, doPost and other requests are not processed substantially, so FrameworkServlet also overwrites the corresponding methods of various requests. DoDelete, doGet, doOptions, doPost, doPut, doTrace, etc. All the other methods except doHead are overwritten.

Let’s look at four methods: doDelete, doGet, doPost, and doPut:

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}

As you can see, the request is again passed to ProcessRequest, and in the ProcessRequest method it is further called to the DoserService to sort out the different types of requests.

DoOptions and doTrace are slightly different, as follows:

@Override protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) { processRequest(request, response); if (response.containsHeader("Allow")) { return; } } super.doOptions(request, new HttpServletResponseWrapper(response) { @Override public void setHeader(String name, String value) { if ("Allow".equals(name)) { value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name(); } super.setHeader(name, value); }}); } @Override protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (this.dispatchTraceRequest) { processRequest(request, response); if ("message/http".equals(response.getContentType())) { return; } } super.doTrace(request, response); }

You can see that there is an extra layer of logic in the handling of these two methods, which is to choose whether to handle the corresponding request in the current method or to hand it to the parent class. Since the DispatchoptionsRequest and DispatchTraceRequest variables are false by default, by default, Both types of requests are handed over to the superclass to handle.

2.processRequest

ProcessRequest, which is the core method of the FrameworkServlet:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    initContextHolders(request, localeContext, requestAttributes);
    try {
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }
    finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }
        logResult(request, response, failureCause, asyncManager);
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

Although this method is relatively long, its core is actually the doService method in the middle. With doService as the bound, we can divide the content of this method into three parts:

  1. The Doservice was mostly preparatory work, and the preparatory work mainly did two things, The first thing to do is to retrieve their original LocalContext and RequestAttributes objects from the LocalContextHolder and RequestContextHolder, respectively. Then we call buildLocalContext and BuildRequestAttributes respectively to get the LocalContext and RequestAttributes objects of the current request. The LocalContext and RequestAttributes objects of the current request are set to the LocalContext and RequestContextHolder, respectively, using the initContextHolders method In the object. The second thing is to get the asynchronous manager and set the interceptor.
  2. Next is the doService method, which is an abstract method, the concrete implementation in the DispatcherServlet, this is put in the DispatcherServlet and then we will analyze.
  3. The third part is finally, which does two things: first, restore the objects in the LocalContextHolder and RequestContextHolder to their original state (see Step 1). The second thing is through publishRequestHandledEvent method to release a ServletRequestHandledEvent type of message.

ProcessRequest does two things. The first is to process LocalContext and RequestAttributes, and the second is to publish the event. Let’s look at these two things separately.

2.1 LocaleContext and RequestAttributes

LocalContext and RequestAttributes are both interfaces, but the objects inside are different.

2.1.1 LocaleContext

LocalContext has a Locale inside it, which is the localization information, and if we need to support internationalization, we’ll use a Locale.

When internationalizing, if we need to use a Locale object, the first reaction is to get it from HttpServletRequest, like this:

Locale locale = req.getLocale();

But you know, HttpServletRequest only exists in the Controller, so if we want to get HttpServletRequest from the Service layer, we have to pass the parameters from the Controller, which is a little bit cumbersome, This is especially true when Service methods have already been defined.

So SpringMVC also provides us with LocalContext Holder, which is used to store the LocalContext of the current request. When you look at LocalContextHolder, I don’t know if that sounds familiar, but Songo talked about SecurityContextHolder in the previous series of Spring Security tutorials, and the principle of the two is basically the same. ThreadLocal is used to store variables, so that different threads do not interfere with each other. For those who are not familiar with ThreadLocal, check out the Spring Security series of ThreadLocal, which has been analyzed in detail.

With LocalContexHolder, we can get a Locale anywhere. For example, in a Service we can get a Locale as follows:

Locale locale = LocaleContextHolder.getLocale();

The above Locale object is actually pulled from the LocalContext inside the LocalContext holder.

SpringMVC also has a localeSolver parser, so req.getLocale() doesn’t always get the value of a Locale. I’ll talk about that in a future article.

2.1.2 RequestAttributes

RequestAttributes is an interface that can be used to get/set/remove an attribute.

RequestAttributes has many implementation classes. By default, ServletRequestAttributes is used. We can getRequest, we can getResponse, we can getSession.

In the implementation of ServletRequestAttributes, the scope parameter is used to determine whether the operation is request or session. Take a look at the ServletRequestAttributes#setAttribute method (the get/remove methods execute similar logic) :

public void setAttribute(String name, Object value, int scope) { if (scope == 0) { if (! this.isRequestActive()) { throw new IllegalStateException("Cannot set request attribute - request is not active anymore!" ); } this.request.setAttribute(name, value); } else { HttpSession session = this.obtainSession(); this.sessionAttributesToUpdate.remove(name); session.setAttribute(name, value); }}

If scope is 0, it operates request; if scope is 1, it operates session. If you are operating a request, you need to first determine whether the request has been executed through isRequestActive method. If the request has been executed, Can’t again on the other operation (when performing the requestAttributes. In the finally block requestCompleted method after isRequestActive returns false).

Similar to LocalContext, RequestAttributes are stored in RequestContextHolder, which works similarly to SecurityContextHolder. No more details here.

As you can see, in SpringMVC, if we need to use request, response, and session somewhere other than Controller, Instead of passing the Request, Response, and Session objects from the Controller each time, we can get them directly from the RequestContextHolder like this:

ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpServletResponse response = servletRequestAttributes.getResponse();

Is not very easy!

2.2 Event publishing

Finally, the event is published in the processRequest method.

In the finally block of code will call publishRequestHandledEvent method sends a ServletRequestHandledEvent types of events, specific sending code is as follows:

private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
        long startTime, @Nullable Throwable failureCause) {
    if (this.publishEvents && this.webApplicationContext != null) {
        // Whether or not we succeeded, publish an event.
        long processingTime = System.currentTimeMillis() - startTime;
        this.webApplicationContext.publishEvent(
                new ServletRequestHandledEvent(this,
                        request.getRequestURI(), request.getRemoteAddr(),
                        request.getMethod(), getServletConfig().getServletName(),
                        WebUtils.getSessionId(request), getUsernameForRequest(request),
                        processingTime, failureCause, response.getStatus()));
    }
}

As you can see, sending an event requires publishEvents to be true, which is true by default. If you need to change the value of this variable, you can incidently configure it via the init-param node when you configure the DispatcherServlet in web.xml. Normally, this event will always be sent out, but we can listen for it if the project needs it, as follows:

@Component public class ServletRequestHandleListener implements ApplicationListener<ServletRequestHandledEvent> { @Override public void onApplicationEvent(ServletRequestHandledEvent servletRequestHandledEvent) { System. The out. Println (" - the request has been completed "+ servletRequestHandledEvent. GetRequestUrl ()); }}

The event is emitted when a request completes.

3. Summary

In this article, we mainly share the parent class of DispatcherServlet in SpringMVC FrameworkServlet. FrameworkServlet has relatively simple functions. The main reason is that PATCH processing is added to the service method, and other types of requests are classified into the processRequest method for unified processing, and the processRequest method is divided into three parts. First, the LocalContext and RequestAttributes are processed, then the DoserService is executed, and finally the LocalContext and RequestAttributes attributes are restored in the finally block. At the same time publish a request to end the event.

Doservice is the centerpiece, which Songo will share with you in the next article. All right, so much for today’s chat with friends