源码分析(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
相关推荐
在这个源码分析中,我们将深入探讨Spring MVC的核心组件和工作流程。 1. **DispatcherServlet**:Spring MVC的入口点,它是所有请求的前端控制器。当一个HTTP请求到达服务器时,DispatcherServlet负责拦截请求,并...
在"03.SpringMVC源码分析1"的主题下,我们可能会探讨Spring MVC的请求处理流程,尤其是`DispatcherServlet`、`HandlerMapping`、`HandlerAdapter`以及`HandlerInterceptor`等核心组件。 1. **DispatcherServlet**:...
在本篇博客“SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门”中,我们将探讨Spring MVC框架的核心组件——HandlerMapping和HandlerAdapter。这两个组件是Spring MVC处理请求的关键,它们负责映射HTTP...
4. **项目源码分析** - `springmvc_hello` 可能是简单的 Hello World 示例,展示了 Spring MVC 的基本使用。 - `spring_user` 可能是一个用户管理模块,涉及到 Hibernate 的实体类、持久化操作以及 Spring MVC 的 ...
### SpringMVC源码分析概览 #### 一、JavaWeb与Servlet基础知识 在深入了解SpringMVC源码之前,我们首先需要对JavaWeb的基础概念进行简单的回顾。JavaWeb的核心是Servlet,这是一种运行在服务器端的应用程序接口...
《看透SpringMVC源代码分析与实践》这...总的来说,深入学习SpringMVC源码能够提升我们的技术水平,使我们在面对复杂业务场景时更有信心。《看透SpringMVC源代码分析与实践》这本书无疑是实现这一目标的重要参考资料。
本视频课程“SpringMvc深入理解源码分析”旨在帮助开发者深入理解Spring MVC的工作原理和核心机制,从而更好地利用它来构建高效、可维护的Web应用。 在Spring MVC中,主要涉及以下几个核心概念: 1. **...
在源码分析中,我们可以看到如何通过@SpringBootApplication注解启动SpringBoot应用,以及AutoConfiguration类是如何根据项目依赖自动配置Bean的。 要熟练掌握这三个框架,你需要理解以下关键概念: 1. Bean的生命...
源码分析是理解SpringMVC工作原理的重要步骤,下面我们将探讨几个关键的SpringMVC源码知识点: 1. **DispatcherServlet**:这是SpringMVC的核心组件,负责接收HTTP请求,并根据请求信息分发到相应的处理器。它通过`...
通过尚硅谷的SpringMVC源码分析,开发者可以更深入地了解SpringMVC的内部机制,如DispatcherServlet、HandlerAdapter、ModelAndView的实现细节,以及AOP、IOC容器在SpringMVC中的应用。源码学习有助于提升对框架原理...
总之,深入理解SpringMVC的源码分析,不仅可以帮助我们解决实际开发中的问题,还能提升我们的设计能力和代码质量,让Web应用更加高效、稳定。通过实战项目,将理论知识运用到实践中,进一步巩固和提升技能。
源码分析时,你可以关注以下几个方面: 1. **DispatcherServlet的初始化过程**:了解如何加载配置,以及如何创建并注册HandlerMapping和HandlerAdapter。 2. **HandlerMapping的工作流程**:研究如何解析`@...
本教程将帮助你理解如何搭建一个Spring MVC的开发环境,并通过源码分析深入理解其工作原理。 首先,我们需要安装和配置Java Development Kit (JDK)。确保你的系统已经安装了JDK 8或更高版本,因为Spring MVC通常与...
SpringMVC是一个广泛使用的Java Web表现层框架...通过源码分析,我们可以更好地理解其设计哲学,从而更高效地利用SpringMVC进行Web应用的开发。在后续的文章中,我们将继续深入探讨SpringMVC的其他重要组件和实现细节。
本资料“Java进阶之SpringMVC源码深度剖析共12页.pdf”是对SpringMVC核心原理的深入探讨,通过分析源码来帮助开发者更深入地理解其工作原理。 1. **SpringMVC概述** SpringMVC是Spring框架的一个模块,主要负责...
**SpringMVC 3.1 实例源码详解** ...通过分析源码,你可以更好地理解SpringMVC 3.1的工作原理,并将其应用到自己的项目中。这个实例是一个宝贵的参考资料,对于学习和实践SpringMVC 3.1的开发者来说,非常有价值。