spring mvc 启动过程及源码分析

由于公司开源框架选用的spring+spring mvc + mybatis。使用这些框架,网上都有现成的案例;需要那些配置文件、每种类型的配置文件的节点该如何书写等等。如果只是需要项目能够跑起来,只要按照网上的例子依葫芦画瓢就可,项目也能够运行起来。但是对于有长远目标的人来说,一件事应当知其然更要知其所以然。所以结合网上的其他人阅读spring源码的经验(网上很多人的阅读经验是按照spring分出的那些模块[七大模块:corecontextdaoormwebaopweb mvc]来解读)。但是本篇想换个角度来。就根据自己平时项目运行的那个角度来解读。大概得到了解下为什么在配置文件中加上对应的扫描路径,spring容器就能够加载这个路径下面的所有相关的类;我们定义的类是在哪个步骤实例化的、类与类之间的是怎么装配的等等。

对于web项目,我们知道tomcat容器首先加载的文件就是项目中配置的web.xml。在这个文件里面,配置者项目启动需要的所有资源。因此,调试的第一步就是读懂web.xml。无论是sshssm类型的项目来说,web.xml的配置都一样;需要配置springContextLoaderListener监听器;如下:

 

<listener>      
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

需要配置一个servlet,这样,将所有的请求都拦截交给spring来处理

 

<servlet>
  <servlet-name>springServlet</servlet-name>  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring-mvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>springServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

 

web.xml的加载顺序:context-param -> listener -> filter -> servlet

对于context-param来说,它就像是Java里的静态变量,在容器一启动的时候就将对应的值加载进容器里边了。由以下配置文件可看出

 

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:spring.xml</param-value>
</context-param>
<listener>  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

那就先从contextLoadListener入手,首先查看它的类图:它的作用是在容器初始化的时候做些操作和在容器销毁,程序销毁的时候做资源回收的事情。所以终点关注下 ContextLoaderListenercontextInitialized方法(调用父类)

初始化context,将context设置为XmlApplicationContext来处理容器资源的初始化。

 

 

获取contextParam中的配置信息。继而调用ConfigurableWebApplicationContext.redresh()。在refresh方法里面会实例声明的bean

实例化bean

ConfigurableWebApplicationContext.redresh()。多态的呈现,父类引用指向子类对象(AbstractApplicationContext)。

XmlApplicationContext对配置文件的解析。首先会获取web.xml配置文件路径,然后将这些路径循环给到xmlBeanDefinition来解析。

 

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    String[] configLocations = this.getConfigLocations();
    if(configLocations != null) {
        String[] var3 = configLocations;
        int var4 = configLocations.length;
        for(int var5 = 0; var5 < var4; ++var5) {
            String configLocation = var3[var5];
            reader.loadBeanDefinitions(configLocation);
        }
    }

 

转换为对应时序图如下:

最后,所有的解析xml工作都交给了XMLBeanDefinitionReader

 

Document doc = this.doLoadDocument(inputSource, resource);
return this.registerBeanDefinitions(doc, resource);

 

遇到对应的标签做相应的操作。xml中的namespace对应的handler如下:

 

 

public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
    this(classLoader, "META-INF/spring.handlers");
}

实际读取的文件是spring内置的配置。详细如下(每个namespace对应的handler):

 

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

 

spring.xmlcontext标签为例。当程序解析到这个标签的时候,会找到标签所映射的具体handler。每个标签解析完找到对应的handler后都会调用init()init方法体如下:每一个标签值对应的parser,解析类是不同的。

 

public void init() {
    this.registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
    this.registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
    this.registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
    this.registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
    this.registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
    this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    this.registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
    this.registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}

每一个标签值对应的parser,解析类是不同的。已最常见的自动扫面包路径来说,component-scan

 

 

 

这样,配置文件中的bean就被实例化了。

以上就是针对spring mvc框架启动过程中所做的那些事。做的简答的概述。方法间调用关系没找到好的方式表现出来,所以就选了时序图这种方式来体现。

 

posted @ 2017-09-08 10:59  Ronaldo7  阅读(770)  评论(0编辑  收藏  举报