Overview
For web developers, the MVC model is very familiar to everyone. In SpringMVC, the request that meets the conditions enters the DispatcherServlet responsible for the distribution of the request. The DispatcherServlet maps the request url to the controller (save in HandlerMapping). HandlerMapping finally returns the HandlerExecutionChain, which contains the specific processing object handler (that is, what we wrote when we program it). Controller) and a series of interceptors. At this time, the DispatcherServlet will find a handlerAdapter that supports this processor type based on the handler in the handler in the returned HandlerExecutionChain. In the processor adapter, it will eventually call the controller's request response method and return the result view (ModelAndView). After obtaining the result view, the result is displayed through the render method.
HanderMapping inheritance system:
SpringMVC requests distribution to handler processor. This step is solved through the HandlerMapping module. handlerMapping also handles interceptors.
Let's take a look at the inheritance tree of HandlerMapping first
You can roughly make a classification like this:
1. An interface HandlerMapping, define an API: HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
2. A basic abstract class: mainly prepares the context environment, provides the getHandlerInternal hook, encapsulates the interceptor to the HandlerExecutionChain
3. Use of @Controller, @RequestMapping based on annotation
4. Configure the SimpleUrlHandlerMapping from the URL to the handler in the configuration file
5. BeanNameUrlHandlerMapping is implemented by default
6. Mapping of Controller subclasses
Let’s take a look at HandlerMapping, just a getHandler API is very simple.
// HandlerMappingpackage org.springframework.web.servlet;public interface HandlerMapping {HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;} AbstractHandlerMapping is not that simple
First look at the class inherited by AbstractHandlerMapping and the interface implemented
package org.springframework.web.servlet.handler;public abstract class AbstractHandlerMapping extends WebApplicationObjectSupportimplements HandlerMapping, Ordered {// ...} WebApplicationObjectSupport is used to provide context ApplicationContext and ServletContext.
There is also the initApplicationContext method here, which is often used in the future. AbstractHandlerMapping is directly overridden.
The ApplicationContextAware and ServletContextAware interfaces are still implemented in the parent class, and the spring concept is very unified.
Ordered is used for collection sorting.
Let's continue to look at the properties of AbstractHandlerMapping
// AbstractHandlerMapping// Order assigns the maximum value, and the priority is the smallest private int order = Integer.MAX_VALUE; // default: same as non-Ordered// The default Handler, the Obejct used here and subclass implementation, use HandlerMethod, HandlerExecutionChain and other private Object defaultHandler;// Auxiliary class for url calculation private UrlPathHelper urlPathHelper = new UrlPathHelper();// Path matching is based on ant, and solves private PathMatcher pathMatcher = new AntPathMatcher();// Interceptor configuration:,HandlerMapping property setting;,extendInterceptors set private final List<Object> interceptors = new ArrayList<Object>();// parse from interceptors and add it directly to all handlerprivate final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();// Before use, you need to match the url, and only if the match is passed will be used private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
Check out the initialization of the interceptor:
// AbstractHandlerMapping@Overrideprotected void initApplicationContext() throws BeansException {extendInterceptors(this.interceptors);detectMappedInterceptors(this.mappedInterceptors);initInterceptors();}/*** Provided to subclass extension interceptors, but unfortunately none of them are used */protected void extendInterceptors(List<Object> interceptors) {}/*** Scan the MappedInterceptors under the application and add them to mappedInterceptors*/protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludedAncestors(getApplicationContext(),MappedInterceptor.class, true, false).values());}/*** Collect MappedInterceptor and adapt to HandlerInterceptor and WebRequestInterceptor*/protected void initInterceptors() {if (!this.interceptors.isEmpty()) {for (int i = ; i < this.interceptors.size(); i++) {Object interceptor = this.interceptors.get(i);if (interceptor == null) {throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");}if (interceptor instance of MappedInterceptor) {mappedInterceptors.add((MappedInterceptor) interceptor);}else {adaptedInterceptors.add(adaptInterceptor(interceptor));}}}} protected HandlerInterceptor adaptInterceptor(Object interceptor) {if (interceptor instanceof HandlerInterceptor) {return (HandlerInterceptor) interceptor;}else if (interceptor instanceof WebRequestInterceptor) {return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);}else {throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());}}Then there is the implementation of getHandler(HttpServletRequest request), and here also reserves getHandlerInternal(HttpServletRequest request) for subclass implementation.
// AbstractHandlerMappingpublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {Object handler = getHandlerInternal(request);if (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}return getHandlerExecutionChain(handler, request);}protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; Finally, encapsulate the interceptor to HandlerExecutionChain
Add adaptedInterceptors directly
mappedInterceptors need to be added after matching according to the url
// AbstractHandlerMappingprotected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {HandlerExecutionChain chain =(handler instanceof HandlerExecutionChain) handler : new HandlerExecutionChain(handler); chain.addInterceptors(getAdaptedInterceptors());String lookupPath = urlPathHelper.getLookupPathForRequest(request);for (MappedInterceptor mappedInterceptor : mappedInterceptors) {if (mappedInterceptor.matches(lookupPath, pathMatcher)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}return chain;} The mapping of Controller subclasses, this branch first looks at class inheritance
Let’s talk about the main responsibilities of each category here
1. AbstractHandlerMapping Prepare the context environment; provide getHandlerInternal hook; encapsulate interceptor to HandlerExecutionChain
2. AbstractUrlHandlerMapping implements the method of registering handler for subclass use; implements getHandlerInternal, and finds handler based on the configuration information initialized by subclasses.
3. AbstractDetectingUrlHandlerMapping scans the Object under the application, and after iteration, provide the hook method determineUrlsForHandler to determine how to filter it.
4. AbstractControllerUrlHandlerMapping implements determineUrlsForHandler, adds the handler operation (configuration file configuration), and reserves the hook method buildUrlsForHandler for subclass implementation; at the same time judges the subclass of controller
5. ControllerBeanNameHandlerMapping Generate URL based on bean name
ControllerClassNameHandlerMapping generates url based on class name
Let’s start with AbstractUrlHandlerMapping. Here we just look at the code roughly. If you need to analyze it carefully, please move to <SpringMVC source code interpretation - HandlerMapping - AbstractUrlHandlerMapping series request distribution>
Registration of handler
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { } protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { } The search for handler
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {}// Find handlerprotected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {}// Verify handlerprotected void validateHandler(Object handler, HttpServletRequest request) throws Exception {}// Encapsulate the interceptor to HandlerExecutionChainprotected Object buildPathExpositingHandler(Object rawHandler, String bestMatchingPattern,String pathWithinMapping, Map<String, String> uriTemplateVariables) {}AbstractDetectingUrlHandlerMapping, this is not expanded, please move in detail <SpringMVC source code interpretation - HandlerMapping - AbstractDetectingUrlHandlerMapping series initialization>
What to do:
1. Call detectHandlers to scan Objct by overwriting initApplicationContext
2. Provide hook method determineUrlsForHandler to subclass generate urls according to handler
3. Call the registerHandler of the parent class to register
@Overridepublic void initApplicationContext() throws ApplicationContextException {super.initApplicationContext();detectHandlers();}protected void detectHandlers() throws BeansException {// ...}/*** Determine the URLs for the given handler bean.* Just hook*/protected abstract String[] determineUrlsForHandler(String beanName); AbstractControllerUrlHandlerMapping, this is not expanded, please move in detail <SpringMVC source code interpretation - HandlerMapping - AbstractDetectingUrlHandlerMapping series initialization> What to do specifically;
1. Overwrite the determineUrlsForHandler to add logic to remove some classes, and use excludedClasses and excludedPackages configured in the configuration file.
2. Determine whether the controller's subclass
3. Reserve buildUrlsForHandler to generate urls for subclasses
@Overrideprotected String[] determineUrlsForHandler(String beanName) {Class beanClass = getApplicationContext().getType(beanName);if (isEligibleForMapping(beanName, beanClass)) {return buildUrlsForHandler(beanName, beanClass);}else {return null;}}protected boolean isEligibleForMapping(String beanName, Class beanClass) {}protected boolean isControllerType(Class beanClass) {}protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass); ControllerBeanNameHandlerMapping and ControllerClassNameHandlerMapping Look at the source code directly, or move on <SpringMVC source code interpretation - HandlerMapping - AbstractDetectingUrlHandlerMapping series initialization> SimpleUrlHandlerMapping directly configures url to handler in the configuration file, which is to use registerHandlers to register handler in the configuration document, read the code directly or move <SpringMVC source code interpretation - HandlerMapping - SimpleUrlHandlerMapping initialization>
BeanNameUrlHandlerMapping implements determineUrlsForHandler to generate urls, look at the code directly or move <SpringMVC source code interpretation - HandlerMapping - AbstractDetectingUrlHandlerMapping series initialization>
Use of @Controller, @RequestMapping based on annotation
The hardest bone
Let's look at class inheritance first
Let’s talk about the responsibilities of each category. For specific analysis, please move to the following article
<SpringMVC source code interpretation - HandlerMapping - RequestMappingHandlerMapping initialization>
<SpringMVC source code interpretation - HandlerMapping - RequestMappingHandlerMapping request distribution>
1. AbstractHandlerMethodMaping defines the initialization process and how to map it when requesting
initialization:
1.1.1 Scan the Object under the application
1.1.2 Reserve the isHandler hook method to subclass to determine whether the Object is handler
1.1.3 Iteratively scan each handler to find a method that meets the requirements. The judgment here is still left to the subclass to implement getMappingForMethod
1.1.4 When registering the found processor, you need to ensure that a matching condition RequestMappingInfo can only map to a handler
1.1.5 Get the url according to the matching conditions, and the same is just to define the process. The specific algorithm is left to the subclass to implement getMappingPathPatterns
Request distribution processing:
1.2.1 Direct string matching method, look up handler
1.2.2 Matching condition search, the specific algorithm here is handed over to the subclass to getMatchingMapping
1.2.3 Sort and get the best matching handler. The sorting method here is still the subclass processing getMappingConparator
1.2.4 Encapsulation of matching and not matching handlers respectively
2. RequestMappingInfoHandlerMapping uses RequestMappingInfo to implement matching conditions, and the initialization of RequestMappingInfo is left to the subclass
2.1 Generate url -> getMappingPathPatterns according to RequestMappingInfo
2.2 Find Handler using matching conditions -> getMatchingMapping
2.3 Comparator algorithm -> getMappingComparator
2.4 Overwrite handleMatch and cache n multiple information to request
Register pattern, best matching pattern, parsed parameters in url, multi-value parameters parsed in url, mediaType
2.1.5 Overwrite handlerNoMatch, try to match it again after the last struggle
3. RequestMappingHandlerMapping Generate RequestMappingInfo according to the annotation @Controller @RequestMapping and verify isHandler
3.1 Overwrite afterPropertiesSet and add file suffix to judge
3.2 Implement isHandler, and one of the annotations on the class is correct.
3.3 Analyze the annotation content and produce RequestMappingInfo instances