2.2.2.1 处理器映射的实现架构
作为总控制器的派遣器Servlet首先会轮询处理器映射模块,查找能够处理当前请求的处理器,处理器映射模块根据当前请求的URL返回简单的控制器类型,注解控制器类型或者远程调用处理器类型。前一小节中,我们根据流程的实现分析了Bean名URL处理器映射(BeanNameUrlHandlerMapping)和缺省注解处理器映射(DefaultAnnotationHandlerMapping)。事实上,在处理器映射的实现体系结构中还有其他的实现,用来实现根据不同的规则查找相应的处理器的逻辑。不同层次的处理器映射的实现,无论是抽象类还是实现类,都关联着特殊而又完整的逻辑。如下类图所示,
图表 4‑29
上一节流程分析中讨论的Bean名URL处理器映射(BeanNameUrlHandlerMapping)和缺省注解处理器映射(DefaultAnnotationHandlerMapping),他们是经常使用到的处理器映射的实现,下面我们介绍所有的处理器映射的具体实现类的逻辑功能。
l Bean名URL处理器映射(BeanNameUrlHandlerMapping)
这个实现类通过识别Web应用程序环境中以URL为名字声明的Bean为处理器。URL是通过以斜线(/)开头的并且以斜线(/)分隔的字符串。然后,使用Bean名中声明的URL和请求的URL进行匹配,如果匹配成功,则使用匹配的Bean作为处理器返回。
l 缺省注解处理器映射(DefaultAnnotationHandlerMapping)
这个实现类通过声明在Web应用程序环境中Bean类型中的请求映射注解(@RequestMapping)来注册处理器映射的。请求映射注解声明有匹配请求URL所用的URL Pattern。然后,使用方法级别的请求映射注解中声明的URL Pattern和类型级别的请求映射注解中声明的URL Pattern结合并且匹配请求的URL,如果匹配成功,则使用匹配的Bean作为处理器返回。
l 控制器类名处理器映射(ControllerClassNameHandlerMapping)
这个实现类通过声明在Web应用程序环境中的控制器类型来注册处理器映射的。它从控制器的类型转换出控制器所服务的URL Pattern。这个转换规则是,把点号分割的具有包前缀的类名替换成斜线(/)分割的具有包前缀的字符串,再加上前缀和后缀构成URL Pattern,然后,使用得到的Pattern匹配请求的URL,如果匹配成功,则使用匹配的Bean作为处理器返回。
l 控制器Bean名处理器映射(ControllerBeanNameHandlerMapping)
这个实现类通过声明在Web应用程序环境中的控制器类型来注册处理器映射的。它从控制器的Bean名字转换出控制器所服务的URL Pattern。这个转换规则是,把Bean名字加上前缀和后缀构成URL Pattern,然后,使用得到的Pattern匹配请求的URL,如果匹配成功,则使用匹配的Bean作为处理器返回。
l 简单URL处理器映射(SimpleUrlHandlerMapping)
这个实现类通过配置一套URL Pattern到处理器的映射而实现的。它使用配置的映射中的URL Pattern匹配请求中的URL,如果匹配成功,则使用匹配URL Pattern映射的Bean作为处理器返回。
我们看到具体的实现类并不是直接实现处理器映射接口的,而是通过一系列的抽象类的实现最终完成的,在每个抽象类的实现层次上完成不同的独立的逻辑功能。下面我们分析这些抽象类和实现他们的具体实现类是如何分工并且最终完成必要的业务逻辑的。
l 抽象处理器映射(AbstractHandlerMapping)
抽象处理器映射是处理器映射实现中的最底层的实现,它直接实现处理器映射接口,并且继承Web应用程序环境支持对象。它提供了配置缺省处理器以及应用到所有处理器上的处理器拦截器的功能。
它把具体如何取得一个处理器抽象并且留给子类进行特殊化的实现。
l 抽象URL处理器映射(AbstractUrlHandlerMapping)
抽象URL处理器映射继承自抽象处理器映射,实现了取得一个处理器的抽象方法,提供了功能根据URL进行匹配处理器的功能。也提供了根据URL查找应用在处理器上特殊的拦截器。并且提供了方法实现注册URL到处理器的映射。
如何获得URL到处理器的映射的逻辑留给子类进行完成。
简单URL处理器映射就是通过应用程序环境中Bean串联配置直接注射URL到处理器映射来实现抽象URL处理器映射的。
l 抽象探测URL处理器映射(AbstractDetectingUrlHandlerMapping)
抽象探测URL处理器映射通过一定的规则在Web应用程序环境中自动发现URL到处理器的映射。
使用什么样的规则在Web应用程序环境中自动发现URL到处理器的映射并没有直接实现,因为这会有很多的映射规则,并且根据需求可以自由扩展。这个规则留给子类进行实现。
Bean名URL处理器映射就是根据把Bean名声明作为URL来发现处理器的。而缺省注解处理器映射是根据声明在控制器中的请求映射注解中包含的URL Pattern信息来解析处理器的。
l 抽象控制器URL处理器映射(AbstractControllerUrlHandlerMapping)
这是抽象探测URL处理器映射的另外一个实现,这个实现也是一个抽象的实现,它是通过在Bean环境中找到合适的控制器类型,根据一定的规则将控制器类型映射到一个或者多个URL Pattern来实现的。
它有两个具体的实现类,控制器类名处理器映射是根据类名解析出映射的URL Pattern。而控制器Bean名处理器映射则是根据控制器在Web应用程序环境中声明的Bean名映射到URL Pattern的。
在上一节的分析中,我们已经对Bean名URL处理器映射(BeanNameUrlHandlerMapping)和缺省注解处理器映射(DefaultAnnotationHandlerMapping)以及他们的父类进行分析。下面我们将对剩余的其他的类进行代码分析,首先回顾一下抽象探测URL处理器映射的实现,如下代码注释,
protected abstract String[] determineUrlsForHandler(String beanName);
它留下了一个抽象方法,对于Web应用程序环境中的每一个Bean,都将使用此方法找到Bean所映射到的URL Pattern。子类需要根据具体的映射规则来实现这个方法。除了上一节中分析的Bean名URL处理器映射和缺省注解处理器映射实现了这个方法外,存在另外一套根据控制器类型映射的实现。这套根据控制器类型映射的实现包含一个抽象实现和两个具体实现,抽象实现是抽象控制器处理器映射。如下代码所示,
public abstract class AbstractControllerUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
// 通过控制器接口类型或者控制器注解类型查找简单控制器和注解控制器的实用类
private ControllerTypePredicate predicate = new AnnotationControllerTypePredicate();
// 声明的包中的控制器不会作为处理器进行注册
private Set<String> excludedPackages = Collections.singleton("org.springframework.web.servlet.mvc");
// 声明的类不会作为处理器进行注册
private Set<Class> excludedClasses = Collections.emptySet();
// 实现根据Bean映射出URL的逻辑
@Override
protected String[] determineUrlsForHandler(String beanName) {
// 取得Bean的类型
Class beanClass = getApplicationContext().getType(beanName);
// 判断是否Bean可以作为控制器处理器
if (isEligibleForMapping(beanName, beanClass)) {
// 根据Bean名字或者类型名映射出URL的逻辑
return buildUrlsForHandler(beanName, beanClass);
}
else {
return null;
}
}
protected boolean isEligibleForMapping(String beanName, Class beanClass) {
// 如果Bean类型是空,则不映射此Bean
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;
}
// 如果Bean的包配置为排除包,则不映射此Bean
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;
}
// 如果Bean的包配置为排除类,则不映射此Bean
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);
}
protected boolean isControllerType(Class beanClass) {
return this.predicate.isControllerType(beanClass);
}
protected boolean isMultiActionControllerType(Class beanClass) {
return this.predicate.isMultiActionControllerType(beanClass);
}
// 子类可以选择根据Bean名字还是根据Bean类来映射URL Pattern
protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass);
}
抽象控制器处理器映射有两个实现,一个是根据Bean名字映射到URL Pattern的实现,另外一个是根据Bean的类型映射到URL Pattern的实现。
以下是控制器Bean名处理器映射(ControllerBeanNameHandlerMapping)的代码注释,
public class ControllerBeanNameHandlerMapping extends AbstractControllerUrlHandlerMapping {
private String urlPrefix = "";
private String urlSuffix = "";
@Override
protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
List<String> urls = new ArrayList<String>();
// 根据Bean名产生URL Pattern
urls.add(generatePathMaping(beanName));
// 对于Bean名的别名,以同样的规则产生URL Pattern
String[] aliases = getApplicationContext().getAliases(beanName);
for (String alias : aliases) {
urls.add(generatePathMapping(alias));
}
// 返回URL Pattern数组
return StringUtils.toStringArray(urls);
}
/**
* Prepends a '/' if required and appends the URL suffix to the name.
*/
protected String generatePathMapping(String beanName) {
// 如果bean名不是以斜线(/)开头,则增加斜线(/)
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();
}
}
以下是控制器类名处理器映射(ControllerClassNameHandlerMapping)的代码注释,public class ControllerClassNameHandlerMapping extends AbstractControllerUrlHandlerMapping {
// 控制器名的后缀
private static final String CONTROLLER_SUFFIX = "Controller";
// 通过类型映射的路径是否保持大写字母的存在
private boolean caseSensitive = false;
private String pathPrefix;
private String basePackage;
public void setPathPrefix(String prefixPath) {
this.pathPrefix = prefixPath;
// 一个路径应该保证有斜线(/)开头,但是没有斜线(/)结尾
if (StringUtils.hasLength(this.pathPrefix)) {
if (!this.pathPrefix.startsWith("/")) {
this.pathPrefix = "/" + this.pathPrefix;
}
if (this.pathPrefix.endsWith("/")) {
this.pathPrefix = this.pathPrefix.substring(0, this.pathPrefix.length() - 1);
}
}
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
// 设置缺省的包前缀
if (StringUtils.hasLength(this.basePackage) && !this.basePackage.endsWith(".")) {
this.basePackage = this.basePackage + ".";
}
}
@Override
protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
// 仅仅使用类名进行映射
return generatePathMappings(beanClass);
}
protected String[] generatePathMappings(Class beanClass) {
// 产生路径前缀
StringBuilder pathMapping = buildPathPrefix(beanClass);
// 取得不包含包名的类名
String className = ClassUtils.getShortName(beanClass);
// 如果以控制器后缀(Controller)结尾,则移除控制器后缀
String path = (className.endsWith(CONTROLLER_SUFFIX) ?
className.substring(0, className.lastIndexOf(CONTROLLER_SUFFIX)) : className);
if (path.length() > 0) {
// 如果保持路径大小写,则把类名的第一个字符小写
if (this.caseSensitive) {
pathMapping.append(path.substring(0, 1).toLowerCase()).append(path.substring(1));
}
// 否则使所有路径字符变成小写
else {
pathMapping.append(path.toLowerCase());
}
}
// 如果是多行为控制器类型,则加URL本身和所有的子URL
if (isMultiActionControllerType(beanClass)) {
return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"};
}
// 否则只加URL本身
else {
return new String[] {pathMapping.toString() + "*"};
}
}
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;
}
}
抽象URL处理器映射有另外一个具体实现简单URL处理器映射,它根据配置的URL Pattern到处理器的映射来查找处理器,如下代码所示,public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
private final Map<String, Object> urlMap = new HashMap<String, Object>();
// 通过属性配置URL到Bean名的映射
public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}
// 配置URL到Bean的映射
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
public Map<String, ?> getUrlMap() {
return this.urlMap;
}
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
// 初始化的时候注册处理器
registerHandlers(this.urlMap);
}
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
// 如果配置的处理器映射为空,则警告
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
// 对于没一个配置的URL到处理器的映射,如果URL不是以斜线(/)开头,则追加斜线开头,则注册处理器
for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
String url = entry.getKey();
Object handler = entry.getValue();
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
}
}
}
}