今天主要總結前幾天晚上看了Control設計。 java
Spring MVC 的Control主要是由HandlerMapping(interface)和HandlerAdapter(interface)兩個組件提供。 web
對於HandlerMapping:主要負責映射用戶的URL和對應的處理類,HandlerMapping並無規定這個URL與應用的處理類如何映射,HandlerMapping接口中只定義了根據一個URL必須返回一個由HandlerExceptionChain表明的處理鏈,咱們能夠在這個處理鏈中添加任意的HandlerAdapter實例來處理這個URL對應的請求,這個設計思路和Servlet規範中的Filter處理是相似的。 spring
/** * Return a handler and any interceptors for this request. The choice may be made * on request URL, session state, or any factor the implementing class chooses. * <p>The returned HandlerExecutionChain contains a handler Object, rather than * even a tag interface, so that handlers are not constrained in any way. * For example, a HandlerAdapter could be written to allow another framework's * handler objects to be used. * <p>Returns <code>null</code> if no match was found. This is not an error. * The DispatcherServlet will query all registered HandlerMapping beans to find * a match, and only decide there is an error if none can find a handler. * @param request current HTTP request * @return a HandlerExecutionChain instance containing handler object and * any interceptors, or <code>null</code> if no mapping found * @throws Exception if there is an internal error */ HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;------------------------------------邪惡的分割線-------------------------------------------
Spring MVC自己也提供了不少HandlerMapping 的實現,默認使用的是BeanNameUrlHandlerMapping,也能夠根據Bean的name屬性映射到URL中。 數組
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); } }
對於HandlerMapping,能夠幫助咱們管理URL和處理類的映射關係,簡單的說就是能夠幫助咱們將一個或者多個URL映射到一個或者多個spring Bean中。 session
下面總結下spring MVC是如何將請求的URL映射到咱們定義的bean中的。 app
對於HandlerMapping是如何初始化的。spring MVC提供了一個HandlerMapping的抽象類 AbstractHandlerMapping。 ide
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { }能夠經過讓HandlerMapping設置setOrder方法提升優先級和經過覆蓋initApplicationContext方法實現初始化的工做。
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); }
對於SimpleUrlHandlerMapping類是如何初始化的: ui
public final void setApplicationContext(ApplicationContext context) throws BeansException { if (context == null && !isContextRequired()) { // Reset internal context state. this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { // Initialize with passed-in context. if (!requiredContextClass().isInstance(context)) { throw new ApplicationContextException( "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]"); } this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); initApplicationContext(context); } else { // Ignore reinitialization if same context passed in. if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } }
/** * Calls the {@link #registerHandlers} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); }
/** * Initializes the interceptors. * @see #extendInterceptors(java.util.List) * @see #initInterceptors() */ @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); }初始化initInterceptors()方法:將SimpleUrlHandlerMapping中定義的interceptors包裝成handlerInterceptor對象保存在adaptedInterceptors數組中。
在方法registerHandlers中,調用了AbstractUrlHandlerMapping的registerHandler(url, handler)方法; this
在方法registerHandler(url, handler)中,將在SimpleUrlHandlerMapping中定義的mappings註冊到handlerMap集合中。 url
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); } } }在registerHandler方法中;
/** * Register the specified handler for the given URL path. * @param urlPath the URL the bean should be mapped to * @param handler the handler instance or handler bean name String * (a bean name will automatically be resolved into the corresponding handler bean) * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */----------------------------------邪惡的分割線-----------------------------------
小結: HandlerMapping初始化工做完成的兩個最重要的工做就是:
------------------------------------邪惡的分割線-------------------------------------------
HandlerMapping 能夠完成URL與Handler的映射關係,那麼HandlerAdapter就能夠幫助自定義各類handler了。由於SpringMVC首先幫助咱們把特別的URL對應到一個Handler,那麼這個Handler一定要符合某種規則,最多見的方法就是咱們的全部handler都繼承某個接口,而後SpringMVC 天然就調用這個接口中定義的特性方法。
對於spring MVC提供了三種典型的handlerAdapter實現類。
對於handlerAdapter的初始化沒有什麼特別之處,只是簡單的建立一個handlerAdapter對象,將這個對象保存在DispatcherServlet的HandlerAdapters集合中。當Spring MVC將某個URL對應到某個Handler時候,在handlerAdapters集合中查詢那個handlerAdapter對象supports這個Handler,handlerAdapter對象將會被返回,用了調用這個handlerAdapter接口對應的方法。
------------------------------------邪惡的分割線-------------------------------------------
整個Spring MVC的調用是從DispatcherServlet的doService方法開始的,在doService方法中會將ApplicationContext、localeResolver、themeResolver等對象添加到request中便於在後面使用,接着就調用doDispatch方法,這個方法是主要的處理用戶請求的地方。