Introduction to the

In the past few years, I have been working with SpringWeb for most of my time. In the past few years, I was not good at reading source code and methodology. I learned a lot during the epidemic, and now I have some spare time to write Spring related source code analysis at my current level

This article will take a brief look at how a request is handled to reach our Controllers

The preparatory work

Preparation is relatively simple, we use Spring Initializr, set up an initial project, and write a simple HelloWorld

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorld {

    @GetMapping("/")
    public String helloWorld(a) {
        return "Hello world"; }}Copy the code

RestfulTool and RestfulToolKit are recommended plug-ins for IDEA

After starting the project, it reads the list of all requests and makes a simple and quick request:

In this way, the preliminary environment of this chapter is set up, and the source code analysis begins below:

The source code parsing

We added the HelloWorld class directly:

return "Hello world"; 
Copy the code

This line interrupts the request directly, and you can see the following call stack:

Let’s go from the Internet, general click into, see the general idea

Look with questions:

  • Requests received from there: How can you write network programming so that there is at least one Netty service listening on a specified port to receive requests
  • How does the received request find the page code we wrote (HelloWorld) : how does the request path correspond to the handler, for example

A simple request for local exploration is as follows:

Find the general listening point

From the bottom up, we see that there is a class: nioendpoint.java. We don’t know much about it, but we know that there is one

Find the important Request and Response handlers

We move on to an important class: http11Processor.java, which adds filter-related code, and also explicitly handles Request and Response

Here we leave two questions. For now, let’s sort out the request path, and we’ll come back to it later

  • How is the Filter set in this class used
  • How are Request and Response converted
public class Http11Processor extends AbstractProcessor {
    @Override
    public SocketState service(SocketWrapperBase
        socketWrapper)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

        // Setting up the I/O
        setSocketWrapper(socketWrapper);

        // Flags
        keepAlive = true;
        openSocket = false;
        readComplete = true;
        boolean keptAlive = false;
        SendfileState sendfileState = SendfileState.DONE;

        while(! getErrorState().isError() && keepAlive && ! isAsync() && upgradeToken ==null&& sendfileState == SendfileState.DONE && ! protocol.isPaused()) {// Parsing the request header
            try{... }// Has an upgrade been requested?
            if (isConnectionToken(request.getMimeHeaders(), "upgrade")) {... }...// Process the request in the adapter
            if (getErrorState().isIoAllowed()) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    getAdapter().service(request, response);
                    // Handle when the response was committed before a serious
                    // error occurred. Throwing a ServletException should both
                    // set the status to 500 and set the errorException.
                    // If we fail here, then the response is likely already
                    // committed, so we can't try and set headers.
                    if(keepAlive && ! getErrorState().isError() && ! isAsync() && statusDropsConnection(response.getStatus())) { setErrorState(ErrorState.CLOSE_CLEAN,null); }}catch (InterruptedIOException e) {
                    setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
                } catch (HeadersTooLargeException e) {
                    log.error(sm.getString("http11processor.request.process"), e);
                    // The response should not have been committed but check it
                    // anyway to be safe
                    if (response.isCommitted()) {
                        setErrorState(ErrorState.CLOSE_NOW, e);
                    } else {
                        response.reset();
                        response.setStatus(500);
                        setErrorState(ErrorState.CLOSE_CLEAN, e);
                        response.setHeader("Connection"."close"); // TODO: Remove}}catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    setErrorState(ErrorState.CLOSE_CLEAN, t);
                    getAdapter().log(request, response, 0); }}}... }}Copy the code

Through a series of Valva class processes

As we move up, we see a lot of value-related processing, a kind of chain of responsibility processing, but we don’t know exactly what it does yet, but it doesn’t matter, let’s move on

After a series of Filter processing

After the processing of the above Valva classes, we came to the familiar Filter processing. We will not delve into the details and continue to leave questions for future processing

  • How is the Filter initialized
  • How is the specified request path processed
  • Will custom added filters be inserted into it? How to insert?

Request paths to specific functions for processing

The class dispatcherServlet.java has a very high frequency of occurrences

Through breakpoint debugging, we find that we directly get the request method corresponding to our request path, as indicated in the following code:

public class DispatcherServlet {
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

	try {
		ModelAndView mv = null;
		Exception dispatchException = null;

		try{ processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest ! = request);// Determine handler for the current request.
			// Get the request processing method
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			if(! mappedHandler.applyPreHandle(processedRequest, response)) {return;
			}

			// Actually invoke the handler.
			// This line is viewed with a breakpoint: mappedHandler is our requested handler
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}

			applyDefaultViewName(processedRequest, mv);
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch(Exception ex) { ........ }}}Copy the code

Continue down, we came to a class: InvocableHandlerMethod. Java, including: method (getBean (), args), compared with the familiar, the classic reflection calls

public class InvocableHandlerMethod {
	protected Object doInvoke(Object... args) throws Exception {
		Method method = getBridgedMethod();
		ReflectionUtils.makeAccessible(method);
		try {
			if (KotlinDetector.isSuspendingFunction(method)) {
				return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
			}
			return method.invoke(getBean(), args);
		}
		catch(IllegalArgumentException ex) { ...... }}}Copy the code

Here we also leave a question:

  • How do I get the processing method corresponding to the request path?
  • How to parse the parameters of the processing method?

conclusion

After the above simple analysis, we can roughly get a request processing path:

  • Nioendpoint.java: Service listener
  • Http11processor.java: request and response processing entry
  • A series of Valva class processes
  • A series of Filter processing
  • Dispatcherservlet. Java: Request path to specific handling method
  • InvocableHandlerMethod. Java: reflection calls

So far we’ve got a big map, but a lot of places are still fogged up, so we’re going to slowly remove the fog and explore the mystery