Spring加载javaBean过程

Spring加载普通的Bean的过程

今天看到有人问这个问题,于是我从源码中摘抄并借阅百度文档部分内容。

学习后做一个笔记,以后说不定会用上。

首先先从配置:

<!--   配置spring启动listener入口--> 

  <listener>

<listener-class>

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

  </listener>

入手初始化IOC容器。

ContextLoaderListener继承了ContextLoader并实现了ServletContextListener接口,当项目启动时,会收到初始化请求。

 

重写ServletContextListener的方法,并在方法中调用父类initWebApplicationContext方法来初始化。

此方法中:

if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) !=null)

            throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");

        Log logger = LogFactory.getLog(org/springframework/web/context/ContextLoader);

        servletContext.log("Initializing Spring root WebApplicationContext");

        if(logger.isInfoEnabled())

            logger.info("Root WebApplicationContext: initialization started");//是不是很熟悉

首先判断servletContext中是否已经注册WebApplicationContext然后开启日志写出我们很熟悉的started。

然后执行代码:

 

现创建了Context:

 

determineContextClass方法查具体的Context类读取初始化参数contextClass,一般我们不进行配置,然后ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);拿到默认的设置。(Spring写好的配置)

随后执行:configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)context, servletContext);来进行初始化。

 

此方法首先先设置一个默认的id。contextId,后面将配置(ConfigurableWebApplicationContext)set进拿到的上下文中。

接下来读取你的配置文件: String initParameter = sc.getInitParameter("contextConfigLocation");

极其熟悉的名字

   <!-- spring配置文件路径 -->
  <context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath*:springAnnotation-core.xml</param-value>  
        <!-- <param-value>classpath*:config/springAnnotation-servlet.xml</param-value> -->  
  </context-param> 

后面将得到的配置文件路径加入ConfigurableWebApplicationContext中并执行核心方法:wac.refresh();

 

 prepareRefresh();记录时间输出日志,初始化一些东西。

接下来初始化BeanFactory:

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

首先:

 

refreshBeanFactory();

 

创建了bean工厂:DefaultListableBeanFactory实例

beanFactory.setSerializationId(getId());set进一个id

  customizeBeanFactory(beanFactory);配置一些参数

重要的方法:

 

此方法会通过XmlBeanDefinitionReader加载bean定义。

然后根据配置初始化Reader。

具体的实现方法是在XmlBeanDefinitionReader.loadBeanDefinitions方法中定义的。主要就是加载Spring所有的配置文件,以备后面解析,注册用。

方法中有doLoadBeanDefinitions方法然后调用registerBeanDefinitions方法

 

创建完Reder后调用registerBeanDefinitions方法注册。后调用 

doRegisterBeanDefinitions(Element root)方法

这里创建BeanDefinitionParserDelegate实例来解析XML。

 parseBeanDefinitions(root, _flddelegate);来加载类

 

解析xml标签。

 

通过以上方法根据不同的XML节点,会委托NamespaceHandlerSupport找出合适的BeanDefinitionParser。

 

 

例如我们配置

<context:component-scan base-package="com.service.dao" />

那就调用

 componentScanBeanDefinitionParser

这里定义了一个ClassPathBeanDefinitionScanner,通过它去扫描包中的类文件,注意:这里是类文件而不是类,因为现在这些类还没有被加载,只是ClassLoader能找到这些class的路径而已。

随后在doScan方法中Set candidates = findCandidateComponents(basePackage);

String packageSearchPath = (new StringBuilder("classpath*:"))

.append(resolveBasePackage(basePackage))

.append("/").append(resourcePattern).toString();

假设我们配置的需要扫描的包名为com.service,那么packageSearchPath的值就是classpath*:com.service/**/*.class,意思就是com.service包(包括子包)下所有class文件;如果配置的是*,那么packageSearchPath的值就是classpath*:*/**/*.class。这里的表达式是Spring自己定义的。Spring会根据这种表达式找出相关的class文件。

然后通过:Resource resources[] =resourcePatternResolver.getResources(packageSearchPath);拿到文件

 

进入findPathMatchingResources方法。在这里又把**/*.class去掉了,然后在调用getResources方法,然后在进入findAllClassPathResources方法。这里的参数只剩下包名了例如com/geeekr/service/。

 

最后一切工作完毕,该配置的配置了,该拿到的路径拿到了。

Spring调用Thread.currentThread().getContextClassLoader();。到此为止,就拿到class文件了。 Spring会将class信息封装成BeanDefinition,然后再放进DefaultListableBeanFactory的beanDefinitionMap中。

总结:

项目启动时先通过listener初始化容器,初始化WebApplicationContext,主要是讲创建好的ConfigurableWebApplicationContext放入上下文并读取配置文件。 然后创建beanfactory,配置好参数后创建一个Reader,然后再loadBeanDefinitions中利用刚才的Reader加载配置文件并注册,在

doRegisterBeanDefinitions方法中解析XML并根据不同的节点委托NamespaceHandlerSupport找出合适的BeanDefinitionParser。BeanDefinitionParser主要是拿到想应的class路径,该配置的配置好,路径全拿到

Spring调用Thread.currentThread().getContextClassLoader();。到此为止,就拿到class文件了。 Spring会将class信息封装成BeanDefinition,然后再放进DefaultListableBeanFactory的beanDefinitionMap中。

猜你喜欢

转载自blog.csdn.net/dc282614966/article/details/81480002