SpringMVC源码解读之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化
作者:出门向左 发布时间:2023-02-12 16:14:21
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.
* <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 "/".
*/
@Override
protected 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}.
*/
@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<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实现
@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.
* <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系列初始化的相关知识,希望对大家有所帮助!
猜你喜欢
- 因为最近重新看了泛型,又看了些反射,导致我对Object、T(以下代指泛型)、?产生了疑惑。我们先来试着理解一下Object类,学习Java
- 本文为大家分享了Java实现文件上传下载功能的具体代码,供大家参考,具体内容如下前端通过form表单的enctype属性,将数据传递方式修改
- 1. Spring简介Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。2. Spring的优势 1.方便解耦, 简
- 本文实例讲述了Java针对ArrayList自定义排序的2种实现方法。分享给大家供大家参考,具体如下:Java中实现对list的自定义排序主
- 这篇文章主要介绍了Java如何基于ProcessBuilder类调用外部程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的
- 本文需求实现了java通过方向键控制小球移动的具体过程,供大家参考,具体内容如下需求分析:第一 要画出一个小球第二 要能通过控制方向键控制它
- 有很多应用场景,用到了接口动态实现,下面举几个典型的应用:1、mybatis / jpa 等orm框架,可以在接口上加注解进行开发,不需要编
- 本文实例讲述了Java面向接口编程之简单工厂模式。分享给大家供大家参考,具体如下:一 代码interface Output{ /
- 建议缓存放到 service 层,你可以自定义自己的 BaseServiceImpl 重写注解父类方法,继承自己的实现。为了方便,这里我们将
- 前言今天遇到一个问题,我们有个ip.dat二进制文件,通过里面内容可以解析ip所在的地域信息,本地单元测试都是OK的,部署到测试环境后,发现
- 前言应用系统需要通过Cache来缓存不经常改变得数据来提高系统性能和增加系统吞吐量,避免直接访问数据库等低速存储系统。缓存的数据通常存放在访
- 目前市面上流行的爬虫以python居多,简单了解之后,觉得简单的一些页面的爬虫,主要就是去解析目标页面(html)。那么就在想,java有没
- 前言最近写一个东东,可能会考虑到字符串拼接,想了几种方法,但对性能未知,所以下面就来测试下面,话不多说了,来一起看看详细的介绍吧。示例代码p
- 一、泛型的概念1.1 基础案例泛型在Java中的应用非常广泛,最常见则是在集合容器中,先看下基础用法:public class Generi
- 1概述众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统
- final File imageFile = new File(getCacheDir().getPath() + "/img/&
- 需求在配置类中,从application.properties中读取一个复杂list。如List<Person>或者初始化一个m
- JPanel是面板组件,非顶层容器,一个界面只有可以有一个JFrame窗体组件,但可以有多个Jpanel面板,而JPanel上也可以使用Fl
- 短网址(Short URL) ,顾名思义就是看起来很短的网址。自从twitter推出短网址服务以后,各大互联网公司都推出了自己的短网址服务。
- 引言一个复杂的分布式系统,用户发起一个请求,这个请求可能调用几十到几百个服务,经过很多业务层,而每个业务又是多个机器集群,一个请求具体被随机