AbstractDetectingUrlHandlerMapping是通過掃描方式注冊Handler,收到請求時由AbstractUrlHandlerMapping的getHandlerInternal進行分發.
共有5個子類,一個抽象類.
與SimpleUrlHandlerMapping類似,通過覆寫initApplicationContext,然后調用detectHandlers進行初始化.
detectHandlers通過BeanFactoryUtils掃描應用下的Object,然后預留determineUrlsForHandler給子類根據Handler生成對應的url.
注冊使用的registerHandler依然由AbstractUrlHandlerMapping提供.
// AbstractDetectingUrlHandlerMapping/*** Calls the {@link #detectHandlers()} method in addition to the* superclass's initialization.*/@Overridepublic void initApplicationContext() throws ApplicationContextException {super.initApplicationContext();detectHandlers();}
這邊一樣是調用AbstractHandlerMapping的initApplicationContext初始化攔截器.
主角上場,detectHandlers,掃描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.beanNamesForTypeIncludingAncestors(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");}}}}
這邊預留的模板方法定義如下:
/*** 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); 我們再來看看模板方法在BeanNameUrlHandlerMapping和AbstractControllerUrlHandlerMapping中的實現吧.BeanNameUrlHandlerMapping非常簡單,就實現了determineUrlsForHandler.其中的alias應該是應該就是通過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);}
再來看看AbstractControllerUrlHandlerMapping中的實現
isEligibleForMapping判斷controller是否被排除在外(通過包package排除或類class排除).
buildUrlsForHandler由子類實現具體的url生成規則
isControllerType判斷是否Controller的子類
buildUrlsForHandler預留給子類生產url的模板方法.
// 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/**判斷controller是否被排除在外(通過包package排除或類class排除).* Determine whether the specified 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("Excluding 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("Excluding 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("Excluding 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這邊提供2個api,分別判斷是Controller的子類還是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);}}
預留生成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);
再來看看AbstractControllerUrlHandlerMapping的2個實現ControllerBeanNameUrlHandlerMapping和ControllerClassNameUrlHandlerMapping.
其實這兩個,很簡單,一個是根據beanName來生產url,一個是根據className來生產url.
// ControllerBeanNameUrlHandlerMapping@Overrideprotected String[] buildUrlsForHandler(String beanName, Class beanClass) {List<String> urls = new ArrayList<String>();urls.add(generatePathMapping(beanName));String[] aliases = getApplicationContext().getAliases(beanName);// 也獲取配置的別名for (String alias : aliases) {urls.add(generatePathMapping(alias));}return StringUtils.toStringArray(urls);} // ControllerBeanNameUrlHandlerMapping/**對path添加前后綴,還有/* Prepends 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
直接委托給generatePathMappings實現
@Overrideprotected String[] buildUrlsForHandler(String beanName, Class beanClass) {return generatePathMappings(beanClass);} // ControllerClassNameUrlHandlerMapping
通過buildPathPrefix獲取path的前綴
通過ClassUtils獲取className,如BookController(不帶包名),同時使用cglib代理的問題一并解決
根據大小寫是否敏感,轉換className(默認caseSensitive = false;)
isMultiActionControllerType判斷Controller是否MultiActionController的子類,就是controller是否包含多個handler
/*** 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具體實現看上面的ControllerTypePredicate
/*** 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);}
以上所述是小編給大家介紹的SpringMVC源碼解讀之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化的相關知識,希望對大家有所幫助!
新聞熱點
疑難解答