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