在上一章中,主要讲了ContextLoaderListener对IoC容器在Web环境中的构建。本章讲另一个重点,DispatcherServlet
1.DispatcherServlet设计概述
DispatcherServlet作为一个前端控制器,所有的请求都要经过它来处理,进行转发、匹配、数据处理后,由页面进行展示。
在对完成ContextLoaderListener的初始化以后,Web容器开始初始化DispatcherServlet。DispatcherServlet会建立自己的上下文(Context)来持有SpringMVC的Bean对象,在建立这个自己持有的IoC容器的时候,会从ServletContext中得到根上下文(Root Context)来作为DispatcherServlet持有的上下文的双亲上下文。这个根上下文在上一章中有提到,是ContextLoader所设置的。
有了这个根上下文,在对自己持有的上下文进行初始化,并保存到ServletContext
中,供以后使用。
1.1 DispatcherServlet类继承
先看类继承图:
DispatcherSerlvet通过继承FrameworkServlet和HttpServletBean而进程了HttpServlet,通过使用Serevlet AP来响应HTTP请求。
1.2 DispatcherServlet处理过程
从改图中,我们可以看出,其处理过程分为两个部分,一个是初始化,另一个则是doGet/doPost,处理请求的过程。doGet/doPost方法在进过Framework的processRequest()方法简单处理后,调用DispatcherServlet的doService方法,这个方法中封装了doDispatcher,这个方法是实现MVC模式的主要部分,也是接下来重点关注的部分。
2.DispatcherServlet的启动和初始化
作为一个Servlet的,开始时,Servlet的init方法被调用,加载Servlet中的设置。接下来,会执行DispatcherServlet持有的IoC容器的初始化过程,在这个初始化过程中,属于DispatcherServlet的上下文被建立起来,并设置为根上下文的子上下文。IoC容器getBean的时候,是会向双亲上下文getBaen。即根上下文的Bean可以被共享。上下文建立之后,和其它IoC容器一样通过refresh
完成初始化。最后,DispatcherServlet给这个自己持有的上下文命名,并把它设置到Web容器的上下文中,这个名称和在web.xml中设置的DispatcherServlet的Servlet名称相关,从而保证了这个上下文在Web环境上下文体系的唯一性。
实现代码逻辑如上所述,这里不贴出来了。
Bean管理
这个时候IoC容器已经建立起来了,这个IoC容器是根上下文的子容器。对于一个Bean,系统首先到根容器查找,找不到之后,才回到DispatcherServlet所管理的IoC容器中去查找,这个是有IoC容器的getBean
决定的。
2.1 Dispatcher Server的初始化
DispatcherServlet持有一个以自己Servlet命名的IoC容器。这个IoC容器是一个WebApplicationContext对象,这个IoC容器建立之后,意味着DispatcherServlet拥有自己的Bean定义空间,这位后来的XML文件来配置MVC中各个Bean创造了条件。自此之后,Web容器相关的加载已经完成。
接下来,是Sping MVC Dispatcher Server的初始化。
其调用关系如下:
其中初始化在initStrategies方法中完成。
其中,initHandlerMappings
的作用是,为HTTP请求找到相应的Conteroller。其代码如下:
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//找到所有在ApplicationContext中包含双亲上下文中的HandlerMappings。
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
//排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else { //根据名称从当前的IoC容器中通过getBean获取handlerMapping
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
//要有至少一个HandlerMapping,如果没有找到,则设置一个默认的。
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
3. MVC处理HTTP分发请求
3.1 HandlerMapping的配置和设计原理
HandlerMapping也就是控制URL和相对应的处理方法的地方。在初始化的时候,在上下文环境中已定义的所有HandlerMapping都已经被加载在一个List中,并被排序(为什么排序)。这个List的每一个元素对应着一个具体的HandlerMapping的配置。一般来说,每一个HandlerMapping可以持有一系列从URL请求道Contorller的映射(对应/*等URL匹配规则)。
HandlerMapping的设计图:
其中,HandlerMapping的getHandler方法:
public interface HandlerMapping{
HandlerExecutionChain getHander(HttpServletRequest request);
}
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler; //HTTP请求对应的Handler
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;
//下面是一些增强方法
/**
* Create a new HandlerExecutionChain.
* @param handler the handler object to execute
*/
public HandlerExecutionChain(Object handler) {
this(handler, (HandlerInterceptor[]) null);
}
/**
* Create a new HandlerExecutionChain.
* @param handler the handler object to execute
* @param interceptors the array of interceptors to apply
* (in the given order) before the handler itself executes
*/
public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
if (handler instanceof HandlerExecutionChain) {
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
this.handler = originalChain.getHandler();
this.interceptorList = new ArrayList<HandlerInterceptor>();
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
}
else {
this.handler = handler;
this.interceptors = interceptors;
}
}
/**
* Return the handler object to execute.
* @return the handler object (may be {@code null})
*/
public Object getHandler() {
return this.handler;
}
public void addInterceptor(HandlerInterceptor interceptor) {
initInterceptorList().add(interceptor);
}
public void addInterceptors(HandlerInterceptor... interceptors) {
if (!ObjectUtils.isEmpty(interceptors)) {
CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
}
}
private List<HandlerInterceptor> initInterceptorList() {
if (this.interceptorList == null) {
this.interceptorList = new ArrayList<HandlerInterceptor>();
if (this.interceptors != null) {
// An interceptor array specified through the constructor
CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
}
}
this.interceptors = null;
return this.interceptorList;
}
/**
* Return the array of interceptors to apply (in the given order).
* @return the array of HandlerInterceptors instances (may be {@code null})
*/
public HandlerInterceptor[] getInterceptors() {
if (this.interceptors == null && this.interceptorList != null) {
this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
}
return this.interceptors;
}
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
/**
* Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
*/
void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
if (interceptors[i] instanceof AsyncHandlerInterceptor) {
try {
AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
}
catch (Throwable ex) {
logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
}
}
}
}
}
/**
* Delegates to the handler's {@code toString()}.
*/
@Override
public String toString() {
Object handler = getHandler();
if (handler == null) {
return "HandlerExecutionChain with no handler";
}
StringBuilder sb = new StringBuilder();
sb.append("HandlerExecutionChain with handler [").append(handler).append("]");
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
sb.append(" and ").append(interceptors.length).append(" interceptor");
if (interceptors.length > 1) {
sb.append("s");
}
}
return sb.toString();
}
}
}
在HandlerExecutionChain中设置了一个拦截器链,通过这个拦截器链中的拦截器,为handler对象提供功能的增强。
以SimpleURLHandlerMapping ,要做的就是根据URL映射的方式,注册Handle和Interceptor(拦截器),从而维护这样的一个映射的Map。
那么,SimplerURLHandlerMapping的注册Handler的过程就是我们接下来要关注的。
3.2 SimpleURLHandlerMapping注册Handler
···
public void initApplicationContext(){
super.initApplicationContext();
registerHandlers(this.urlMap);
}
//表层注册函数
protected void registerHandlers(Map
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// 直接用Bean的名称进行映射
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
//"/"设置到rootHandler中
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
//"/*"设置到defaultHandler当中
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {
//放入到一个Map当中
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
上面这个就是handlerMap了,其中保存了URL和COnteroller之间的映射关系。
3.3 使用HandlerMapping完成请求的映射处理
这个主要是HandleMapping中定义的getHandler
方法了,只是通过URL匹配规则来获取持有Handler的HandlerExecutionChain。
3.4 SpingMCV对HTTP的请求的分发处理
这个无疑是我们所关注的重点啦。这里重新回到DispatcherSerlvelt。请求的分发在doService
中的doDispatch
完成。
以下是这个方法的过程,看似很长,其实只是,获取HandleExecutinChain,请求前置处理,处理,后置处理,视图呈现,结束。
以下是处理过程图:
以下是Spring技术内幕的代码注释图,不关注View的部分。
接下来,看下DispatcherServlet是如何获得一个Handler的,一个个找.
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
视图呈现,在这里不关注。