码迷,mamicode.com
首页 > 编程语言 > 详细

spring源码(1)之初始化

时间:2015-11-27 01:20:41      阅读:227      评论:0      收藏:0      [点我收藏+]

标签:spring源码 初始化

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启动的时候,会自动运行配置的监听器ContextLoaderListenercontextInitialized()方法。这是因为ContextLoaderListener继承了ServletContextListener的接口。

2.spring初始化主要工作

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


2.1.创建ContextLoader

①为什么需要ContextLoader?

  springioc容器有很多,不同的应用层面有不同的ioc服务,如接口的有BeanFactoyApplicationContext,继承这两个接口类又有很多,如继承applicationContext的有ConfigurableApplicationContextWebApplicationContext,而实现这些接口的类也有很多,如实现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);
       }
    }

  instantiateClasscontextClassName)方法中重载了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.xmlcontextConfigLocation配置参数

    <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();
           }
       }
    }

本文出自 “总有贱人想害朕。” 博客,请务必保留此出处http://yoyanda.blog.51cto.com/9675421/1717243

spring源码(1)之初始化

标签:spring源码 初始化

原文地址:http://yoyanda.blog.51cto.com/9675421/1717243

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!