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系列初始化的相关知识,希望对大家有所帮助!
新闻热点
疑难解答