按住ctrl再单击DispatcherServlet,等到MyEclipse下载完源代码,这是一个一千多行代码的大类,我们没有看到Init(),没有看到doGet(),和doPost()。且耐下心来,细细琢磨。
首先,这个Sevlet并不直接继承自HttpServlet,而是继承自FrameworkServlet类。不必惊讶这个类最终还是集成自HttpServlet,一些需要重写的方法也在一层层的继承中,封装了一遍又一遍。
此时需要了解一个前提,springMVC附着于spring之上,我们无法撇开spring单纯地写出一个和springMVC一模一样的框架来,我们能做的只是了解springMVC的处理过程,用自己的方式进行模拟。
DispatcherServlet做为调度总指挥,一些数据的初始化时必不可少的,这一节,我们开始了解DispatcherServlet数据初始化过程。我们把眼光放在这段代码上
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
各种init,我想这就是数据的初始化部分了吧,来看看注释:初始化servlet的必备对象。再看看onRefresh()父类中的实现:
/**
* Template method which can be overridden to add servlet-specific refresh work.
* Called after successful context refresh.
* <p>This implementation is empty.
* @param context the current WebApplicationContext
* @see #refresh()
*/
protected void onRefresh(ApplicationContext context) {
// For subclasses: do nothing by default.
}
注释中说,这是一个可被重写的模版方法,该方法会在上下文refresh成功后被调用,这里有个参数WebApplicationContext,熟悉Spring的同学应该知道,这是spring提供的上下文环境,我们不准备实现spring所以忽略之。回到之前的initStrategies方法,一共有9个初始化过程,我们把目光放在最核心的两个初始化过程上,initHandlerMappings(context);initHandlerAdapters(context); 前者的核心代码为:
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
也就是从上下文环境中取出HandlerMapping对象。先不要纠结我们没有什么上下文环境,先看看这个HandlerMapping是什么鬼。这是一个接口,里面有个核心方法
/**
* 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} 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} if no mapping found
* @throws Exception if there is an internal error
*/
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
这注释够长的,概括一下就是:该方法通过request对象返回有一个匹配的handler和一些拦截器,封装在HandlerExecutionChain中,简单地说就是返回request对应的控制器和拦截器的集合体。看看HandlerExecutionChain里是啥:
最主要的内容是两个成员变量:
private final Object handler;
private HandlerInterceptor[] interceptors;
意思在明白不过了。
重点看三个方法:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response)
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
你就说你熟不熟!第一个在requestMapping注解的方法调用前执行,第二个在方法调用后,页面加载前执行,第三个在页面加载后执行。至于具体怎么做的看一段代码:
if (getInterceptors() != null) {
for (int i = 0; i < getInterceptors().length; i++) {
HandlerInterceptor interceptor = getInterceptors()[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
知道为什么要实现HandlerInterceptor接口了吧。这里有个重点:在写这系列文章前,我也看过一些作者对springMVC理解的文章,部分作者认为postHandler,和afterCopletion只有在preHandler返回true时才执行,这里我只能说:大错特错。具体看代码吧。
不要问我所谓的拦截器栈,栈在哪里,人家压根就没用stack,就用了个数组模拟了一下,不信?看吧:这是 applyPostHanler的代码:
for (int i = getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = getInterceptors()[i];
interceptor.postHandle(request, response, this.handler, mv);
}
至于getHandler(request)内部到底怎么实现,我只能说,底层实现进过了层层封装,要贴代码也贴不过来了,只要知道getHandler()方法首先通过lookupHandler()方法(url匹配)从spring上下文中获得了一个handler对象,再通过getHandlerExecutionChain(handler, request)装填了匹配的interceptor,封装成一个HandlerExecutionChain返回。知道了大概意思,我们就能自己实现了。
另一个方法:initHandlerAdapters():首先我们想来看看这个HandlerAdapter是什么,查看其源代码,可以发现,这是一个接口,被许多个类实现了,留意其中的AbstractHandlerMethodAdapter类(注意AnnotationMethodHandlerAdapter在3.2版本后已经过期!),这是一个抽象类,拥有子类RequestMappingHandlerAdapter,这才是我们关注的重点。其中的关键方法为:
private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod)
至此,我想我们可以得出这样的结论:DispatcherServlet在初始化数据时无非再做两件事:
1.加载了一些HandlerMapping。
2.加载了一些HandlerAdapte。
前者包含了一个可以获得执行模块链(包括了拦截器和handler本身)的方法,后者包含了一个确实地执行hanlder并返回modelAndView的方法。有人可能会注意到我的用词“一些”,为什么是一些,现在还不是回答这个问题的时候。另一个需要注意的点是HandlerMapping和HandlerAdapte在数据初始化的过程中并没有执行。现在把眼光放回到我们的项目中来,有没有觉得springMVC其实并没有初始化一堆interceptor,也没有初始化一堆controller,为什么?因为人家压根就没必要啊,这些东西都在IOC容器中实例化好了,要用的时候通过HandlerMapping拿出来就好了。我们显然没有IOC容器,我们只能自己手动初始化这些数据。
思考一下我们到底该如何初始化。我们不一定要照搬springMVC的方式,我们可以在初始化时便将请求的URL和handler一一匹配,存放在map中,方便到时候取用。而拦截器没有固定的URL映射,我们可以先全部加载出来,在请求到来时再进行匹配