今天主要总结前几天晚上看了Control设计。
Control设计
Spring MVC 的Control主要是由HandlerMapping(interface)和HandlerAdapter(interface)两个组件提供。
- org.springframework.web.servlet.HandlerMapping
- org.springframework.web.servlet.HandlerAdapter
对于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类是如何初始化的:
- 首先调用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)方法;
在方法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初始化工作完成的两个最重要的工作就是:
- 将URL与Handler的对应关系保存在HandlerMapping集合中,并将所有的interceptors对象保存在adaptedInterceptors数组中,等请求到来的时候执行所有的adaptedIntercoptors数组中的interceptor对象。
- 所有的interceptor必须实现HandlerInterceptor接口。
------------------------------------邪恶的分割线-------------------------------------------
HandlerAdapter初始化
HandlerMapping 可以完成URL与Handler的映射关系,那么HandlerAdapter就可以帮助自定义各种handler了。因为SpringMVC首先帮助我们把特别的URL对应到一个Handler,那么这个Handler必定要符合某种规则,最常见的方法就是我们的所有handler都继承某个接口,然后SpringMVC 自然就调用这个接口中定义的特性方法。
对于spring MVC提供了三种典型的handlerAdapter实现类。
- SimpleServletHandlerAdapter:可以直接继承Servlet接口。
- SimpleControllerHandlerAdapter:可以继承Controller接口。
- 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方法,这个方法是主要的处理用户请求的地方。