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

按住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映射,我们可以先全部加载出来,在请求到来时再进行匹配

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值