源碼分析(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