http://geeekr.com/read-spring-source-1-how-to-load-bean/
地址链接
源码分析
org.springframework.web.context.ContextLoaderListener初始化IOC容器的。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
initWebApplicationContext方法是在org.springframework.web.context.ContextLoader类里面。方法太长,分段读一下。
首先是初始化initWebApplicationContext容器
首先determineContextClass()方法查明具体的Context类,他会读取servletContext的初始化参数contextClass,此参数我们一半不配置,所以Spring就会读取跟org.springframework.web.context.WebApplicationContext同一个包下面的ContextLoader.properties文件读取默认设置,反射出org.springframework.web.context.support.XmlWebApplicationContext类来。接下来就是在configureAndRefreshWebApplicationContext()方法里将新创建的XmlWebApplicationContext进行初始化。首先会设置一个默认ID,即org.springframework.web.context.WebApplicationContext:+你项目的ContextPath。
紧接着就是将ServletContext设置成XmlWebApplicationContext的属性,这样Spring就能在上下文里轻松拿到ServletContext了。
接下来就是读取web.xml文件中的contextConfigLocation参数。如果没有配置就会去读WEB-INF下的applicationContext.xml文件。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
并将值设置(就是我们的Spring配置文件的路径)进XmlWebApplicationContext中。然后就会在指定的路径加载配置文件。
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
1初始化initWebApplicationContext 容器
2创建XmlWebApplicationContext进行初始化 ServletContext设置成XmlWebApplicationContext的属性 Spring拿到ServletContext里面的内容
3就是拿到web.xml文件中的contextConfigLocation参数。如果没有配置就会去读WEB-INF下的applicationContext.xml文件。
4接下来就是初始化BeanFactory,是整个refresh()方法的核心,其中完成了配置文件的加载、解析、注册
5根据web.xml文件中的contextConfigLocation参数下的路径解析xml文件
6这里会根据不同的XML节点,会委托NamespaceHandlerSupport找出合适的BeanDefinitionParser,如果我们配置了
| <context:component-scan |
8通过它去扫描包中的类文件,注意:这里是类文件而不是类,因为现在这些类还没有被加载,只是ClassLoader能找到这些class的路径而已。到目前为止,感觉真想距离我们越来越近了。顺着继续往下摸。进入doSacn方法里,映入眼帘的又是一大坨代码,但是我们只关心观点的部分。
9一眼就能看出是通过
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
有时候不得不佩服这些外国人起名字的功力,把扫描出来的类叫做candidates(候选人);真是不服不行啊,这种名字真的很容易理解有不有?哈哈,貌似扯远了。继续往下看。这里只列出方法的主题部分。
10假设我们配置的需要扫描的包名为com.geeekr.service,那么packageSearchPath的值就是classpath*:com.geeekr.service/**/*.class,意思就是com.geeekr.service包(包括子包)下所有class文件;如果配置的是*,那么packageSearchPath的值就是classpath*:*/**/*.class。这里的表达式是Spring自己定义的。Spring会根据这种表达式找出相关的class文件。
11这里会先判断表达式是否以classpath*:开头。前面我们看到Spring已经给我们添加了这个头,这里当然符合条件了。接着会进入findPathMatchingResources方法。在这里又把**/*.class去掉了,然后在调用getResources方法,然后在进入findAllClassPathResources方法。这里的参数只剩下包名了例如com/geeekr/service/
12真相大白了,Spring也是用的ClassLoader加载的class文件。一路追踪,原始的ClassLoader是Thread.currentThread().getContextClassLoader();。到此为止,就拿到class文件了。
Spring会将class信息封装成BeanDefinition,然后再放进DefaultListableBeanFactory的beanDefinitionMap中。
反射代理加载