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