SpringMVC原始碼總結(一)HandlerMapping和HandlerAdapter入門
剛接觸SpringMVC,對它的xml檔案配置一直比較模模糊糊,最近花了一點時間稍微看了下原始碼,再加上除錯,開始逐漸理解它,網上的類似的內容有很多,寫本文主要是自己加深一下理解。本文適合用過SpringMVC的開發者,言歸正傳,首先搭建一個最簡單的工程體驗一下。
該工程是基於maven的,pom配置不再說明,所使用的spring版本4.0.5。
首先是web.xml檔案配置,最簡單的配置
-
<!DOCTYPE web-app PUBLIC
-
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
-
"http://java.sun.com/dtd/web-app_2_3.dtd" >
-
-
<web-app>
-
<display-name>Archetype Created Web Application</display-name>
-
<servlet>
-
<servlet-name>mvc</servlet-name>
-
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-
<load-on-startup>1</load-on-startup>
-
</servlet>
-
-
<servlet-mapping>
-
<servlet-name>mvc</servlet-name>
-
<url-pattern>/*</url-pattern>
-
</servlet-mapping>
-
</web-app>
然後是mvc-servlet.xml檔案的配置,上面配置DispatcherServlet會預設載入[servlet-name]-servlet.xml檔案。對於我的配置,會去載入mvc-servlet.xml檔案。
mvc-servlet.xml檔案的內容:
-
<?xml version="1.0" encoding="UTF-8" ?>
-
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context"
-
xsi:schemaLocation="http://www.springframework.org/schema/beans
-
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
-
http://www.springframework.org/schema/mvc
-
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
-
http://www.springframework.org/schema/util
-
http://www.springframework.org/schema/util/spring-util-2.0.xsd
-
http://www.springframework.org/schema/context
-
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
-
-
<bean name="/index" class="com.lg.mvc.HomeAction"></bean>
-
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
-
<property name="templateLoaderPath" value="/WEB-INF/views" />
-
<property name="defaultEncoding" value="utf-8" />
-
<property name="freemarkerSettings">
-
<props>
-
<prop key="locale">zh_CN</prop>
-
</props>
-
</property>
-
</bean>
-
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
-
<property name="suffix" value=".html" />
-
<property name="contentType" value="text/html;charset=utf-8" />
-
<property name="requestContextAttribute" value="request" />
-
<property name="exposeRequestAttributes" value="true" />
-
<property name="exposeSessionAttributes" value="true" />
-
</bean>
-
</beans>
在該配置中定義了一個HomeAction的Bean。內容為:
-
package com.lg.mvc;
-
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
-
import org.springframework.web.servlet.ModelAndView;
-
import org.springframework.web.servlet.mvc.Controller;
-
-
public class HomeAction implements Controller{
-
-
@Override
-
public ModelAndView handleRequest(HttpServletRequest request,
-
HttpServletResponse response) throws Exception {
-
return new ModelAndView("hello");
-
}
-
}
這是最原始的mvc做法,要繼承Controller介面,先從原始的說起,最後再過渡到@Controller和@RequestMapping註解式的配置。它在mvc-serlet.xml檔案中的配置有一個關鍵的屬性name="/index"。
WEB-INF/view目錄下有一個簡單的hello.html,內容為:
-
<html>
-
<head>
-
-
</head>
-
<body>
-
hello lg !
-
</body>
-
</html>
至此該工程就寫完了,部署到tomcat中,專案路徑為/,執行一下。
訪問 http://localhost:8080/index
至此整個工程就算搭建成功了。
下面就要說說原理了。
用過python Django框架的都知道Django對於訪問方式的配置就是,一個url路徑和一個函式配對,你訪問這個url,就會直接呼叫這個函式,簡單明瞭。對於java的物件導向來說,就要分兩步走。第一步首先要找到是哪個物件,即handler,本工程的handler則是HomeAction物件。第二步要找到訪問的函式,即HomeAction的handleRequest方法。所以就出現了兩個原始碼介面 HandlerMapping和HandlerAdapter,前者負責第一步,後者負責第二步。借用網上的SpringMVC架構圖。
HandlerMapping介面的實現(只舉了我認識的幾個) :
BeanNameUrlHandlerMapping :通過對比url和bean的name找到對應的物件
SimpleUrlHandlerMapping :也是直接配置url和對應bean,比BeanNameUrlHandlerMapping功能更多
DefaultAnnotationHandlerMapping : 主要是針對註解配置@RequestMapping的,已過時
RequestMappingHandlerMapping :取代了上面一個
HandlerAdapter 介面實現:
HttpRequestHandlerAdapter : 要求handler實現HttpRequestHandler介面,該介面的方法為 void handleRequest(HttpServletRequest request, HttpServletResponse response)也就是 handler必須有一個handleRequest方法
SimpleControllerHandlerAdapter:要求handler實現Controller介面,該介面的方法為ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也就是本工程採用的
AnnotationMethodHandlerAdapter :和上面的DefaultAnnotationHandlerMapping配對使用的,也已過時
RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配對使用,針對@RequestMapping
先簡單的說下這個工程的流程,訪問http://localhost:8080/index首先由DispatcherServlet進行轉發,通過BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),找到了HomeAction,然後再拿HomeAction和每個adapter進行適配,由於HomeAction實現了Controller介面,所以最終會有SimpleControllerHandlerAdapter來完成對HomeAction的handleRequest方法的排程。然後就順利的執行了我們想要的方法,後面的內容不在本節中說明。
瞭解了大概流程,然後就需要看原始碼了。
首先就是SpringMVC的入口類,DispatcherServlet,它實現了Servlet介面,不再詳細說DispatcherServlet的細節,不然又是一大堆的內容。每次請求都會呼叫它的doService->doDispatch,我們關注的重點就在doDispatch方法中。
-
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
-
HttpServletRequest processedRequest = request;
-
HandlerExecutionChain mappedHandler = null;
-
boolean multipartRequestParsed = false;
-
-
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
-
-
try {
-
ModelAndView mv = null;
-
Exception dispatchException = null;
-
-
try {
-
processedRequest = checkMultipart(request);
-
multipartRequestParsed = (processedRequest != request);
-
//這個是重點,第一步由HandlerMapping找到對應的handler
-
// Determine handler for the current request.
-
mappedHandler = getHandler(processedRequest);
-
if (mappedHandler == null || mappedHandler.getHandler() == null) {
-
noHandlerFound(processedRequest, response);
-
return;
-
}
-
-
// Determine handler adapter for the current request.
-
//這是第二步,找到合適的HandlerAdapter,然後由它來排程執行handler的方法
-
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
-
-
// Process last-modified header, if supported by the handler.
-
String method = request.getMethod();
-
boolean isGet = "GET".equals(method);
-
if (isGet || "HEAD".equals(method)) {
-
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
-
if (logger.isDebugEnabled()) {
-
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
-
}
-
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
-
return;
-
}
-
}
-
-
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
-
return;
-
}
-
-
try {
-
// Actually invoke the handler.
-
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
-
}
-
finally {
-
if (asyncManager.isConcurrentHandlingStarted()) {
-
return;
-
}
-
}
-
-
applyDefaultViewName(request, mv);
-
mappedHandler.applyPostHandle(processedRequest, response, mv);
-
}
-
catch (Exception ex) {
-
dispatchException = ex;
-
}
-
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
-
}
-
catch (Exception ex) {
-
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
-
}
-
catch (Error err) {
-
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
-
}
-
finally {
-
if (asyncManager.isConcurrentHandlingStarted()) {
-
// Instead of postHandle and afterCompletion
-
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
-
return;
-
}
-
// Clean up any resources used by a multipart request.
-
if (multipartRequestParsed) {
-
cleanupMultipart(processedRequest);
-
}
-
}
-
}
第一步詳細檢視:
-
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
-
for (HandlerMapping hm : this.handlerMappings) {
-
if (logger.isTraceEnabled()) {
-
logger.trace(
-
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
-
}
-
HandlerExecutionChain handler = hm.getHandler(request);
-
if (handler != null) {
-
return handler;
-
}
-
}
-
return null;
-
}
可以看到就是通過遍歷所有已註冊的HandlerMapping來找到對應的handler,然後構建出一個HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些攔截器,如下
-
public class HandlerExecutionChain {
-
-
private final Object handler;
-
-
private HandlerInterceptor[] interceptors;
-
-
private List<HandlerInterceptor> interceptorList;
-
-
//其他程式碼省略
-
}
其中HandlerMapping的getHandler實現:
-
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
-
Object handler = getHandlerInternal(request);
-
if (handler == null) {
-
handler = getDefaultHandler();
-
}
-
if (handler == null) {
-
return null;
-
}
-
// Bean name or resolved handler?
-
if (handler instanceof String) {
-
String handlerName = (String) handler;
-
handler = getApplicationContext().getBean(handlerName);
-
}
-
return getHandlerExecutionChain(handler, request);
-
}
這裡的getHandlerInternal(request)是個抽象方法,由具體的HandlerMapping來實現,獲取到的handler如果為空,則獲取預設配置的handler,如果handler為String型別,則表示這個則會去Spring容器裡面去找這樣名字的bean。
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具體實現(通過一系列的介面設計,之後再好好看看這個設計,到BeanNameUrlHandlerMapping這隻用實現該方法中的一部分),如下
-
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
-
-
/**
-
* Checks name and aliases of the given bean for URLs, starting with "/".
-
*/
-
@Override
-
protected String[] determineUrlsForHandler(String beanName) {
-
List<String> urls = new ArrayList<String>();
-
if (beanName.startsWith("/")) {
-
urls.add(beanName);
-
}
-
String[] aliases = getApplicationContext().getAliases(beanName);
-
for (String alias : aliases) {
-
if (alias.startsWith("/")) {
-
urls.add(alias);
-
}
-
}
-
return StringUtils.toStringArray(urls);
-
}
-
-
}
這裡面註釋說,bean的name必須以/開頭,它才處理,將資訊儲存在Map<String, Object> handlerMap中,對於本工程來說就是{'/index':HomeAction物件}。
至此這裡完成了第一步,下面開始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具體實現:
-
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
-
for (HandlerAdapter ha : this.handlerAdapters) {
-
if (logger.isTraceEnabled()) {
-
logger.trace("Testing handler adapter [" + ha + "]");
-
}
-
if (ha.supports(handler)) {
-
return ha;
-
}
-
}
-
throw new ServletException("No adapter for handler [" + handler +
-
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
-
}
遍歷所有的HandlerAdapter,判斷他們是否支援這個handler。
我們來看下HttpRequestHandlerAdapter的supports(handler)方法:
-
public class HttpRequestHandlerAdapter implements HandlerAdapter {
-
-
@Override
-
public boolean supports(Object handler) {
-
//就是判斷handler是否實現了HttpRequestHandler介面
-
return (handler instanceof HttpRequestHandler);
-
}
-
-
@Override
-
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
-
throws Exception {
-
//若handler實現了HttpRequestHandler介面,則呼叫該介面的方法,執行我們在該方法中寫的業務邏輯
-
((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;
-
}
-
-
}
同理SimpleControllerHandlerAdapter也是這樣類似的邏輯
-
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
-
-
@Override
-
public boolean supports(Object handler) {
-
return (handler instanceof Controller);
-
}
-
-
@Override
-
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;
-
}
-
-
}
剩餘兩個AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比較複雜,我也沒看。
按照本工程的配置,則SimpleControllerHandlerAdapter是支援HomeAction的,然後就會執行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本質上就會呼叫HomeAction實現Controller介面的方法。至此就分析完了。
瞭解過程了之後,然後就是最重要的也是經常配置出問題的地方。DispatcherServlet的handlerMappings和handlerAdapters的來源問題。
DispatcherServlet初始化的時候,會呼叫一個方法如下:
-
protected void initStrategies(ApplicationContext context) {
-
initMultipartResolver(context);
-
initLocaleResolver(context);
-
initThemeResolver(context);
-
//初始化一些HandlerMapping
-
initHandlerMappings(context);
-
//初始化一些HandlerAdapter
-
initHandlerAdapters(context);
-
initHandlerExceptionResolvers(context);
-
initRequestToViewNameTranslator(context);
-
initViewResolvers(context);
-
initFlashMapManager(context);
-
}
這裡可以看到,它會初始化一些HandlerMapping和HandlerAdapter,這兩個方法非常重要,理解了這兩個方法你就會知道,配置不對問題出在哪裡,下面具體看下這兩個方法:
-
private void initHandlerMappings(ApplicationContext context) {
-
this.handlerMappings = null;
-
-
if (this.detectAllHandlerMappings) {
-
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
-
Map<String, HandlerMapping> matchingBeans =
-
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
-
if (!matchingBeans.isEmpty()) {
-
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
-
// We keep HandlerMappings in sorted order.
-
OrderComparator.sort(this.handlerMappings);
-
}
-
}
-
else {
-
try {
-
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
-
this.handlerMappings = Collections.singletonList(hm);
-
}
-
catch (NoSuchBeanDefinitionException ex) {
-
// Ignore, we'll add a default HandlerMapping later.
-
}
-
}
-
-
// Ensure we have at least one HandlerMapping, by registering
-
// a default HandlerMapping if no other mappings are found.
-
if (this.handlerMappings == null) {
-
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
-
if (logger.isDebugEnabled()) {
-
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
-
}
-
}
-
}
detectAllHandlerMappings是DispatcherServlet的一個屬性,你是可以在web.xml中配置的,預設是true,如果為true,則會去從本工程mvc-servlet.xml檔案中去探測所有實現了HandlerMapping的bean,如果有,則加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings為false,則直接去容器中找id="handlerMapping"且實現了HandlerMapping的bean.如果以上都沒找到,則會去載入預設的HandlerMapping。
-
/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
-
private boolean detectAllHandlerMappings = true;
本工程由於沒有配置HandlerMapping,所以它會去載入預設的,下面看看預設的配置是什麼
-
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
-
String key = strategyInterface.getName();
-
//defaultStrategies儲存了預設的配置
-
String value = defaultStrategies.getProperty(key);
-
if (value != null) {
-
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
-
List<T> strategies = new ArrayList<T>(classNames.length);
-
for (String className : classNames) {
-
try {
-
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
-
Object strategy = createDefaultStrategy(context, clazz);
-
strategies.add((T) strategy);
-
}
-
catch (ClassNotFoundException ex) {
-
throw new BeanInitializationException(
-
"Could not find DispatcherServlet's default strategy class [" + className +
-
"] for interface [" + key + "]", ex);
-
}
-
catch (LinkageError err) {
-
throw new BeanInitializationException(
-
"Error loading DispatcherServlet's default strategy class [" + className +
-
"] for interface [" + key + "]: problem with class file or dependent class", err);
-
}
-
}
-
return strategies;
-
}
-
else {
-
return new LinkedList<T>();
-
}
-
}
繼續看看defaultStrategies是如何初始化的:
-
private static final Properties defaultStrategies;
-
-
static {
-
// Load default strategy implementations from properties file.
-
// This is currently strictly internal and not meant to be customized
-
// by application developers.
-
try {
-
//這裡的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties
-
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
-
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
-
}
-
catch (IOException ex) {
-
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
-
}
-
}
這裡使用靜態程式碼塊來載入配置檔案DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目錄下面的,如下圖所示:
該預設的配置檔案的內容如下:
-
# 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.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
-
-
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
-
-
#這裡就是預設的HandlerMapping的配置
-
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
-
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
-
#這裡就是預設的HandlerAdapter的配置
-
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
-
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
-
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
-
-
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
-
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
-
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
-
-
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
-
-
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
-
-
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
也就是說,當你什麼都沒有配置時,預設會載入以上的配置。正是由於有了上述預設配置的BeanNameUrlHandlerMapping(它要求name必須是以/開頭的),它才會儲存我們在mvc-servlet.xml中配置的<bean name="/index" class="com.lg.mvc.HomeAction"></bean>,同樣正是由於有了SimpleControllerHandlerAdapter(由於handler實現了Controller介面,所以它的support方法支援我們的handler),才會排程執行HomeAction的handleRequest方法。
- <!DOCTYPE web-app PUBLIC
- "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
- "http://java.sun.com/dtd/web-app_2_3.dtd" >
- <web-app>
- <display-name>Archetype Created Web Application</display-name>
- <servlet>
- <servlet-name>mvc</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>mvc</servlet-name>
- <url-pattern>/*</url-pattern>
- </servlet-mapping>
- </web-app>
- <?xml version="1.0" encoding="UTF-8" ?>
- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
- http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util-2.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.2.xsd">
- <bean name="/index" class="com.lg.mvc.HomeAction"></bean>
- <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
- <property name="templateLoaderPath" value="/WEB-INF/views" />
- <property name="defaultEncoding" value="utf-8" />
- <property name="freemarkerSettings">
- <props>
- <prop key="locale">zh_CN</prop>
- </props>
- </property>
- </bean>
- <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
- <property name="suffix" value=".html" />
- <property name="contentType" value="text/html;charset=utf-8" />
- <property name="requestContextAttribute" value="request" />
- <property name="exposeRequestAttributes" value="true" />
- <property name="exposeSessionAttributes" value="true" />
- </bean>
- </beans>
- package com.lg.mvc;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.web.servlet.ModelAndView;
- import org.springframework.web.servlet.mvc.Controller;
- public class HomeAction implements Controller{
- @Override
- public ModelAndView handleRequest(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- return new ModelAndView("hello");
- }
- }
- <html>
- <head>
- </head>
- <body>
- hello lg !
- </body>
- </html>
BeanNameUrlHandlerMapping :通過對比url和bean的name找到對應的物件
SimpleUrlHandlerMapping :也是直接配置url和對應bean,比BeanNameUrlHandlerMapping功能更多
DefaultAnnotationHandlerMapping : 主要是針對註解配置@RequestMapping的,已過時
RequestMappingHandlerMapping :取代了上面一個
HttpRequestHandlerAdapter : 要求handler實現HttpRequestHandler介面,該介面的方法為 void handleRequest(HttpServletRequest request, HttpServletResponse response)也就是 handler必須有一個handleRequest方法
SimpleControllerHandlerAdapter:要求handler實現Controller介面,該介面的方法為ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也就是本工程採用的
AnnotationMethodHandlerAdapter :和上面的DefaultAnnotationHandlerMapping配對使用的,也已過時
RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配對使用,針對@RequestMapping
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- boolean multipartRequestParsed = false;
- WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- try {
- ModelAndView mv = null;
- Exception dispatchException = null;
- try {
- processedRequest = checkMultipart(request);
- multipartRequestParsed = (processedRequest != request);
- //這個是重點,第一步由HandlerMapping找到對應的handler
- // Determine handler for the current request.
- mappedHandler = getHandler(processedRequest);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request.
- //這是第二步,找到合適的HandlerAdapter,然後由它來排程執行handler的方法
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- // Process last-modified header, if supported by the handler.
- String method = request.getMethod();
- boolean isGet = "GET".equals(method);
- if (isGet || "HEAD".equals(method)) {
- long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
- if (logger.isDebugEnabled()) {
- logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- if (!mappedHandler.applyPreHandle(processedRequest, response)) {
- return;
- }
- try {
- // Actually invoke the handler.
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- }
- finally {
- if (asyncManager.isConcurrentHandlingStarted()) {
- return;
- }
- }
- applyDefaultViewName(request, mv);
- mappedHandler.applyPostHandle(processedRequest, response, mv);
- }
- catch (Exception ex) {
- dispatchException = ex;
- }
- processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
- }
- catch (Exception ex) {
- triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
- }
- catch (Error err) {
- triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
- }
- finally {
- if (asyncManager.isConcurrentHandlingStarted()) {
- // Instead of postHandle and afterCompletion
- mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
- return;
- }
- // Clean up any resources used by a multipart request.
- if (multipartRequestParsed) {
- cleanupMultipart(processedRequest);
- }
- }
- }
- protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- for (HandlerMapping hm : this.handlerMappings) {
- if (logger.isTraceEnabled()) {
- logger.trace(
- "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
- }
- HandlerExecutionChain handler = hm.getHandler(request);
- if (handler != null) {
- return handler;
- }
- }
- return null;
- }
- public class HandlerExecutionChain {
- private final Object handler;
- private HandlerInterceptor[] interceptors;
- private List<HandlerInterceptor> interceptorList;
- //其他程式碼省略
- }
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- Object handler = getHandlerInternal(request);
- if (handler == null) {
- handler = getDefaultHandler();
- }
- if (handler == null) {
- return null;
- }
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- return getHandlerExecutionChain(handler, request);
- }
- public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
- /**
- * Checks name and aliases of the given bean for URLs, starting with "/".
- */
- @Override
- protected String[] determineUrlsForHandler(String beanName) {
- List<String> urls = new ArrayList<String>();
- if (beanName.startsWith("/")) {
- urls.add(beanName);
- }
- String[] aliases = getApplicationContext().getAliases(beanName);
- for (String alias : aliases) {
- if (alias.startsWith("/")) {
- urls.add(alias);
- }
- }
- return StringUtils.toStringArray(urls);
- }
- }
- protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
- for (HandlerAdapter ha : this.handlerAdapters) {
- if (logger.isTraceEnabled()) {
- logger.trace("Testing handler adapter [" + ha + "]");
- }
- if (ha.supports(handler)) {
- return ha;
- }
- }
- throw new ServletException("No adapter for handler [" + handler +
- "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
- }
- public class HttpRequestHandlerAdapter implements HandlerAdapter {
- @Override
- public boolean supports(Object handler) {
- //就是判斷handler是否實現了HttpRequestHandler介面
- return (handler instanceof HttpRequestHandler);
- }
- @Override
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- //若handler實現了HttpRequestHandler介面,則呼叫該介面的方法,執行我們在該方法中寫的業務邏輯
- ((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;
- }
- }
- public class SimpleControllerHandlerAdapter implements HandlerAdapter {
- @Override
- public boolean supports(Object handler) {
- return (handler instanceof Controller);
- }
- @Override
- 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;
- }
- }
- protected void initStrategies(ApplicationContext context) {
- initMultipartResolver(context);
- initLocaleResolver(context);
- initThemeResolver(context);
- //初始化一些HandlerMapping
- initHandlerMappings(context);
- //初始化一些HandlerAdapter
- initHandlerAdapters(context);
- initHandlerExceptionResolvers(context);
- initRequestToViewNameTranslator(context);
- initViewResolvers(context);
- initFlashMapManager(context);
- }
- private void initHandlerMappings(ApplicationContext context) {
- this.handlerMappings = null;
- if (this.detectAllHandlerMappings) {
- // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
- Map<String, HandlerMapping> matchingBeans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
- if (!matchingBeans.isEmpty()) {
- this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
- // We keep HandlerMappings in sorted order.
- OrderComparator.sort(this.handlerMappings);
- }
- }
- else {
- try {
- HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
- this.handlerMappings = Collections.singletonList(hm);
- }
- catch (NoSuchBeanDefinitionException ex) {
- // Ignore, we'll add a default HandlerMapping later.
- }
- }
- // Ensure we have at least one HandlerMapping, by registering
- // a default HandlerMapping if no other mappings are found.
- if (this.handlerMappings == null) {
- this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
- if (logger.isDebugEnabled()) {
- logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
- }
- }
- }
- /** Detect all HandlerMappings or just expect "handlerMapping" bean? */
- private boolean detectAllHandlerMappings = true;
- protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
- String key = strategyInterface.getName();
- //defaultStrategies儲存了預設的配置
- String value = defaultStrategies.getProperty(key);
- if (value != null) {
- String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
- List<T> strategies = new ArrayList<T>(classNames.length);
- for (String className : classNames) {
- try {
- Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
- Object strategy = createDefaultStrategy(context, clazz);
- strategies.add((T) strategy);
- }
- catch (ClassNotFoundException ex) {
- throw new BeanInitializationException(
- "Could not find DispatcherServlet's default strategy class [" + className +
- "] for interface [" + key + "]", ex);
- }
- catch (LinkageError err) {
- throw new BeanInitializationException(
- "Error loading DispatcherServlet's default strategy class [" + className +
- "] for interface [" + key + "]: problem with class file or dependent class", err);
- }
- }
- return strategies;
- }
- else {
- return new LinkedList<T>();
- }
- }
- private static final Properties defaultStrategies;
- static {
- // Load default strategy implementations from properties file.
- // This is currently strictly internal and not meant to be customized
- // by application developers.
- try {
- //這裡的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties
- ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
- defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
- }
- catch (IOException ex) {
- throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
- }
- }
- # 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.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
- org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
- #這裡就是預設的HandlerMapping的配置
- org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
- org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
- #這裡就是預設的HandlerAdapter的配置
- org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
- org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
- org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
- org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
- org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
- org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
- org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
- org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
- org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
相關文章
- SpringMVC原始碼系列:HandlerMappingSpringMVC原始碼APP
- HandlerMapping、Handler和HandlerAdapter的介紹APPAPT
- 精盡Spring MVC原始碼分析 - HandlerAdapter 元件(一)之 HandlerAdapterSpringMVC原始碼APT元件
- springMvc原始碼學習之:spring原始碼總結SpringMVC原始碼
- 程式碼審計入門總結
- SpringMVC 入門SpringMVC
- SpringMVC入門SpringMVC
- maven 學習總結(一)——Maven入門Maven
- React入門總結React
- vue 入門總結Vue
- Nuxt入門總結UX
- Spring 原始碼解析一:SpringMVC 的載入機制原始碼SpringMVC
- SpringMVC總結SpringMVC
- SpringMVC原始碼剖析(一)- 從抽象和介面說起SpringMVC原始碼抽象
- 【UML入門教程】——總結和自我補充
- springmvc簡單學習(一)-----入門SpringMVC
- RabbitMQ由淺入深入門全總結(一)MQ
- 輕鬆入門機器學習之概念總結(一)機器學習
- SpringMVC基礎原始碼分析(一)SpringMVC原始碼
- 測試入門總結
- Jenkins入門總結Jenkins
- ODA入門指南總結
- koa原始碼總結原始碼
- SpringMVC入門學習---結果跳轉方式SpringMVC
- 總結-SpringMVC實現驗證碼介面SpringMVC
- springmvc入門登入功能SpringMVC
- Android OkHttp原始碼解析入門教程(一)AndroidHTTP原始碼
- VC常見入門問題總結(一) (轉)
- Jmeter 原始碼改造入門JMeter原始碼
- SpringMVC三個HandlerMapping實現類解讀SpringMVCAPP
- Giraph 原始碼分析(五)—— 載入資料+同步總結原始碼
- HTTPS入門級總結HTTP
- docker入門知識總結Docker
- JavaScript入門⑧-事件總結大全JavaScript事件
- ElasticSearch極簡入門總結Elasticsearch
- SpringMVC原始碼之引數解析繫結原理SpringMVC原始碼
- vuex 原始碼:原始碼系列解讀總結Vue原始碼
- 精盡Spring MVC原始碼分析 - HandlerMapping 元件(一)之 AbstractHandlerMappingSpringMVC原始碼APP元件