Spring MVC processes http requests through the handler mapping and handler adapter. When a request passes through the dispatcher servlet, the dispatcher servlet will select an appropriate handler mapping and handler adapter to process the request.
By customizing the processor mapper and processor adapter, we can learn how spring MVC handles a request. Therefore, this article uses the custom mapper and adapter to see how spring MVC handles requests using the mapper and adapter.
Direct customization is meaningless. We first determine a requirement, and then customize the mapper and adapter according to the requirement. Generally, when we process http requests, we use the @ RequestMapping provided to us by spring MVC most. Mark @ RequestMapping on the method, and spring MVC will automatically find this method for us to process requests. What we need now is that all of us For the GET request at the end of myhtml, I do not need to write @ RequestMapping to process the request, but directly return to / myhtml / xxx under the resources directory HTML file. The file name is determined by the request address, such as accessing localhost / ABC Myhtml, you will find / myhtml / ABC. In the resources directory HTML. This is equivalent to a static resource access, without the need for us to write a method with @ RequestMapping in the controller.
The core method for spring MVC to process requests is org springframework. web. servlet. Dispatcherservlet#doDispatch. For convenience, I post the doDispatch method at the end of the article, so that you can see the following article in combination with the doDispatch process.
getHandler
In the doDispatch method, the getHandler method will be called first to obtain an appropriate handler to process the request. The getHandler method is also very simple, as shown below.
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
The getHandler method will loop all the handlerMappings. If a handlermapping returns a handler, it will be used. Therefore, we must first customize a handlermapping to return our customized handler. Customize the handlermapping and handler as follows.
Custom HandlerMapping
import org.springframework.web.servlet.handler.AbstractHandlerMapping; import javax.servlet.http.HttpServletRequest; public class MyHandlerMapping extends AbstractHandlerMapping { @Override protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String servletPath = request.getServletPath(); String method = request.getMethod(); // If the request is in The get request at the end of myhtml will return our custom handler, which will not be processed in other cases. if (servletPath.endsWith(".myhtml") && HttpMethod.GET.matches(method)) { MyHandler myHandler = new MyHandler(); String[] pathSplit = servletPath.split("/"); String endPath = pathSplit[pathSplit.length - 1]; String fileName = endPath.split("\\.")[0]; // setPath is used to record which file we finally want to find and return myHandler.setPath(fileName); return myHandler; } return null; } }
Custom Handler. This is where the request is really processed. We will write back the file with the path specified in the url to the browser through response.
@Getter @Setter public class MyHandler { private String path; public void handle(HttpServletResponse response) throws IOException { File targetFile; // Read the corresponding files in the classpath directory according to the request path. If there is no corresponding file, return the page404 prepared in advance html try { targetFile = ResourceUtils.getFile("classpath:myhtml/" + path + ".html"); } catch (FileNotFoundException e) { e.printStackTrace(); targetFile = ResourceUtils.getFile("classpath:myhtml/page404.html"); } // Write the read file to the response. InputStream inputStream = new FileInputStream(targetFile); byte[] bytes = new byte[1024]; int len; PrintWriter writer = response.getWriter(); while ((len = inputStream.read(bytes) )!= -1) { writer.write(new String(Arrays.copyOf(bytes, len))); } } }
getHandlerAdapter
The getHandlerAdapter method is as follows. You can see that spring will loop all handleradapters to see if the current adapter supports this handler.
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
The reason why the handler is called through the adapter is that after spring MVC gets the handler, it will use this handler to process the request, but the handler is customized by us. Spring MVC does not know how to call our handler, so here spring MVC calls getHandlerAdapter, that is, spring MVC will indirectly call the handler through the adapter. Because only we know how to call our handler, we also need to customize a handleradapter to spring MVC, so that spring MVC can indirectly call our handler with the handleradapter we give. The advantage of this is that it gives us great freedom to customize the handler. Our handler can write whatever it wants without implementing any interface, as long as there is a corresponding handleradapter. Customize the handleradapter as follows. The core methods are the support and handle methods. In the support method, we judge that if it is a custom handler, we will return true, indicating that we know the handler, and the handler can be handled by me. In the handler method, we transform handler into its own defined handler and then call handle to process the request.
public class MyHandlerAdapter implements HandlerAdapter, Ordered { // The Ordered method can be implemented so that when multiple adapter s are available, a smaller order can be used. private int order; public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return order; } @Override public boolean supports(Object handler) { return handler instanceof MyHandler; } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { MyHandler myHandler = (MyHandler) handler; myHandler.handle(response); return null; } @Override public long getLastModified(HttpServletRequest request, Object handler) { return 0; } }
to configure
After the above, we need to register the handlerMapping and handlerAdapter defined by ourselves as spring bean s, so that spring will automatically use our handlerMapping and handlerAdapter. Finally, remember to create a new myhtml folder in the resources directory and add your own defined pages to test.
@Configuration public class WebMvcConfig { @Bean public MyHandlerMapping myHandlerMapping() { MyHandlerMapping myHandlerMapping = new MyHandlerMapping(); myHandlerMapping.setOrder(10); return myHandlerMapping; } @Bean public MyHandlerAdapter myHandlerAdapter() { MyHandlerAdapter myHandlerAdapter = new MyHandlerAdapter(); myHandlerAdapter.setOrder(10); return myHandlerAdapter; } }
org.springframework.web.servlet.DispatcherServlet#doDispatch
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. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }