今天主要总结前几天晚上看了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方法,这个方法是主要的处理用户请求的地方。