SpringMVC源码解读之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化

 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.
*/
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}

这边一样是调用AbstractHandlerMapping的initApplicationContext初始化拦截器.

主角上场,detectHandlers,扫描Handlers

// AbstractDetectingUrlHandlerMapping
/**
* Register all handlers found in the current ApplicationContext.
* 

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 "/".
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List urls = new ArrayList();
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}.
*/
@Override
protected 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
@Override
protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
List urls = new ArrayList();
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实现

@Override
protected 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.
* 

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系列初始化的相关知识,希望对大家有所帮助!

你可能感兴趣的:(SpringMVC源码解读之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化)