HandlerMapping接口负责根据request请求找到对应的Handler处理器及Interceptor拦截器,并将它们封装在HandlerExecutionChain对象内,返回给中央调度器。
SpringMVC,有很多默认配置,具体可参考jar包内的DispatcherServlet.properties文件。
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
......
这里,我们只关注HandlerMapping接口的几个实现,主要是BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping、RequestMappingHandlerMapping这三个类。
HandlerMapping接口,仅有一个方法,就是HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
- BeanNameUrlHandlerMapping
bean的名字或别名,必须以/开头,必须实现Controller接口。配置方式如下
<!-- 1:BeanNameUrlHandlerMapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 2:ViewResolver,视图解析器。-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3:Controller -->
<bean name="/beanNameUrlController" class="com.zhao.web.MessageControl"/>
public class MessageControl implements Controller {
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponseresponse) throws Exception {
ModelAndView mav = new ModelAndView("index");
mav.addObject("message", "默认的映射处理器示例");
return mav;
}
}
- SimpleUrlHandlerMapping
配置方式如下
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/simpleUrlHandlerMapping.do">welcomeController</prop>
</props>
</property>
</bean>
<bean id="welcomeController" class="com.epoch.controller.SimpleUrlHandlerMappingController" />
public class SimpleUrlHandlerMappingController implements Controller {
protected final Log logger = LogFactory.getLog(this.getClass());
//@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("run here");
return null;
}
}
- RequestMappingHandlerMapping
RequestMappingHandlerMapping,是现在常用的方式。上面两种方式,已经不常用了。
RequestMappingHandlerMapping,一般结合配置文件和java标签@Controller、@RequestMapping一起使用。如下:
<!-- 注册HandlerMapper、HandlerAdapter两个映射类 -->
<mvc:annotation-driven />
<!-- 访问静态资源 -->
<mvc:default-servlet-handler />
<!-- 配置扫描的包 -->
<context:component-scan base-package="com.epoch.*" />
@RestController
@RequestMapping("/hello")
public class HelloController {
protected final Log logger = LogFactory.getLog(this.getClass());
@RequestMapping("/index")
public String index(){
return "test";
}
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
这里,有几点需要额外注意。
这里,我们只关注HandlerMapping接口的几个实现,主要是BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping、RequestMappingHandlerMapping这三个类。
1、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping的引入
现在,注解很流行,对于注解的解析,可能会涉及到上面两个类的引入。所以,针对上面两个类在配置文件中的配置时,一定要注意配置方法,避免重复引入导致重复实例化的情况。我在验证SimpleUrlHandlerMapping时,因为在配置文件文件中,明确配置了SimpleUrlHandlerMapping类。同时,SpringMVC,对于标签<mvc:default-servlet-handler />的解析,也会引入SimpleUrlHandlerMapping。这样的结果就是,springMVC容器中,出现两个SimpleUrlHandlerMapping类的实例。
registerHandler this = org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@6c219ca4,this.handlerMap = {/**=org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler@54a02e00}
registerHandler this = org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@e448d70,this.handlerMap = {/simpleUrlHandlerMapping.do=com.epoch.controller.SimpleUrlHandlerMappingController@71b9e643}
第一个SimpleUrlHandlerMapping实例,是解析<mvc:default-servlet-handler />标签时引入的。第二个SimpleUrlHandlerMapping实例,是解析配置文件引入的。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/simpleUrlHandlerMapping.do">welcomeController</prop>
</props>
</property>
</bean>
<bean id="welcomeController" class="com.epoch.controller.SimpleUrlHandlerMappingController" />
因为引入了两个SimpleUrlHandlerMapping实例,而每次通过HandlerMapping接口获取handler时,调用的都是第一个实例,所以对应的Handler是DefaultServletHttpRequestHandler。这种情况,导致SimpleUrlHandlerMappingController类的handleRequest方法始终无法执行。解决方案,去掉<mvc:default-servlet-handler />标签即可。
2、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping对应的Handler实现接口的问题
为BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping两种HandlerMapping接口配置Handler时,Handler需要实现相应的接口,即org.springframework.web.servlet.mvc.Controller接口。这是因为,这两种HandlerMapping实现类所配置的Handler,使用的是相同的HandlerAdapter接口实现类,即SimpleControllerHandlerAdapter类。SimpleControllerHandlerAdapter仅支持实现了Controller的Handler。
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
/**
* Adapter to use the plain {@link Controller} workflow interface with
* the generic {@link org.springframework.web.servlet.DispatcherServlet}.
* Supports handlers that implement the {@link LastModified} interface.
*
* <p>This is an SPI class, not used directly by application code.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.web.servlet.DispatcherServlet
* @see Controller
* @see LastModified
* @see HttpRequestHandlerAdapter
*/
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
其实,Handler实现其他接口也行,只要能找到对应的HandlerAdapter接口实现类即可。这里,可能根据自身情况,灵活定制。
3、SimpleUrlHandlerMapping的默认初始化引入DefaultServletHttpRequestHandler类
如果利用配置方式<mvc:default-servlet-handler />,那么配置对应的解析类DefaultServletHandlerBeanDefinitionParser,在解析配置时,系统会向容器注册的DefaultServletHttpRequestHandler类和SimpleUrlHandlerMapping。
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.config;
import java.util.Map;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
/**
* {@link BeanDefinitionParser} that parses a {@code default-servlet-handler} element to
* register a {@link DefaultServletHttpRequestHandler}. Will also register a
* {@link SimpleUrlHandlerMapping} for mapping resource requests, and a
* {@link HttpRequestHandlerAdapter}.
*
* @author Jeremy Grelle
* @author Rossen Stoyanchev
* @since 3.0.4
*/
class DefaultServletHandlerBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
//注册DefaultServletHttpRequestHandler类
String defaultServletName = element.getAttribute("default-servlet-name");
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
defaultServletHandlerDef.setSource(source);
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (StringUtils.hasText(defaultServletName)) {
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
}
String defaultServletHandlerName = parserContext.getReaderContext().generateBeanName(defaultServletHandlerDef);
parserContext.getRegistry().registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
parserContext.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));
Map<String, String> urlMap = new ManagedMap<>();
urlMap.put("/**", defaultServletHandlerName);
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
String handlerMappingBeanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
return null;
}
}
DefaultServletHttpRequestHandler类实现了HttpRequestHandler接口,这也导致DefaultServletHttpRequestHandler在获取HandlerAdapter时,获取到的是HttpRequestHandlerAdapter类。
package org.springframework.web.servlet.resource;
public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
......
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
......
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc;
/**
* Adapter to use the plain {@link HttpRequestHandler}
* interface with the generic {@link org.springframework.web.servlet.DispatcherServlet}.
* Supports handlers that implement the {@link LastModified} interface.
*
* <p>This is an SPI class, not used directly by application code.
*
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.web.servlet.DispatcherServlet
* @see HttpRequestHandler
* @see LastModified
* @see SimpleControllerHandlerAdapter
*/
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
4、RequestMappingHandlerMapping
RequestMappingHandlerMapping配置handler时,对handler没有任何要求。这是因为,RequestMappingHandlerMapping在构建HandlerExecutionChain对象时,会在内部将对象内的handler属性的类型,设置成HandlerMethod类型。
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.handler;
/**
* Abstract base class for {@link HandlerMapping} implementations that define
* a mapping between a request and a {@link HandlerMethod}.
*
* <p>For each registered handler method, a unique mapping is maintained with
* subclasses defining the details of the mapping type {@code <T>}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
* @param <T> The mapping for a {@link HandlerMethod} containing the conditions
* needed to match the handler method to incoming request.
*/
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* Bean name prefix for target beans behind scoped proxies. Used to exclude those
* targets from handler method detection, in favor of the corresponding proxies.
* <p>We're not checking the autowire-candidate status here, which is how the
* proxy target filtering problem is being handled at the autowiring level,
* since autowire-candidate may have been turned to {@code false} for other
* reasons, while still expecting the bean to be eligible for handler methods.
* <p>Originally defined in {@link org.springframework.aop.scope.ScopedProxyUtils}
* but duplicated here to avoid a hard dependency on the spring-aop module.
*/
// Handler method lookup
/**
* Look up a handler method for the given request.
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
//返回HandlerMethod对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
//返回HandlerMethod对象
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
}
通过RequestMappingHandlerMapping获取的HandlerExecutionChain对象的handler属性,是HandlerMethod的类型,这也导致了SpringMVC后续会使用到RequestMappingHandlerAdapter作为handler的Adapter。因为RequestMappingHandlerAdapter支持HandlerMethod类型的Handler。
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
* @param handler the handler instance to check
* @return whether or not this adapter can adapt the given handler
*/
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
/**
* Always return {@code true} since any method argument and return value
* type will be processed in some way. A method argument not recognized
* by any HandlerMethodArgumentResolver is interpreted as a request parameter
* if it is a simple type, or as a model attribute otherwise. A return value
* not recognized by any HandlerMethodReturnValueHandler will be interpreted
* as a model attribute.
*/
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
}
5、HandlerAdapter接口源代码
public interface HandlerAdapter {
/**
* Given a handler instance, return whether or not this {@code HandlerAdapter}
* can support it. Typical HandlerAdapters will base the decision on the handler
* type. HandlerAdapters will usually only support one handler type each.
* <p>A typical implementation:
* <p>{@code
* return (handler instanceof MyHandler);
* }
* @param handler handler object to check
* @return whether or not this object can use the given handler
*/
boolean supports(Object handler);
/**
* Use the given handler to handle this request.
* The workflow that is required may vary widely.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler to use. This object must have previously been passed
* to the {@code supports} method of this interface, which must have
* returned {@code true}.
* @throws Exception in case of errors
* @return ModelAndView object with the name of the view and the required
* model data, or {@code null} if the request has been handled directly
*/
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/**
* Same contract as for HttpServlet's {@code getLastModified} method.
* Can simply return -1 if there's no support in the handler class.
* @param request current HTTP request
* @param handler handler to use
* @return the lastModified value for the given handler
* @see javax.servlet.http.HttpServlet#getLastModified
* @see org.springframework.web.servlet.mvc.LastModified#getLastModified
*/
long getLastModified(HttpServletRequest request, Object handler);
}
有关HandlerAdapter的,不在这里详细介绍。