spring MVC工做機制與設計模式-讀後小結(二)

今天主要總結前幾天晚上看了Control設計。 java

Control設計

Spring MVC 的Control主要是由HandlerMapping(interface)和HandlerAdapter(interface)兩個組件提供。 web

  • org.springframework.web.servlet.HandlerMapping
  • org.springframework.web.servlet.HandlerAdapter

對於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;
------------------------------------邪惡的分割線-------------------------------------------

HandlerMapping初始化

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


  • 首先調用ApplicationObjectSupport的setApplicationContext()方法。


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 + "]");
			}
		}
	}




  • 調用SimpleUrlHandlerMapping的initApplicationContext()方法


/**
	 * Calls the {@link #registerHandlers} method in addition to the
	 * superclass's initialization.
	 */
	@Override
	public void initApplicationContext() throws BeansException {
		super.initApplicationContext();
		registerHandlers(this.urlMap);
	}


  • 調用super.initApplicationContext(),調用了AbstractHandlerMapping的initApplicationContext()方法

/**
	 * 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數組中。


  • SimpleUrlHandlerMapping繼續執行registerHandlers(this.urlMap)方法;

在方法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初始化工做完成的兩個最重要的工做就是:

  1. 將URL與Handler的對應關係保存在HandlerMapping集合中,並將全部的interceptors對象保存在adaptedInterceptors數組中,等請求到來的時候執行全部的adaptedIntercoptors數組中的interceptor對象。
  2. 全部的interceptor必須實現HandlerInterceptor接口。


------------------------------------邪惡的分割線-------------------------------------------

HandlerAdapter初始化

HandlerMapping 能夠完成URL與Handler的映射關係,那麼HandlerAdapter就能夠幫助自定義各類handler了。由於SpringMVC首先幫助咱們把特別的URL對應到一個Handler,那麼這個Handler一定要符合某種規則,最多見的方法就是咱們的全部handler都繼承某個接口,而後SpringMVC 天然就調用這個接口中定義的特性方法。

對於spring MVC提供了三種典型的handlerAdapter實現類。

  1. SimpleServletHandlerAdapter:能夠直接繼承Servlet接口。
  2. SimpleControllerHandlerAdapter:能夠繼承Controller接口。
  3. SimpleRequestHandlerAdapter:能夠繼承HttpRequsetHandler接口。

對於handlerAdapter的初始化沒有什麼特別之處,只是簡單的建立一個handlerAdapter對象,將這個對象保存在DispatcherServlet的HandlerAdapters集合中。當Spring MVC將某個URL對應到某個Handler時候,在handlerAdapters集合中查詢那個handlerAdapter對象supports這個Handler,handlerAdapter對象將會被返回,用了調用這個handlerAdapter接口對應的方法。

------------------------------------邪惡的分割線-------------------------------------------

Control的調用邏輯

整個Spring MVC的調用是從DispatcherServlet的doService方法開始的,在doService方法中會將ApplicationContext、localeResolver、themeResolver等對象添加到request中便於在後面使用,接着就調用doDispatch方法,這個方法是主要的處理用戶請求的地方。

相關文章
相關標籤/搜索