AbstractDetectingUrlHandlerMapping registers Handler through scanning, and is distributed by the getHandlerInternal of AbstractUrlHandlerMapping when receiving the request.
There are 5 subclasses and one abstract class.
Similar to SimpleUrlHandlerMapping, it is initialized by overwriting the initApplicationContext and then calling detectHandlers.
detectHandlers scan the application Object through BeanFactoryUtils, and then reserve the determineUrlsForHandler for the subclass to generate the corresponding url according to the Handler.
The registered registerHandler is still provided by AbstractUrlHandlerMapping.
// AbstractDetectingUrlHandlerMapping/*** Calls the {@link #detectHandlers()} method in addition to the* superclass's initialization.*/@Overridepublic void initApplicationContext() throws ApplicationContextException {super.initApplicationContext();detectHandlers();} This is also called the initApplicationContext initialization interceptor of AbstractHandlerMapping.
The protagonist appears, detectHandlers, scan Handlers
// AbstractDetectingUrlHandlerMapping/*** Register all handlers found in the current ApplicationContext.* <p>The actual URL determination for a handler is up to the concrete* {@link #determineUrlsForHandler(String)} implementation. A bean for* which no such URLs could be determined is simply not considered a handler.* @throws org.springframework.beans.BeansException if the handler couldn't be registered* @see #determineUrlsForHandler(String)*/protected void detectHandlers() throws BeansException {if (logger.isDebugEnabled()) {logger.debug("Looking for URL mappings in application context: " + getApplicationContext());}String[] beanNames = (this.detectHandlersInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludedAncestors(getApplicationContext(), Object.class) :getApplicationContext().getBeanNamesForType(Object.class));// Take any bean name that we can determine URLs for.for (String beanName : beanNames) {String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) {// URL paths found: Let's consider it a handler.registerHandler(urls, beanName);}else {if (logger.isDebugEnabled()) {logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");}}}} The template method reserved here is defined as follows:
/*** Determine the URLs for the given handler bean.* @param beanName the name of the candidate bean* @return the URLs determined for the bean,* or {@code null} or an empty array if none*/protected abstract String[] determineUrlsForHandler(String beanName); Let's take a look at the implementation of the template method in BeanNameUrlHandlerMapping and AbstractControllerUrlHandlerMapping. BeanNameUrlHandlerMapping is very simple, so it implements determineUrlsForHandler. The alias in it should be configured in the configuration file through beanName.// BeanNameUrlHandlerMapping/*** Checks name and aliases of the given bean for URLs, starting with "/".*/@Overrideprotected String[] determineUrlsForHandler(String beanName) {List<String> urls = new ArrayList<String>();if (beanName.startsWith("/")) {urls.add(beanName);}String[] aliases = getApplicationContext().getAliases(beanName); for (String alias : aliases) {if (alias.startsWith("/")) {urls.add(alias);}}return StringUtils.toStringArray(urls);} Let’s take a look at the implementation in AbstractControllerUrlHandlerMapping
isEligibleForMapping determines whether the controller is excluded (excluded through package package or class class).
buildUrlsForHandler implements specific url generation rules from subclasses
isControllerType determines whether the subclass of the Controller
buildUrlsForHandler reserves template method for subclass production urls.
// AbstractControllerUrlHandlerMapping/*** This implementation delegates to {@link #buildUrlsForHandler},* provided that {@link #isEligibleForMapping} returns {@code true}.*/@Overrideprotected String[] determineUrlsForHandler(String beanName) {Class beanClass = getApplicationContext().getType(beanName);if (isEligibleForMapping(beanName, beanClass)) {return buildUrlsForHandler(beanName, beanClass);}else {return null;}} // AbstractControllerUrlHandlerMapping/**Just determine whether the controller is excluded from this mapping.* @param beanName the name of the controller bean* @param beanClass the concrete class of the controller bean* @return whether the specified class is excluded* @see #setExcludedPackages* @see #setExcludedClasses*/protected boolean isEligibleForMapping(String beanName, Class beanClass) {if (beanClass == null) {if (logger.isDebugEnabled()) {logger.debug("Exclude controller bean '" + beanName + "' from class name mapping " +"because its bean type could not be determined");}return false;}if (this.excludedClasses.contains(beanClass)) {if (logger.isDebugEnabled()) {logger.debug("Excluded controller bean '" + beanName + "' from class name mapping " +"because its bean class is explicitly excluded: " + beanClass.getName());}return false;}String beanClassName = beanClass.getName();for (String packageName : this.excludedPackages) {if (beanClassName.startsWith(packageName)) {if (logger.isDebugEnabled()) {logger.debug("Exclude controller bean '" + beanName + "' from class name mapping " +"because its bean class is defined in an excluded package: " + beanClass.getName());}return false;}}return isControllerType(beanClass);} // AbstractControllerUrlHandlerMapping/*** Determine whether the given bean class indicates a controller type* that is supported by this mapping strategy.* @param beanClass the class to introspect*/protected boolean isControllerType(Class beanClass) {return this.predicate.isControllerType(beanClass);} // ControllerTypePredicate provides 2 APIs to determine whether it is a subclass of Controller or a subclass of MultiActionController./*** Internal helper class that identifies controller types.** @author Juergen Hoeller* @since ..*/class ControllerTypePredicate {public boolean isControllerType(Class beanClass) {return Controller.class.isAssignableFrom(beanClass);}public boolean isMultiActionControllerType(Class beanClass) {return MultiActionController.class.isAssignableFrom(beanClass);}} Reserve the template method to generate url
// AbstractControllerUrlHandlerMapping/*** Abstract template method to be implemented by subclasses.* @param beanName the name of the bean* @param beanClass the type of the bean* @return the URLs determined for the bean*/protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass);
Let’s take a look at the two implementations of AbstractControllerUrlHandlerMapping ControllerBeanNameUrlHandlerMapping and ControllerClassNameUrlHandlerMapping.
In fact, these two are very simple. One is to produce url based on beanName, and the other is to produce url based on className.
// ControllerBeanNameUrlHandlerMapping@Overrideprotected String[] buildUrlsForHandler(String beanName, Class beanClass) {List<String> urls = new ArrayList<String>();urls.add(generatePathMapping(beanName));String[] aliases = getApplicationContext().getAliases(beanName);// Also get the alias for (String alias: aliases) {urls.add(generatePathMapping(alias));}return StringUtils.toStringArray(urls);} // ControllerBeanNameUrlHandlerMapping/**Add a '/' if required and appends the URL suffix to the name.*/protected String generatePathMapping(String beanName) {String name = (beanName.startsWith("/") ? beanName : "/" + beanName);StringBuilder path = new StringBuilder();if (!name.startsWith(this.urlPrefix)) {path.append(this.urlPrefix);}path.append(name);if (!name.endsWith(this.urlSuffix)) {path.append(this.urlSuffix);} return path.toString();} // ControllerClassNameUrlHandlerMapping Directly delegate to generatePathMappings implementation
@Overrideprotected String[] buildUrlsForHandler(String beanName, Class beanClass) {return generatePathMappings(beanClass);} // ControllerClassNameUrlHandlerMapping Get the prefix of path through buildPathPrefix
Get className through ClassUtils, such as BookController (without package name), and use cglib proxy to solve the problem together.
Convert className according to whether the case is sensitive (default caseSensitive = false;)
isMultiActionControllerType determines whether the Controller is a subclass of MultiActionController, that is, whether the controller contains multiple handlers.
/*** Generate the actual URL paths for the given controller class.* <p>Subclasses may choose to customize the paths that are generated* by overriding this method.* @param beanClass the controller bean class to generate a mapping for* @return the URL path mappings for the given controller*/protected String[] generatePathMappings(Class beanClass) {StringBuilder pathMapping = buildPathPrefix(beanClass);String className = ClassUtils.getShortName(beanClass);String path = (className.endsWith(CONTROLLER_SUFFIX) ?className.substring(, className.lastIndexOf(CONTROLLER_SUFFIX)) : className);if (path.length() > ) {if (this.caseSensitive) {pathMapping.append(path.substring(, ).toLowerCase()).append(path.substring());}else {pathMapping.append(path.toLowerCase());}}if (isMultiActionControllerType(beanClass)) {return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"};}else {return new String[] {pathMapping.toString() + "*"};}} // ControllerClassNameUrlHandlerMapping/*** Build a path prefix for the given controller bean class.* @param beanClass the controller bean class to generate a mapping for* @return the path prefix, potentially including subpackage names as path elements*/private StringBuilder buildPathPrefix(Class beanClass) {StringBuilder pathMapping = new StringBuilder();if (this.pathPrefix != null) {pathMapping.append(this.pathPrefix);pathMapping.append("/");}else {pathMapping.append("/");}if (this.basePackage != null) {String packageName = ClassUtils.getPackageName(beanClass);if (packageName.startsWith(this.basePackage)) {String subPackage = packageName.substring(this.basePackage.length()).replace('.', '/');pathMapping.append(this.caseSensitive? subPackage: subPackage.toLowerCase());pathMapping.append("/");}}return pathMapping;} // AbstractControllerUrlHandlerMapping Predicate.isMultiActionControllerType Specific implementation see ControllerTypePredicate above
/*** Determine whether the given bean class indicates a controller type* that dispatches to multiple action methods.* @param beanClass the class to introspect*/protected boolean isMultiActionControllerType(Class beanClass) {return this.predicate.isMultiActionControllerType(beanClass);}The above is the relevant knowledge about the initialization of the SpringMVC source code interpretation of HandlerMapping - AbstractDetectingUrlHandlerMapping series that the editor introduced to you. I hope it will be helpful to everyone!