spring MVC工作机制与设计模式-读后小结(二)
2013-05-15 14:56 阅读(189)


今天主要总结前几天晚上看了Control设计。

Control设计

Spring MVC 的Control主要是由HandlerMapping(interface)和HandlerAdapter(interface)两个组件提供。

对于HandlerMapping:主要负责映射用户的URL和对应的处理类,HandlerMapping并没有规定这个URL与应用的处理类如何映射,HandlerMapping接口中只定义了根据一个URL必须返回一个由HandlerExceptionChain代表的处理链,我们可以在这个处理链中添加任意的HandlerAdapter实例来处理这个URL对应的请求,这个设计思路和Servlet规范中的Filter处理是类似的。

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

下面总结下spring MVC是如何将请求的URL映射到我们定义的bean中的。

    对于HandlerMapping是如何初始化的。spring MVC提供了一个HandlerMapping的抽象类 AbstractHandlerMapping。

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类是如何初始化的:


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数组中。


在方法registerHandlers中,调用了AbstractUrlHandlerMapping的registerHandler(url, handler)方法;

在方法registerHandler(url, handler)中,将在SimpleUrlHandlerMapping中定义的mappings注册到handlerMap集合中。

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方法,这个方法是主要的处理用户请求的地方。




来自:http://my.oschina.net/bosscheng/blog/130748