spring源码(1)之初始化

spring源码之初始化

1.spring的入口

spring的核心是ioc容器。spring如何在tomcat启动的时候,实现IOC容器初始化?这里用到了监听器。

【代码清单】:在web.xml中配置监听器listener

<!--【代码清单】:在web.xml中配置监听器listener-->
<!-- 配置spring初始化监听器 -->
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

在tomcat启动的时候,会自动运行配置的监听器ContextLoaderListener的contextInitialized()方法。这是因为ContextLoaderListener继承了ServletContextListener的接口。

2.spring初始化主要工作

//【代码清单】:contextInitialized()方法
publicvoid contextInitialized(ServletContextEvent event) {
    //创建一个ContextLoader
    this.contextLoader =createContextLoader();
    //初始化ioc容器
    this.contextLoader.initWebApplicationContext(event.getServletContext());
    }

2.1.创建ContextLoader

①为什么需要ContextLoader?

spring的ioc容器有很多,不同的应用层面有不同的ioc服务,如接口的有BeanFactoy和ApplicationContext,继承这两个接口类又有很多,如继承applicationContext的有ConfigurableApplicationContext和WebApplicationContext,而实现这些接口的类也有很多,如实现webApplicationContext的有xmlWebApplictationContext,实现ConfigurableApplicationContext的有FileSystemApplicationContext。这些都是针对不用应用层面提供的ioc服务。FileSystemApplicationContext是在文件目录下解析xml 文件,而xmlWebApplictationContext是在xml配置中获取xml路径并解析。

针对这么多的ioc容器,tomcat启动的时候,到底初始化哪个ioc容器?这就需要一个类来对这些IOC容器进行管理,并决定在tomcat启动的时候,决定加载哪个ioc容器,这个类就是ContextLoader。

实际web项目开发是在web.xml中配置applicationContext.xml文件的路径。因此本文以webapplicationContext作主要解析。

<!--代码清单:web.xml中配置 applicationContext.xml路径-->
<context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:applicationContext*.xml</param-value>
</context-param>

classpath:表示从类路劲加载配置文件,详细讲解,请看spring的策略管理。

②什么是ContextLoader?
//【代码清单】:ContextLoader
publicclass ContextLoader {
//初始化IOC容器
  WebApplicationContext initWebApplicationContext();
//获得当前ioc容器
  WebApplicationContext getCurrentWebApplicationContext();
//关闭ioc容器
  void closeWebApplicationContext
}

ContextLoader主要提供了初始化ioc容器,获取当前ioc容器,和关闭当前ioc容器的几个方法。我们可以创建自己的ContextLoader来加载自己想要的ioc容器。

③怎么创建ContextLoader?
//【代码清单】:createContextLoader()
protected ContextLoadercreateContextLoader() {
        //实例化一个ContextLoader对象
       returnnew ContextLoader();
}

在createContextLoader()方法中主要是new了一个ContextLoader实例。

2.2.初始化IOC容器

初始化IOC容器主要是调用了ContextLoader. initWebApplicationContext()方法。

【代码清单】:initWebApplicationContext()
    public WebApplicationContextinitWebApplicationContext
(ServletContext servletContext)
           throwsIllegalStateException, BeansException {
// 因为webApplicationContext最后要保存到request的*.ROOT常量中,所以这里先判断这个这个变量是否为null,为null则抛出异常。此处代码省略。
       try {
           // 1.获取根上下文,
       ApplicationContextparent = loadParentContext(servletContext);
           //2. 创建webApplicationContext,完成初始化后添加到根上下文中
    this.context =createWebApplicationContext(servletContext, parent);
     //3保存到servletContext.ROOT中
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
     // 4.添加到当前线程中
    currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
      // 此处异常信息,代码省略。
           returnthis.context;
       }
(1)获取根上下文
LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
【代码清单】:loadParentContext()
protected ApplicationContext loadParentContext(ServletContextservletContext)throws BeansException {
  // 1.从ServletContext获取上面那2个常量值
     servletContext.getInitParameter();
  // 2.如果parentContextKey的值为空,则返回一个空的applicationContext,否则
    if (parentContextKey!= null) {
 //3.先判断locatorFactorySelector是否为null,如果是null,则用默认值classpath*:beanRefContext.xml,如果不为null,则根据locatorFactorySelector实例化一个BeanFactoryLocator
           BeanFactoryLocatorlocator =
ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
  // 4.然后用BeanFactoryLocator. useBeanFactory()方法实例化一个parentContextRef .ApplicationContext是parentContextRef的一个field.
 parentContextRef.applicationContext=
 new ClassPathXmlApplicationContext(parentContextKey)
this.parentContextRef = locator.useBeanFactory(parentContextKey);
//5. return parentContextRef.applicationContext
parentContext = (ApplicationContext)
this.parentContextRef.getFactory();
       }
       return parentContext;
    }
(2)创建webApplicationContext
【代码清单】:createWebApplicationContext()
    protectedWebApplicationContext createWebApplicationContext(
           ServletContextservletContext, ApplicationContext parent) throws BeansException {
   //1.判断载入的applicationContext类型,如果没有则默认使用继承WebApplicationContext接口的类。
       ClasscontextClass = determineContextClass(servletContext);
//判断contextClass是否是ConfigurableWebApplicationContext.class类型,不是则抛出异常。此处代码省略。ConfigurableWebApplicationContext是一个继承了webApplicationContext和ConfiguteApplicationContext接口的接口。、
 
  //2.根据contextClass实例化一个ConfigurableWebApplicationContext
       ConfigurableWebApplicationContextwac =
              (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
  //3.添加根上下文
       wac.setParent(parent);
  //4.添加servletContext
       wac.setServletContext(servletContext);
  //5.获取web.xml中配置的contextConfigLocation参数
    wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
 //6.定制Context,这是一个什么也不做的方法,用于拓展,可以忽略
       customizeContext(servletContext,wac);
 //7.解析applicationContext.xml,完成初始化
       wac.refresh();
       return wac;
    }
①判断载入的根applicationContext类型
【代码清单】:determineContextClass()
protected Class determineContextClass(ServletContextservletContext) throws ApplicationContextException {
  //1.获取我们在web.xml中配置的contextClass参数,这个参数可以可以决定我们需要载入的applicationContext类型
       StringcontextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
       if (contextClassName!= null) {
           try {
              return ClassUtils.forName(contextClassName);
           }
       }
       else {
//2.如果没配置有,则使用继承webApplicationContext接口的Class类
           contextClassName= defaultStrategies.getProperty(WebApplicationContext.class.getName());
           try {
              return ClassUtils.forName(contextClassName,ContextLoader.class.getClassLoader());
           }
       }
    }
②实例化一个实现ConfigurableWebApplicationContext的类
【代码清单】:instantiateClass(contextClassName)
    publicstatic Object instantiateClass(Classclazz) throws BeanInstantiationException {
       try {
   //重载instantiateClass方法
           return instantiateClass(clazz.getDeclaredConstructor((Class[])null), null);
       }
    }

在instantiateClass(contextClassName)方法中重载了instantiateClass方法

【代码清单】:instantiateClass(constructor,args)
    publicstatic Object instantiateClass(Constructorctor, Object[] args) throws BeanInstantiationException {
       try {
           //setAccessible(true)
           ReflectionUtils.makeAccessible(ctor);
          //实例化
           returnctor.newInstance(args);
       }
    }
③添加根上下文
【代码清单】:setParent()
publicvoid setParent(ApplicationContextparent) {
       this.parent = parent;
    }
④设置servletContext
【代码清单】:setServletContext()
publicvoid setServletContext(ServletContext servletContext) {
       this.servletContext = servletContext;
    }
⑤获取配置文件信息
CONFIG_LOCATION_PARAM = "contextConfigLocation";
servletContext.getInitParameter(CONFIG_LOCATION_PARAM)

获的web.xml配置contextConfigLocation的参数

【代码清单】:web.xml的contextConfigLocation配置参数

    <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:applicationContext*.xml</param-value>
    </context-param>

【代码清单】:setConfigLocation()

publicvoid setConfigLocation(String location) {
       //String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
        //“制表符(‘\t’)”、“换行符(‘\n’)”、“回车符(‘\r’)”
       setConfigLocations(StringUtils.tokenizeToStringArray(location,CONFIG_LOCATION_DELIMITERS));
    }

tokenizeToStringArray()方法是处理“classpath:applicationContext*.xml”这个字符串成一个数组,然后调用setConfigLocations(String[]locations)方法。

【代码清单】:tokenizeToStringArray()
    publicstatic String[]tokenizeToStringArray(
           Stringstr, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
       StringTokenizerst = new StringTokenizer(str, delimiters);
       Listtokens = new ArrayList();
       while(st.hasMoreTokens()) {
             //分割
           Stringtoken = st.nextToken();
            //前后去掉空格。此处代码省略
             if (!ignoreEmptyTokens || token.length() > 0) {
                 //添加到数组
              tokens.add(token);
           }
       }
       //转字符数组
       returntoStringArray(tokens);
    }

【代码清单】:setConfigLocations(String[] locations)

//configLocations只是一个字符数组
private String[] configLocations;
publicvoid setConfigLocations(String[] locations) {
       if (locations != null) {
           this.configLocations = newString[locations.length];
           for (int i = 0; i <locations.length; i++) {
        //循环遍历,赋给configLocations
           this.configLocations[i] =resolvePath(locations[i]).trim();
           }
       }
       else {
           this.configLocations = null;
       }
    }
⑥解析applicationContext.xml,完成初始化

AbstractRefreshableWebApplicationContext是实现了

ConfigurableWebApplicationContext接口的类,但是

AbstractRefreshableWebApplicationContext中 并没有refresh()方法。当我们一层一层找它的父类的时候,在父类AbstractApplicationContext中找到了refresh()这个方法。

代码清单:AbstractApplicationContext.refresh()
    publicvoid refresh() throws BeansException,IllegalStateException {
       synchronized (this.startupShutdownMonitor) {
           // 1.容器启动的预先准备,记录容器启动的时间和标记.
           prepareRefresh();
           //2.创建BeanFactory,如果已有就销毁,没有就创建.此类实现了对BeanDefinition的装载
           ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory();
           //3.配置BeanFactory标准上下文特性,如类装载器、postProcesser等
           prepareBeanFactory(beanFactory);
           try {
              // 4.在bean被装载后,提供一个修改BeanFactory的入口
              postProcessBeanFactory(beanFactory);
              // 5.调用postProceessBeanFactory
              invokeBeanFactoryPostProcessors(beanFactory);
              //6.注册用于拦截bean创建过程中的BeanPostProcessors
              registerBeanPostProcessors(beanFactory);
              // 7.Initialize message source for this context.
              initMessageSource();
              // 8.Initialize event multicaster for this context.
              initApplicationEventMulticaster();
       //9.Initialize other special beans in specific contextsubclasses.
              onRefresh();
              //10. 注册监听器
              registerListeners();
    //11.完成容器的初始化,里面的preInstantiateSingletons完成单例对象的创建
              finishBeanFactoryInitialization(beanFactory);
              // 12.Last step: publish corresponding event.
              finishRefresh();
           }
       }
    }
时间: 2024-10-08 11:28:46

spring源码(1)之初始化的相关文章

spring源码-bean之初始化-1

一.spring的IOC控制反转:控制反转--Spring通过一种称作控制反转(IOC)的技术促进了松耦合.当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象.说白了就是原本需要手动去创建和管理的对象,现在交给容器了.在容器的基础上,实现对象的控制.这个方式也很好的减少了开发者在框架上面的时间,更多关注点在业务逻辑上面. 二.这表博客的主要目的是研究,bean容器的初始化,会在最基本的bean初始化作源码的解析和分析.如何存在不妥当的地方,还请

spring源码-开篇

一.写博客也有一段时间了,感觉东西越来越多了,但是自己掌握的东西越来越少了,很多时候自己也在想.学那么多东西,到头来知道的东西越来越少了.是不是很奇怪,其实一点都不奇怪. 我最近发现了一个很大的问题,就是自己做开发这么久了.很多原理以及实现都不是很了解,包括自己在假装面试了解自己不足的时候,也突显除了自己的问题所在.面试官会问你,你真的懂这个技术吗.那么它的实现原理是怎么样的,简单的描述一下.当时我就懵逼了,这个问题说严重也严重,说不严重也不严重. 因为,作为2年开发的我.开发和自己积累下来的应

spring源码-自定义标签-4

一.自定义标签,自定义标签在使用上面相对来说非常常见了,这个也算是spring对于容器的拓展.通过自定义标签的方式可以创造出很多新的配置方式,并且交给容器直接管理,不需要人工太多的关注.这也是spring对于配置拓展的一个很重要的方式. 二.自定义标签的几个步骤:1.创建可扫描的标签和对应的解析类  2.读取页面元素解析 3.加入容器管理 三.涉及到的常用类:BeanDefinitionParser.NamespaceHandlerSupport:文件:spring.handlers.sprin

Spring源码阅读:Spring WebApplicationContext初始化与消亡

使用SpringMVC时,需要不论是使用注解配置,还是使用XML配置Bean,他们都会在Web服务器启动后就初始化.根据J2ee的知识可以知道,肯定是使用了ServletContextListener才完成的这个功能.那Spring又是如何实现的呢?还有我们在Web.xml配置的那些applicationContext.xml相关的XML文件的位置(配置方式多样),又是如何读取到相应的文件的呢,读取到这些文件后,是如何初始化类的呢?我们能不能自定义初始化过程或者自定义WebApplication

Spring源码阅读:Spring MVC 初始化

通过之前的源码学习,了解了Spring的两个核心IOC和AOP.也了解到系统初始化时,就已经将所有applicationContext.xml中的bean Definintion加载并初始化了. 如果使用了SpringMVC框架,MVC框架指定的namespace-servlet.xml也已经被初始化了. 使用过SpringMVC,都知道需要在web.xml配置配置DispatcherServlet,它是处理请求的入口.Servlet是单例的,系统指挥在第一次处理用户请求时初始化Servlet对

Spring源码分析(二十四)初始化非延迟加载单例

摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 完成BeanFactory的初始化工作,其中包括ConversionService的设置.配置冻结以及非延迟加载的bean的初始化工作. /** * Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */ protecte

【spring源码分析】IOC容器初始化(一)

前言:spring主要就是对bean进行管理,因此IOC容器的初始化过程非常重要,搞清楚其原理不管在实际生产或面试过程中都十分的有用.在[spring源码分析]准备工作中已经搭建好spring的环境,并利用xml配置形式对类进行了实例化.在test代码中有一个非常关键的类ClassPathXmlApplicationContext,在这个类中实现了IOC容器的初始化,因此我们从ClassPathXmlApplicationContext入手开始研究IOC的初始化过程. 1.ClassPathXm

【spring源码分析】IOC容器初始化(十二)

前言:在doCreateBean方法中还遗留一个问题没有分析:循环依赖.循环依赖在Spring中是非常重要的一个知识点,因此单独进行分析. 什么是循环依赖 循环依赖就是循环引用,两个或两个以上的bean互相引用对方,最终形成一个闭环.如A依赖B,B依赖C,C依赖A.如下图所示: 循环依赖其实就是一个死循环的过程,在初始化A的时候发现引用了B,则就会去初始化B,然后发现B又引用C,则又去初始化C,在初始化C的时候,再次发现C引用了A,则又去初始化A,这样就处于死循环,除非有终结条件. Spring

【spring源码分析】IOC容器初始化(总结)

前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正的bean对象. 总结 [spring源码分析]IOC容器初始化(一):主要分析了Spring是如何解析占位符以及BeanFactory的最终实现类DefaultListableBeanFactory. [spring源码分析]IOC容器初始化(二):以loadBeanDefinitions函数为切