写出一个你自己的MVC框架-基于对springMVC源码实现和理解(2):数据初始化(一)

按住ctrl再单击DispatcherServlet,等到MyEclipse下载完源代码,这是一个一千多行代码的大类,我们没有看到Init(),没有看到doGet(),和doPost()。且耐下心来,细细琢磨。

首先,这个Sevlet并不直接继承自HttpServlet,而是继承自FrameworkServlet类。不必惊讶这个类最终还是集成自HttpServlet,一些需要重写的方法也在一层层的继承中,封装了一遍又一遍。

此时需要了解一个前提,springMVC附着于spring之上,我们无法撇开spring单纯地写出一个和springMVC一模一样的框架来,我们能做的只是了解springMVC的处理过程,用自己的方式进行模拟。

DispatcherServlet做为调度总指挥,一些数据的初始化时必不可少的,这一节,我们开始了解DispatcherServlet数据初始化过程。我们把眼光放在这段代码上

  1. /** 
  2.      * This implementation calls {@link #initStrategies}. 
  3.      */  
  4.     @Override  
  5.     protected void onRefresh(ApplicationContext context) {  
  6.         initStrategies(context);  
  7.     }  
  8.   
  9.     /** 
  10.      * Initialize the strategy objects that this servlet uses. 
  11.      * <p>May be overridden in subclasses in order to initialize further strategy objects. 
  12.      */  
  13.     protected void initStrategies(ApplicationContext context) {  
  14.         initMultipartResolver(context);  
  15.         initLocaleResolver(context);  
  16.         initThemeResolver(context);  
  17.         initHandlerMappings(context);  
  18.         initHandlerAdapters(context);  
  19.         initHandlerExceptionResolvers(context);  
  20.         initRequestToViewNameTranslator(context);  
  21.         initViewResolvers(context);  
  22.         initFlashMapManager(context);  
  23.     }  
各种init,我想这就是数据的初始化部分了吧,来看看注释:初始化servlet的必备对象。再看看onRefresh()父类中的实现:
  1. /** 
  2.      * Template method which can be overridden to add servlet-specific refresh work. 
  3.      * Called after successful context refresh. 
  4.      * <p>This implementation is empty. 
  5.      * @param context the current WebApplicationContext 
  6.      * @see #refresh() 
  7.      */  
  8.     protected void onRefresh(ApplicationContext context) {  
  9.         // For subclasses: do nothing by default.  
  10.     }  
注释中说,这是一个可被重写的模版方法,该方法会在上下文refresh成功后被调用,这里有个参数WebApplicationContext,熟悉Spring的同学应该知道,这是spring提供的上下文环境,我们不准备实现spring所以忽略之。回到之前的initStrategies方法,一共有9个初始化过程,我们把目光放在最核心的两个初始化过程上,initHandlerMappings(context);initHandlerAdapters(context);  前者的核心代码为:
  1. HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);  
也就是从上下文环境中取出HandlerMapping对象。先不要纠结我们没有什么上下文环境,先看看这个HandlerMapping是什么鬼。这是一个接口,里面有个核心方法
  1. /** 
  2.      * Return a handler and any interceptors for this request. The choice may be made 
  3.      * on request URL, session state, or any factor the implementing class chooses. 
  4.      * <p>The returned HandlerExecutionChain contains a handler Object, rather than 
  5.      * even a tag interface, so that handlers are not constrained in any way. 
  6.      * For example, a HandlerAdapter could be written to allow another framework's 
  7.      * handler objects to be used. 
  8.      * <p>Returns {@code null} if no match was found. This is not an error. 
  9.      * The DispatcherServlet will query all registered HandlerMapping beans to find 
  10.      * a match, and only decide there is an error if none can find a handler. 
  11.      * @param request current HTTP request 
  12.      * @return a HandlerExecutionChain instance containing handler object and 
  13.      * any interceptors, or {@code null} if no mapping found 
  14.      * @throws Exception if there is an internal error 
  15.      */  
  16.     HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;  
这注释够长的,概括一下就是:该方法通过request对象返回有一个匹配的handler和一些拦截器,封装在HandlerExecutionChain中,简单地说就是返回request对应的控制器和拦截器的集合体。看看HandlerExecutionChain里是啥:

最主要的内容是两个成员变量:

  1. private final Object handler;  
  2.   
  3. private HandlerInterceptor[] interceptors;  
意思在明白不过了。

重点看三个方法:

  1. boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response)  
  2. void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)  
  3. void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)  
你就说你熟不熟!第一个在requestMapping注解的方法调用前执行,第二个在方法调用后,页面加载前执行,第三个在页面加载后执行。至于具体怎么做的看一段代码:
  1. if (getInterceptors() != null) {  
  2.             for (int i = 0; i < getInterceptors().length; i++) {  
  3.                 HandlerInterceptor interceptor = getInterceptors()[i];  
  4.                 if (!interceptor.preHandle(request, response, this.handler)) {  
  5.                     triggerAfterCompletion(request, response, null);  
  6.                     return false;  
  7.                 }  
  8.                 this.interceptorIndex = i;  
  9.             }  
  10.         }  
  11.         return true;  
知道为什么要实现HandlerInterceptor接口了吧。这里有个重点:在写这系列文章前,我也看过一些作者对springMVC理解的文章,部分作者认为postHandler,和afterCopletion只有在preHandler返回true时才执行,这里我只能说:大错特错。具体看代码吧。

不要问我所谓的拦截器栈,栈在哪里,人家压根就没用stack,就用了个数组模拟了一下,不信?看吧:这是 applyPostHanler的代码:

  1. for (int i = getInterceptors().length - 1; i >= 0; i--) {  
  2.             HandlerInterceptor interceptor = getInterceptors()[i];  
  3.             interceptor.postHandle(request, response, this.handler, mv);  
  4.         }  

至于getHandler(request)内部到底怎么实现,我只能说,底层实现进过了层层封装,要贴代码也贴不过来了,只要知道getHandler()方法首先通过lookupHandler()方法(url匹配)从spring上下文中获得了一个handler对象,再通过getHandlerExecutionChain(handler, request)装填了匹配的interceptor,封装成一个HandlerExecutionChain返回。知道了大概意思,我们就能自己实现了。

另一个方法:initHandlerAdapters():首先我们想来看看这个HandlerAdapter是什么,查看其源代码,可以发现,这是一个接口,被许多个类实现了,留意其中的AbstractHandlerMethodAdapter类(注意AnnotationMethodHandlerAdapter在3.2版本后已经过期!),这是一个抽象类,拥有子类RequestMappingHandlerAdapter,这才是我们关注的重点。其中的关键方法为:

  1. private ModelAndView invokeHandleMethod(HttpServletRequest request,  
  2.             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映射,我们可以先全部加载出来,在请求到来时再进行匹配

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值