Spring MVC源码剖析系列(1) - DispatcherServlet工作原理之初始化

最近接手一个项目,用的是SpringMVC框架。循例想剖析一下它的源码,以求更深刻地理解MVC的实现。 DispatcherServletSpringMVC最核心的类,就拿它开刀:)

web.xml中定义DispatcherServlet和它的Mapping:

DispatcherServlet继承于FrameworkServlet,而FrameworkServlet又继承于HttpServletBean, HttpServletBean再继承于HttpServlet:

DispatcherServlet有一段静态代码块值得关注:
它会到ClassPath下加载一个叫DispatcherServlet.properties的文件的内容,在SpringMVC的jar包里面有这个文件: 你可以理解为SpringMVC为你预先定义好一些后面有可能会用到的类。

Servlet容器(如Jetty)会调用Servlet的init方法,在这里,HttpServletBean重写了init()方法如下:
上面方法中值得注意的是调用了initServletBean方法,这个方法在FrameworkServlet中被重写了: 我们看到这里将会通过调用initWebApplicationContext方法来初始化一个webApplicationContext,这个非常重要,MVC的所有类都是在这个Context里面被加载进来的,我们继续往下看是如何初始化这个Context的: 我们看到它先尝试去找一个rootContext,这个rootContext有点tricky,如果你在web.xml里面指定以下listener: 那它ContextLoaderListener就会先去初始化一个Context(通过它的contextInitialized方法),并把这个context存储在ServletContext的attribute里面,名字为: 轮到FrameworkServlet初始化Context的时候,就会把它从ServletContext中取出来,作为parent context传给FrameworkServlet要初始化的Context,让它能够拿到之前已经加载的Spring Bean: 在创建Context的时候,先去拿到Context的class对象, 因为我们并没有在web.xml里面给DispatcherServlet配置任何参数,所以就会用默认的Context Class: XmlWebApplicationContext, 是从xml文件来构建这个context。 注意到我们也并没有在web.xml里面指定了任何contextConfigLocation(xml配置文件),所以至此,该Context的ConfigLocation仍为空的。我们继续往下看configureAndRefreshWebApplicationContext这个方法: 我们要留意里面调用了wac.setNamespace(getNameSpace()), 这个在后面大有作为,我们先看这个getNameSpace方法返回的是什么: 我们可以看到,getNameSpace返回的字符串是在web.xml里面给DispatcherServlet定义的名字"dispatcher"加上“-servlet”这个后缀,也就是“dispatcher-servlet”,这个namespace其实是为了前面所说的没有指定context配置文件而准备的,如果没有指定,将会用这个namespace作为默认的名字取寻找xml配置文件。这一步是在当调用context的refresh方法时去加载bean definition的时候实现的,在这里指的是XmlWebApplicationContext的loadBeanDefinitionis方法: 如上文所说我们并没有显式配置xml文件,所以将会使用默认的名字取寻找: 可以看到最后尝试去找的xml配置文件是:/WEB-INF/dispatcher-servlet.xml 至此,针对DispatcherServlet的基于这个dispatcher-servlet.xml文件的Spring Context就会被初始化,在DispatcherServlet里面就能拿到在dispatcher-servlet.xml中定义好的Spring Bean了。 我们再回到FrameworkServlet的initWebApplicationContext方法里面: 我们可以看到在初始化完Context后,会调用onRefresh方法,那我们看一下DispatcherServletde这个onRefresh方法会做些什么: 我们再回过头来看看dispatcher-servlet.xml文件里面定义了些什么: 又是controller,又是mapping的,顾名思义,就是定义MVC中的V(view)和C(Controller),还有他们之间的mapping关系。 我们再看DispatcherServlet的onRefresh方法中调用的initStrategies方法,一眼就能发现跟dispatcher-servlet.xml文件中定义的东西相关联的几个方法: 我们逐个来看,先看initHandlerMappings方法: 它是去Context里面找在dispatcher-servlet.xml里面定义好的HandlerMapping的实现类,比如我们这里定义的是:
然后把所有找到的HandlerMapping实现类对象加到一个叫handlerMappings的ArrayList里面存放着,如果我们没有在dispatcher-servlet.xml里面定义,它将会去前文所说的DispatcherServlet.properties文件里面找一个默认的出来。 对于initHandlerAdapters方法,有着类似的处理: 不同的时,它找的是HandlerAdapter类,把它存放在名为handlerAdapters的ArrayList里面。因为我们并没有在dispatcher-servlet.xml里面定义了任何HandlerAdapter,那它同样会去DispatcherServlet.properties文件里面找默认的: 对于initViewResolvers方法,同样的处理,找的是ViewResolver类: 我们在dispatcher-servlet.xml里面定义的ViewResolver是:

这些Handler, HandlerAdapter, HandlerMapping,ViewResolver都会在DispatcherServlet后续的执行中派上用场。

至此,DispatcherServlet的初始化算是完成了。下一篇我将会分析它是如何处理请求的。

comments powered by Disqus