欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-03-08 20:01:10
 abstractdetectingurlhandlermapping是通过扫描方式注册handler,收到请求时由abstracturlhandlermappi...

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