源码分析(1):HandlerMapping
当用户在浏览器输入一个URL地址,浏览器发起一个http请求,请求到达服务器后,首先会被SpringMVC注册在web.xml中的前端转发器DispatcherServlet接收,DispatcherServlet是一个标准的Servlet,它的作用是接受和转发web请求到内部框架处理单元.
HandlerMapping
public abstract interface HandlerMapping {
public abstract HandlerExecutionChain getHandler(HttpServletRequest paramHttpServletRequest);
}
当DispatcherServlet接收到web请求后,由标准Servlet类处理方法doGet或者doPost,经过几次转发后,最终注册在DispatcherServlet类中的HandlerMapping实现类组成的一个List会在一个循环中被遍历.以该web请求的HttpServletRequest对象为参数,依次调用其getHandler方法,第一个不为null的调用结果,将被返回.
dispatcher-servlet.xml中可配置多个HandlerMapping实现类,比如HelloWorld配置的BeanNameUrlHandlerMapping,还有其他HandlerMapping实现类SimpleUrlHandlerMapping,DefaultAnnotationHandlerMapping.
DispatcherServlet.doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
mappedHandler = getHandler(processedRequest, false);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
}
DispatcherServlet.getHandler()
protected HandlerExecutionChain getHandler(HttpServletRequest request) {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
handlerMappings变量就是dispatcher-servlet.xml中配置的HandlerMapping的实现类列表.
HandlerMapping HandlerExecutionChain Handler HandlerInterceptors
一个request请求过来,会经由dispatcher-servlet.xml中配置的多个HandlerMapping实现类.
HandlerMapping实现类中还可以配置多个拦截器HandlerInterceptor:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="measurementInterceptor" />
</list>
</property>
</bean>
通过上面的配置,上述四个类的关系:
HandlerMapping通过request请求获得HandlerExecutionChain,而HandlerExecutionChain包含了该URL请求映射的Handler实例,以及一系列的HandlerInterceptors.拦截器会作用在Handler的调用前后,类似AOP的功能.
重点分析:HandlerExecutionChain handler = hm.getHandler(request)
dispatcher-servlet.xml会配置多个HandlerMapping实现类,这些实现类都继承了AbstractHandlerMapping抽象类.
AbstractHandlerMapping是HandlerMapping的抽象类实现
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered {
private Object defaultHandler;
private UrlPathHelper urlPathHelper;
private PathMatcher pathMatcher;
private final List<Object> interceptors;
private final List<HandlerInterceptor> adaptedInterceptors;
private final List<MappedInterceptor> mappedInterceptors;
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
if (handler instanceof String) {
String handlerName = (String)handler;
handler = getApplicationContext().getBean(handlerName);
}
return getHandlerExecutionChain(handler, request);
}
protected abstract Object getHandlerInternal(HttpServletRequest paramHttpServletRequest);
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request){
HandlerExecutionChain chain = new HandlerExecutionChain(handler);
chain.addInterceptors(getAdaptedInterceptors());
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
}
抽象方法getHandlerInternal获得Handler对象,留给子类实现
子类获得的handler如果为null,则取默认的Handler(什么时候设置默认的见下文),如果默认的也为null则返回null.
如果获得的handler是字符串类型,说明是Handler的这个Bean的名称而已,需要从BeanFactory中取出真正对象.
取得Handler后,getHandler()方法返回的不是Handler,而是HandlerExecutionChain,这个对象包装了该Handler,以及给一系列的拦截器.
AbstraUrlHandlerMapping是AbstractHandlerMapping的子类,根据请求URL获取映射的Handler对象
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
private Object rootHandler;
private boolean lazyInitHandlers;
private final Map<String, Object> handlerMap;
protected Object getHandlerInternal(HttpServletRequest request) {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null){
if (rawHandler instanceof String) {
String handlerName = (String)rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
protected Object lookupHandler(String urlPath, HttpServletRequest request){
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
if (handler instanceof String) {
String handlerName = (String)handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
}
}
lookupHandler根据urlPath从handlerMap中获取到Handler,如果是字符串,再通过Spring的Ioc容器获得Handler实例.Handler还不是最终返回的对象,最终返回的是HandlerExecutionChain,给Handler包装上Interceptors:
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, Map<String, String> uriTemplateVariables){
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!(CollectionUtils.isEmpty(uriTemplateVariables))) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
HelloWorld中使用BeanNameUrlHandlerMapping,则handlerMap.get(urlPath)获取到的handler是/hello字符串,在Web容器启动的时候会初始化配置的所有的Bean,即实例化/hello对应的HelloWorld类,所以通过bean的名称就能获取到对应的Bean,即Handler:HelloWorld类.
有get就一定有地方会把对应的Handler和urlPath放入handlerMap中,找到handlerMap.put的地方
将urlPath和对应的Handler注册到handlerMap中以便lookupHandler获取
protected void registerHandler(String urlPath, Object handler) {
Object resolvedHandler = handler;
if ((!(this.lazyInitHandlers)) && (handler instanceof String)) {
String handlerName = (String)handler;
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException("Cannot map " + urlPath + "]: There is already mapped.");
}
} else if (urlPath.equals("/")) {
setRootHandler(resolvedHandler);
} else if (urlPath.equals("/*")) {
setDefaultHandler(resolvedHandler);
} else {
this.handlerMap.put(urlPath, resolvedHandler);
}
}
调用者调用registerHandler方法,传递urlPath和handler.urlPath跟request请求的URL相关,handler跟这个URL的处理器相关.注册是发生在Web容器启动的时候,先根据handler如果是字符串,说明是Handler的名称,则从BeanFactory中取出Handler实例.然后判断handlerMap中是否有urlPath对应的Handler实例.如果从map get出来的value有值,说明已经注册过了,不需要再注册.如果handlerMap中没有urlPath对应的Handler实例,
有两种特殊情况urlPath如果是/,则设置RootHandler,如果是/*,则设置DefaultHandler.
如果不是这两种特殊情况,则把urlPath和对应的实例化好的Handler放入handlerMap中.
registerHandler的调用者是AbstractDetectingUrlHandlerMapping.detectHandlers(),再到initApplicationContext()
或者是SimpleUrlHandlerMapping.registerHandlers,再到initApplicationContext()
即BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping,在AbstractDetectingUrlHandlerMapping初始化initApplicationContext上下文的时候会根据URL来注册相应的Handler到handlerMap中.
Web容器启动时检测Handler并调用registerHandler来注册Handler到handlerMap中
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
private boolean detectHandlersInAncestorContexts;
public AbstractDetectingUrlHandlerMapping(){
this.detectHandlersInAncestorContexts = false;
}
public void initApplicationContext() {
super.initApplicationContext();
detectHandlers();
}
protected void detectHandlers(){
String[] beanNames = (this.detectHandlersInAncestorContexts) ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!(ObjectUtils.isEmpty(urls))) {
registerHandler(urls, beanName);
}
}
}
protected abstract String[] determineUrlsForHandler(String paramString);
}
detectHandlersInAncestorContexts是否从父上下文检测Handler,默认为否.根据注册的类型名称来获取beanNames.
由Spring的Ioc知识,我们知道可以通过bean的名称,也可以加上类型来获得一个bean实例.比如:
applicationContext.getBean("beanName")
applicationContext.getBean("beanName",BeanType.class)
因为beanName一定是唯一的.通过getBeanNamesForType能从BeanFactory获取所有注册的BeanNames数组.
又一个抽象方法determineUrlsForHandler,因为我们能断定AbstractDetectingUrlHandlerMapping的子类肯定会实现这个方法.看BeanNameUrlHandlerMapping类很简单
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping{
protected String[] determineUrlsForHandler(String beanName){
List urls = new ArrayList();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
return StringUtils.toStringArray(urls);
}
}
方法参数beanName是SpringMVC配置文件指定的name的值,比如HelloWorld中的"/hello".
在Web容器启动的时候,配置了BeanNameHandlerMapping,会把所有以/开头的Bean通过registerHandler(urls, beanName)都注册到handlerMap中.
DefaultAnnotationHandlerMapping注册到handlerMap的方法有点复杂,因为注解不仅在Bean类上,在方法上也会有@RequestMapping注解.
再来看看SimpleUrlHandlerMapping的做法:
SimpleUrlHandlerMapping在Web容器启动时取得mappings配置也注册到handlerMap中
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
private final Map<String, Object> urlMap;
public SimpleUrlHandlerMapping() {
this.urlMap = new HashMap();
}
public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}
public void initApplicationContext(){
super.initApplicationContext();
registerHandlers(this.urlMap);
}
protected void registerHandlers(Map<String, Object> urlMap) {
for (Map.Entry entry : urlMap.entrySet()) {
String url = (String)entry.getKey();
Object handler = entry.getValue();
if (!(url.startsWith("/"))) {
url = "/" + url;
}
registerHandler(url, handler);
}
}
}
SpringMVC配置SimpleUrlHandlerMapping,并手动配置映射关系,如果URL为/hello,则映射到helloController上.
<bean id="helloController" class="com.xuyuan.spring3.mvc.helloworld.HelloController"></bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello">helloController</prop>
</props>
</property>
</bean>
SimpleUrlHandlerMapping通过set方法注入mappings的值,这样registerHandlers的参数urlMap就是配置文件中mappings配置的所有key,value值.如果配置的key没有以/开头则自动加上/.比如上面的例子
registerHandler(url, handler)=registerHandler("/hello", "helloController")
注意:registerHandler的调用者AbstractAnnotationHandlerMapping和SimpleUrlHandlerMapping传递的参数value不是Handler的真正类型,而是Handler的名称.而在AbstractUrlHandlerMapping的真正注册到handlerMap中的.
上文AbstractUrlHandlerMapping说到DefaultHandler,如果配置文件或者注解有配置/*对应的Handler.
那么如果浏览器的请求都不会映射到系统中注册的Handler,则采用这个默认的Handler.如果没有配置默认的Handler,我们就认为客户端的请求不会被系统接受,可以抛出404页面.
参考文档:
SpringMVC源码剖析 http://my.oschina.net/lichhao/blog?catalog=285356
Spring MVC 教程,快速入门,深入分析http://elf8848.iteye.com/blog/875830
跟我学SpringMVC http://jinnianshilongnian.iteye.com/blog/1752171