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