转载请注明出处:https://blog.csdn.net/qq_26443737/article/details/90646532
HandlerMapping是DispatcherServlet中直接初始化的九大组件之一。HandlerMapping的作用之一是根据request找到相应的处理器Hanlder(比如,一个@RequestMapping就是一个Handler)和Interceptors。HandlerMapping接口里只有一个方法。
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
getHandler返回的永远是一个HandlerExecutionChain类型的对象,包括一个handler和interceptors(如果有的话)。
HandlerMapping的实现类需要注册到Spring MVC的容器中才能使用。注册方式是在配置文件中配置一个Bean,Spring MVC会按照类型将它注册到HandlerMapping中。如果没有在文件中配置,则使用DispatcherServlet默认的配置,在包org.springframework.web.servlet的同名文件DispatcherServlet.properties中。
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.
BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc
.annotation.DefaultAnnotationHandlerMapping
默认配置的实现类为BeanNameUrlHandlerMapping以及DefaultAnnotationHandlerMapping。
BeanNameUrlHandlerMapping继承自AbstractDetectingUrlHandlerMapping(注:Spring MVC中类名前缀包含Abstract的基本都是抽象类),它不是HanlerMapping的直接实现,而是与HandlerMapping接口隔了好几个抽象类。
BeanNameUrlHandlerMapping的继承结构如图1。
下面将简单分析BeanNameUrlHanderMapping的几个父类及相应的作用。
AbstractHandlerMapping是HandlerMapping的抽象实现,另外该抽象类还继承一个类WebApplicationObjectSupport,实现了另外一个接口Ordered。
继承WebApplicationObjectSupport的目的之一是为了在HandlerMapping具体子类创建后进行初始化,调用相应的入口方法获取Servlet中的handlers和interceptors,并将其和对应的url放入指定的map中。
实现Ordered接口是为了对HandlerMapping进行排序,以便在处理具有相同部分的url时,使用合适的HandlerMapping(具体可参考:《看透Spring MVC源代码与实践》 第11章)。
AbstractHandlerMapping在initApplicationcontext()方法(继承WebApplicationObjectSupport的子类会在对象创建后调用该方法)保存所用配置的Interceptors,在获取到Handler后会自己根据从request提取的lookupPath将相应的Interceptors装配上去。
@Override
protectedvoidinitApplicationContext()throwsBeansException{
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.mappedInterceptors);
initInterceptors();
}
AbstractHandlerMapping另外一个重要作用是实现了getHandler(HttpServletRequest request)方法,根据request返回对应的HandlerExcutionChain对象:
@Override
publicfinalHandlerExecutionChaingetHandler(HttpServletRequestrequest
)throwsException{
Objecthandler=getHandlerInternal(request);
if(handler==null){
handler=getDefaultHandler();
}
if(handler==null){
returnnull;
}
//如果handler是String类型,则根据该字符串获取从容器中获取对应的实例
if(handlerinstanceofString){
StringhandlerName=(String)handler;
handler=getApplicationContext().getBean(handlerName);
}
returngetHandlerExecutionChain(handler,request);
}
getHandler方法调用getHandlerInternal方法获取handler,但是getHandlerInternal方法在此类中定义为抽象方法,具体实现交由子类解决。
AbstractUrlHandlerMapping继承AbstractHandlerMapping,其主要作用是根据request的url寻找对应的handler。该类维护一个rootHandler属性和一个handlerMap属性,将"/"对应的handler放在rootHandler,将其他形式的url放在handlerMap里。
该类有两个重要方法:getHandlerInternal(HttpServletRequest request)和registerHandler(String[] urlPaths, Object handler)。getHandlerInternal方法就是从handlerMap中根据url获取handler,registerHandler是将url和对应的handler放到handlerMap或rootHandler里。
@Override
protectedObjectgetHandlerInternal(HttpServletRequestrequest)throwsException{
StringlookupPath=getUrlPathHelper().getLookupPathForRequest(request);
Objecthandler=lookupHandler(lookupPath,request);
if(handler==null){
ObjectrawHandler=null;
if("/".equals(lookupPath)){
rawHandler=getRootHandler();
}
if(rawHandler==null){
rawHandler=getDefaultHandler();
}
if(rawHandler!=null){
//如果是String类型,则从容器中获取对应的实例
if(rawHandlerinstanceofString){
StringhandlerName=(String)rawHandler;
rawHandler=getApplicationContext().getBean(handlerName);
}
//验证handler,模板方法,子类未做具体事情,目前无效
validateHandler(rawHandler,request);
//给hanlder加上两个interceptor,主要用于在request上添加和url相关的一些
属性
handler=buildPathExposingHandler(rawHandler,lookupPath,lookupPath
,null);
}
}
if(handler!=null&&logger.isDebugEnabled()){
logger.debug("Mapping["+lookupPath+"]to"+handler);
}
elseif(handler==null&&logger.isTraceEnabled()){
logger.trace("Nohandlermappingfoundfor["+lookupPath+"]");
}
returnhandler;
}
protectedvoidregisterHandler(String[]urlPaths,StringbeanName
)throwsBeansException,IllegalStateException{
Assert.notNull(urlPaths,"URLpatharraymustnotbenull");
for(StringurlPath:urlPaths){
registerHandler(urlPath,beanName);
}
}
protectedvoidregisterHandler(StringurlPath,Objecthandler
)throwsBeansException,IllegalStateException{
Assert.notNull(urlPath,"URLpathmustnotbenull");
Assert.notNull(handler,"Handlerobjectmustnotbenull");
ObjectresolvedHandler=handler;
//如果lazyInitHandlers没有设置且handler是String类型,则从容器中获取相应的h
andler
if(!this.lazyInitHandlers&&handlerinstanceofString){
StringhandlerName=(String)handler;
if(getApplicationContext().isSingleton(handlerName)){
resolvedHandler=getApplicationContext().getBean(handlerName);
}
}
//一个url不能具有多个handler
ObjectmappedHandler=this.handlerMap.get(urlPath);
if(mappedHandler!=null){
if(mappedHandler!=resolvedHandler){
thrownewIllegalStateException(
"Cannotmap"+getHandlerDescription(handler)
+"toURLpath["+urlPath+
"]:Thereisalready"+getHandlerDescription
(mappedHandler)+"mapped.");
}
}
else{
if(urlPath.equals("/")){
if(logger.isInfoEnabled()){
logger.info("Rootmappingto"+getHandlerDescription(handler
));
}
setRootHandler(resolvedHandler);
}
elseif(urlPath.equals("/*")){
if(logger.isInfoEnabled()){
logger.info("Defaultmappingto"+getHandlerDescription
(handler));
}
//defaultHanlder属性在父类中
setDefaultHandler(resolvedHandler);
}
else{
this.handlerMap.put(urlPath,resolvedHandler);
if(logger.isInfoEnabled()){
logger.info("MappedURLpath["+urlPath+"]onto"
+getHandlerDescription(handler));
}
}
}
}
getHandlerInteranl方法由父类调用,那么谁来调用registerHandler呢?答案是由子类调用,这样子类就可以通过自己的方式创建handler组件(谁可以成为handler,由子类决定)。
该类继承AbstractUrlHandlerMapping。由这个类的名字就可以猜到它的作用是找handler。这个类重写了AbstractHandlerMapping中的initApplicationContext方法,在该方法中调用了父类方法,并且调用了自己的detectHandlers方法。
@Override
publicvoidinitApplicationContext()throwsApplicationContextException{
super.initApplicationContext();
detectHandlers();
}
detectHandlers方法是找到应用中所有的handler,并掉用父类registerHandler方法将handler放到handlerMap中。
protectedvoiddetectHandlers()throwsBeansException{
if(logger.isDebugEnabled()){
logger.debug("LookingforURLmappingsinapplicationcontext:"
+getApplicationContext());
}
//获取应用中所有的beanName
String[]beanNames=(this.detectHandlersInAncestorContexts?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors
(getApplicationContext(),Object.class):
getApplicationContext().getBeanNamesForType(Object.class));
//将handler和url对应起来,放入handlerMap中
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");
}
}
}
}
determineUrlsForHandler方法是一个抽象方法,交由子类具体实现。该方法根据BeanName决定谁是handler,并且对应的url是哪些。
最后是具体的子类。这个子类只有一个方法,重写父类determineUrlsForHandler方法。
@Override
protectedString[]determineUrlsForHandler(StringbeanName){
List<String>urls=newArrayList<String>();
//以"/"开头的beanName,默认其为handler
if(beanName.startsWith("/")){
urls.add(beanName);
}
String[]aliases=getApplicationContext().getAliases(beanName);
for(Stringalias:aliases){
if(alias.startsWith("/")){
urls.add(alias);
}
}
returnStringUtils.toStringArray(urls);
}
可以看到,最终具体子类BeamNameUrlHandlerMapping的内容很少。因为其他的内容已经被分层的父类一层一层实现了。这也符合类的单一职责性。另外,BeamNameUrlHandlerMapping的整体继承结构设计可以理解为handler的获取与handlerMap的检测,handler的获取在AbstractUrlHandlerMapping及其上层实现,handlerMap的检测在其下层实现,而AbstractUrlHandlerMapping是区分上下层的业务以及连接整体结构的枢纽,它包含两个方法getHandlerInternal和registerHandler,分别为父类和子类方法所调用。
通过以上分析,可以看到整个HandlerMapping接口的具体实现逻辑清晰,每个类分工明确。这是值得我们学习和借鉴的。
HandlerMapping的另外一个具体实现是DefaultAnnotationHandlerMapping。它和BaseNameUrlHandlerMapping有共同的父类继承结构,如图2所示。该类是找到以@RequestMapping注解声明的handler,不过现在已被声明为弃用。因为有了另外一个类RequestMappingHandlerMapping,该类从AbstractHandlerMapping子类开始有了新的继承关系和实现(可参考图3)。
虽然DefaultAnnotationHandlerMapping已经被声明弃用,但是大家可以自己去梳理该类的源码,加深理解。
HandlerMapping在初始化(创建实例对象,找到url和对应的handler并放入handlerMap中)过程中,方法调用关系如图4所示。
由图4可看出,Spring MVC九大组件的入口在DispacterServlet的onRefresh方法中,在初始化过程中,如果没有配置相应的组件,则使用默认配置(上文中有提到)。然后在调用Bean工厂创建实例的过程中,通过WebApplicationObjectSupport中setApplicationContext方法调用HandlerMapping接口具体实现的initApplication方法。因为在BeanNameUrlHandlerMapping中没有重写initApplication方法,其直接父类也没有重写,因此调用祖父类AbstractDetectingUrlHandlerMapping中的initApplication方法。该方法首先调用了父类AbtreactHandlerMapping的initApplication(为什么调用父类的initApplication方法?上文中有提到,父类的initApplicaiton方法设置了应用的interceptors)方法,然后再调用子类的detectHandler方法,该方法又调用子类的determineUrlsForHandler方法。
Handler的获取,可以调用HandlerMapping接口的getHandler(HttpServletRequest request)方法。
参考:韩路彪 《看透Spring MVC源代码分析与实践》