SpringMVC源码解读之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化
AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInternal进行分发.
共有5个子类,一个抽象类.
与SimpleUrlHandlerMapping类似,通过覆写initApplicationContext,然后调用detectHandlers进行初始化.
detectHandlers通过BeanFactoryUtils扫描应用下的Object,然后预留determineUrlsForHandler给子类根据Handler生成对应的url.
注册使用的registerHandler依然由AbstractUrlHandlerMapping提供.
//AbstractDetectingUrlHandlerMapping
/**
*Callsthe{@link#detectHandlers()}methodinadditiontothe
*superclass'sinitialization.
*/
@Override
publicvoidinitApplicationContext()throwsApplicationContextException{
super.initApplicationContext();
detectHandlers();
}
这边一样是调用AbstractHandlerMapping的initApplicationContext初始化拦截器.
主角上场,detectHandlers,扫描Handlers
//AbstractDetectingUrlHandlerMapping
/**
*RegisterallhandlersfoundinthecurrentApplicationContext.
*<p>TheactualURLdeterminationforahandlerisuptotheconcrete
*{@link#determineUrlsForHandler(String)}implementation.Abeanfor
*whichnosuchURLscouldbedeterminedissimplynotconsideredahandler.
*@throwsorg.springframework.beans.BeansExceptionifthehandlercouldn'tberegistered
*@see#determineUrlsForHandler(String)
*/
protectedvoiddetectHandlers()throwsBeansException{
if(logger.isDebugEnabled()){
logger.debug("LookingforURLmappingsinapplicationcontext:"+getApplicationContext());
}
String[]beanNames=(this.detectHandlersInAncestorContexts?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(),Object.class):
getApplicationContext().getBeanNamesForType(Object.class));
//TakeanybeannamethatwecandetermineURLsfor.
for(StringbeanName:beanNames){
String[]urls=determineUrlsForHandler(beanName);
if(!ObjectUtils.isEmpty(urls)){
//URLpathsfound:Let'sconsideritahandler.
registerHandler(urls,beanName);
}
else{
if(logger.isDebugEnabled()){
logger.debug("Rejectedbeanname'"+beanName+"':noURLpathsidentified");
}
}
}
}
这边预留的模板方法定义如下:
/**
*DeterminetheURLsforthegivenhandlerbean.
*@parambeanNamethenameofthecandidatebean
*@returntheURLsdeterminedforthebean,
*or{@codenull}oranemptyarrayifnone
*/
protectedabstractString[]determineUrlsForHandler(StringbeanName);
我们再来看看模板方法在BeanNameUrlHandlerMapping和AbstractControllerUrlHandlerMapping中的实现吧.
BeanNameUrlHandlerMapping非常简单,就实现了determineUrlsForHandler.
其中的alias应该是应该就是通过beanName在配置文件中配置的.
//BeanNameUrlHandlerMapping
/**
*ChecksnameandaliasesofthegivenbeanforURLs,startingwith"/".
*/
@Override
protectedString[]determineUrlsForHandler(StringbeanName){
List<String>urls=newArrayList<String>();
if(beanName.startsWith("/")){
urls.add(beanName);
}
String[]aliases=getApplicationContext().getAliases(beanName);
for(Stringalias:aliases){
if(alias.startsWith("/")){
urls.add(alias);
}
}
returnStringUtils.toStringArray(urls);
}
再来看看AbstractControllerUrlHandlerMapping中的实现
isEligibleForMapping判断controller是否被排除在外(通过包package排除或类class排除).
buildUrlsForHandler由子类实现具体的url生成规则
isControllerType判断是否Controller的子类
buildUrlsForHandler预留给子类生产url的模板方法.
//AbstractControllerUrlHandlerMapping
/**
*Thisimplementationdelegatesto{@link#buildUrlsForHandler},
*providedthat{@link#isEligibleForMapping}returns{@codetrue}.
*/
@Override
protectedString[]determineUrlsForHandler(StringbeanName){
ClassbeanClass=getApplicationContext().getType(beanName);
if(isEligibleForMapping(beanName,beanClass)){
returnbuildUrlsForHandler(beanName,beanClass);
}
else{
returnnull;
}
}
//AbstractControllerUrlHandlerMapping
/**判断controller是否被排除在外(通过包package排除或类class排除).
*Determinewhetherthespecifiedcontrollerisexcludedfromthismapping.
*@parambeanNamethenameofthecontrollerbean
*@parambeanClasstheconcreteclassofthecontrollerbean
*@returnwhetherthespecifiedclassisexcluded
*@see#setExcludedPackages
*@see#setExcludedClasses
*/
protectedbooleanisEligibleForMapping(StringbeanName,ClassbeanClass){
if(beanClass==null){
if(logger.isDebugEnabled()){
logger.debug("Excludingcontrollerbean'"+beanName+"'fromclassnamemapping"+
"becauseitsbeantypecouldnotbedetermined");
}
returnfalse;
}
if(this.excludedClasses.contains(beanClass)){
if(logger.isDebugEnabled()){
logger.debug("Excludingcontrollerbean'"+beanName+"'fromclassnamemapping"+
"becauseitsbeanclassisexplicitlyexcluded:"+beanClass.getName());
}
returnfalse;
}
StringbeanClassName=beanClass.getName();
for(StringpackageName:this.excludedPackages){
if(beanClassName.startsWith(packageName)){
if(logger.isDebugEnabled()){
logger.debug("Excludingcontrollerbean'"+beanName+"'fromclassnamemapping"+
"becauseitsbeanclassisdefinedinanexcludedpackage:"+beanClass.getName());
}
returnfalse;
}
}
returnisControllerType(beanClass);
}
//AbstractControllerUrlHandlerMapping
/**
*Determinewhetherthegivenbeanclassindicatesacontrollertype
*thatissupportedbythismappingstrategy.
*@parambeanClasstheclasstointrospect
*/
protectedbooleanisControllerType(ClassbeanClass){
returnthis.predicate.isControllerType(beanClass);
}
//ControllerTypePredicate
这边提供2个api,分别判断是Controller的子类还是MultiActionController的子类.
/**
*Internalhelperclassthatidentifiescontrollertypes.
*
*@authorJuergenHoeller
*@since..
*/
classControllerTypePredicate{
publicbooleanisControllerType(ClassbeanClass){
returnController.class.isAssignableFrom(beanClass);
}
publicbooleanisMultiActionControllerType(ClassbeanClass){
returnMultiActionController.class.isAssignableFrom(beanClass);
}
}
预留生成url的模板方法
//AbstractControllerUrlHandlerMapping /** *Abstracttemplatemethodtobeimplementedbysubclasses. *@parambeanNamethenameofthebean *@parambeanClassthetypeofthebean *@returntheURLsdeterminedforthebean */ protectedabstractString[]buildUrlsForHandler(StringbeanName,ClassbeanClass);
再来看看AbstractControllerUrlHandlerMapping的2个实现ControllerBeanNameUrlHandlerMapping和ControllerClassNameUrlHandlerMapping.
其实这两个,很简单,一个是根据beanName来生产url,一个是根据className来生产url.
//ControllerBeanNameUrlHandlerMapping
@Override
protectedString[]buildUrlsForHandler(StringbeanName,ClassbeanClass){
List<String>urls=newArrayList<String>();
urls.add(generatePathMapping(beanName));
String[]aliases=getApplicationContext().getAliases(beanName);//也获取配置的别名
for(Stringalias:aliases){
urls.add(generatePathMapping(alias));
}
returnStringUtils.toStringArray(urls);
}
//ControllerBeanNameUrlHandlerMapping
/**对path添加前后缀,还有/
*Prependsa'/'ifrequiredandappendstheURLsuffixtothename.
*/
protectedStringgeneratePathMapping(StringbeanName){
Stringname=(beanName.startsWith("/")?beanName:"/"+beanName);
StringBuilderpath=newStringBuilder();
if(!name.startsWith(this.urlPrefix)){
path.append(this.urlPrefix);
}
path.append(name);
if(!name.endsWith(this.urlSuffix)){
path.append(this.urlSuffix);
}
returnpath.toString();
}
//ControllerClassNameUrlHandlerMapping
直接委托给generatePathMappings实现
@Override
protectedString[]buildUrlsForHandler(StringbeanName,ClassbeanClass){
returngeneratePathMappings(beanClass);
}
//ControllerClassNameUrlHandlerMapping
通过buildPathPrefix获取path的前缀
通过ClassUtils获取className,如BookController(不带包名),同时使用cglib代理的问题一并解决
根据大小写是否敏感,转换className(默认caseSensitive=false;)
isMultiActionControllerType判断Controller是否MultiActionController的子类,就是controller是否包含多个handler
/**
*GeneratetheactualURLpathsforthegivencontrollerclass.
*<p>Subclassesmaychoosetocustomizethepathsthataregenerated
*byoverridingthismethod.
*@parambeanClassthecontrollerbeanclasstogenerateamappingfor
*@returntheURLpathmappingsforthegivencontroller
*/
protectedString[]generatePathMappings(ClassbeanClass){
StringBuilderpathMapping=buildPathPrefix(beanClass);
StringclassName=ClassUtils.getShortName(beanClass);
Stringpath=(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)){
returnnewString[]{pathMapping.toString(),pathMapping.toString()+"/*"};
}
else{
returnnewString[]{pathMapping.toString()+"*"};
}
}
//ControllerClassNameUrlHandlerMapping
/**
*Buildapathprefixforthegivencontrollerbeanclass.
*@parambeanClassthecontrollerbeanclasstogenerateamappingfor
*@returnthepathprefix,potentiallyincludingsubpackagenamesaspathelements
*/
privateStringBuilderbuildPathPrefix(ClassbeanClass){
StringBuilderpathMapping=newStringBuilder();
if(this.pathPrefix!=null){
pathMapping.append(this.pathPrefix);
pathMapping.append("/");
}
else{
pathMapping.append("/");
}
if(this.basePackage!=null){
StringpackageName=ClassUtils.getPackageName(beanClass);
if(packageName.startsWith(this.basePackage)){
StringsubPackage=packageName.substring(this.basePackage.length()).replace('.','/');
pathMapping.append(this.caseSensitive?subPackage:subPackage.toLowerCase());
pathMapping.append("/");
}
}
returnpathMapping;
}
//AbstractControllerUrlHandlerMapping
predicate.isMultiActionControllerType具体实现看上面的ControllerTypePredicate
/**
*Determinewhetherthegivenbeanclassindicatesacontrollertype
*thatdispatchestomultipleactionmethods.
*@parambeanClasstheclasstointrospect
*/
protectedbooleanisMultiActionControllerType(ClassbeanClass){
returnthis.predicate.isMultiActionControllerType(beanClass);
}
以上所述是小编给大家介绍的SpringMVC源码解读之HandlerMapping-AbstractDetectingUrlHandlerMapping系列初始化的相关知识,希望对大家有所帮助!