文档章节

Spring ApplicationContext源码分析

 慕哲
发布于 2017/04/15 16:55
字数 2881
阅读 201
收藏 2

代码的入口:

@Test
public void testFileXmlApplicationContext() {

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/spring.xml");

    Person person = applicationContext.getBean("person", Person.class);
    person.info();
}

其中 ClassPathXmlApplicationContext 的类的继承层次图:

 

Spring加载的时序图:

这个过程中关键的方法是  AbstractApplicationContext,这个方法中挂念的方法是  refresh()方法。这个方法中有13个步骤。refresh是一个典型的模板模式,其中对应ApplicationContext的加载是在AbstractApplicationContext中实现的。其中依赖的一个步骤是交给不同的子类来实现的。分别对这个13个步骤进行描述:

1.prepareRefresh();// Prepare this context for refreshing.准备一下,这个Context将会进行刷新。

//为刷新准备此上下文,设置其启动日期和
		//活动标志以及执行属性源的初始化。
	protected void prepareRefresh() {
  	 this.startupDate = System.currentTimeMillis();
  	 this.closed.set(false);
  	 this.active.set(true);

  	 if (logger.isInfoEnabled()) {
     	 logger.info("Refreshing " + this);
   	}

   	// 初始化所有的上下文环境中的占位符属性元,点进去一看啥都没有。
   	initPropertySources();

  	 // 验证所有的required属性被正确的设置。//这个到底干了啥呀?这个requred属性到底是那些呢?这个没有用过。
  	 getEnvironment().validateRequiredProperties();

   	// 先将ApplicationEvent的队列给建立起来。
  	 this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
	}

2、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 这个是一个将BeanFactoy的加载的过程,这个方法执行完毕后,所有的定义的BeanDefinition和其他的对象都会被加载到当前BeanFactory中来

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   		refreshBeanFactory();//刷新一下BeanFactory,这个方法是一个抽象方法,具体的实现是在其子类(AbstractRefreshableApplicationContext)中实现的。
   		ConfigurableListableBeanFactory beanFactory = getBeanFactory();//获取到对应的BeanFactory,然后返回。
   		if (logger.isDebugEnabled()) {
    		  logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
  		 }
   		return beanFactory;
	}

可以看一下  (AbstractRefreshableApplicationContext)中的refreshBeanFactory()方法。

2.1、refreshBeanFactory()

protected final void refreshBeanFactory() throws BeansException {
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

第一步:判断当前是否有BeanFactory,如果有的话,将BeanFactory ,缓存等相关信息清空。

第二步:创建一个DefaultListableBeanFactory,其中BeanFactory就创建在AbstractRefreshableApplicationContext中进行维护。

第三步:定制当前的BeanFactory,这个主要是能否进行重写和循环引用。

第四步:加载所有的Beans到BeanFactory中来。loadBeanDefinitions(DefaultListableBeanFactory beanFactory)。这个方法是一个抽象方法。这个方法没有实现。交给了子类去实现。

这个方法没有实现的原因:当前的类是AbstractRefreshableApplicationContext中,当前的对象并不知道Resource是哪一个,没有办法确认应该将加载的任务委托给哪一个BeanDefinitionReader。对于  AbstractRefreshableApplicationContext 来讲,就是讲  refreshBeanFactory就是将BeanFactory创建好,并加载好。当时如何加载的还不能确定,这个需要交给底层的方法去实现。

2.2、loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

这个时候看下loadBeanDefinitions(beanFactory) 是如何实现的。

这个方法是在AbstractXmlApplicationContext中实现的。,从类名中可以看出这个方法是XML的,主要是处理以Xml的方式定义的BeanDefinition信息。AbstractXmlApplicationContext,这个类的两个子类是ClassPathXmlApplicationContext 和FileSystemXmlApplicationContext 。在子类中定义了Resource的类型。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);//创建一个XmlBeanDefinitionReader,这个类的选择和 AbstractXmlApplicationContext 对应。

   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());//设置当前的environment的对象。
   beanDefinitionReader.setResourceLoader(this);//设置对应的ResourceLoader。 ApplicationContext是ResourceLoader的子类。
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
   initBeanDefinitionReader(beanDefinitionReader);//设置 reader的属性validateing,设置是否使用xml验证。缺省是true。
   loadBeanDefinitions(beanDefinitionReader);//加载对应的BeanDefinitionReader对象。
}

 

2.3、loadBeanDefinitions(XmlBeanDefinitionReader reader)

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();//首先是getResources().
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();//这里是所有定义的文件的地址。
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

其中:Resource[] getConfigResources() 这个方法是获取所有的Resource的方法。这个方法教给了子类ClassPathXmlApplicationContext或者是FilePathXmlApplicationContext实现的。

String[] getConfigLocations()  是AbstractRefreshableConfigApplicationContext,中定义的方法,这个其中在ClassPathXmlApplicationContext中的方法主要是通过这个 void setConfigLocations(String... locations)将配置文件地址的信息写到当前的对象中的。AbstractRefreshableConfigApplicationContext 类主要定义的就是当前FilePathXmlApplicationContext和ClassPathXmlApplicationContext中定义的locations地址。这个类一般是 ClassPathXmlApplicationContext 和FileSystemXmlApplicationContext 一起配套使用的。

 

3、prepareBeanFactory(beanFactory);//对Bean Factory中的一些其他的信息信息配置。

        在上一步中主要是建立了一个DefaultListableBeanFactory,然后所有定义在xml文件中的  BeanDefinition的信息加载到当前的BeanDefinition中来。这个过程中BeanDefinition并没有被实例化,还是一些定义的信息。这里先对BeanFactoy中配置一些基础的参数。具体代码如下。将ClassLoader,一些其他的基础的类型加载到对应的系统中来。

4、postProcessBeanFactory(beanFactory)

在标准的 初始化之后,需要修改ApplicationContext中的内部BeanFactory的内容。所有的Bean  Definition都被load,但是还没实例化。这能够实现将特定的  BeanPostProcessor注册到特定的  ApplicationContext的实现中去。这个方法是为了 AbstractApplicationContext 的子类来实现的。

5.invokeBeanFactoryPostProcessors(beanFactory);//触发后置处理器。

  BeanFactoryPostProcessor这个类是BeanFactory 的PostProcessor的实现。BeanFactoryPostProcessor主要实现的可以对BeanDefinition的定义(配置数据,并不是实例化对象)进行处理。IOC容器允许BeanFactoryPostProcessor在实例化BeanDefinition之前对当前的配置数据进行修改。用户可以扩展多个BeanFactoryPostProcessor来实现对于BeanDefinition定义的数据进行修改。其中BeanFactoryPostProcessor主要是定义在了AbstractApplicationContext对象中。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

   // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
   if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }
}

 

第一步是public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors)//将BeanFactoryPostProcessor 作用在当前的BeanFactory中去。

第二步是 其中如果当前的BeanFactoy中包含了  loadTimeWeaver bean的时候,这个时候需要配置一个LoadTimeWeaverAwareProcessor,同时添加一个 新的ClassLoader来实现加载是的Weaveer。

6、registerBeanPostProcessors(beanFactory);//注册BeanPostProcessor到当前的BeanFactory中去。

简单介绍下BeanPostProcessor;如果我们想对实例化后的BeanDefinition对象进行处理,可以通过实现一个 BeanPostProcessor的子类。BeanPostProcessor 是容器级别的,只有定义在一个容器中BeanPostProcessor才会作用。其中在ApplicationContext和BeanFactoy中是ApplicationContext会在refresh中将当前所有的BeanPostProcessor注册到BeanFactory中去。但是BeanFacoty需要自己手动的调用BeanFactoy中的addBeanPostProcessor来实现。

主要是调用  PostProcessorRegistrationDelegate 方法中对应的 registerBeanPostProcessors,方法来将所有的BeanProcessor注册到对应的BeanFactory中去的。

7.protected void initMessageSource()//初始化MesageSource

protected void initMessageSource() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {//如果BeanFactoy中已经定义了MessageSource的实例
      this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);//就使用这个MessageSource。
      // Make MessageSource aware of parent MessageSource.
      if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {//然后看下如果这个Mesage是支持继承的,就把parenetBeanFactory(如果有)中的MessageSource放进去。
         HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
         if (hms.getParentMessageSource() == null) {
            // Only set parent context as parent MessageSource if no parent MessageSource
            // registered already.
            hms.setParentMessageSource(getInternalParentMessageSource());
         }
      }
      if (logger.isDebugEnabled()) {
         logger.debug("Using MessageSource [" + this.messageSource + "]");
      }
   }
   else {//如果没有定义,那么就使用一个默认的   DelegatingMessageSource 。
      // Use empty MessageSource to be able to accept getMessage calls.
      DelegatingMessageSource dms = new DelegatingMessageSource();
      dms.setParentMessageSource(getInternalParentMessageSource());
      this.messageSource = dms;
      beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
      if (logger.isDebugEnabled()) {
         logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
               "': using default [" + this.messageSource + "]");
      }
   }
}

简而言之:MessageSource主要是首先看用户是否定义了,如果定义了再看下是否是支持继承的MessageSource,如果支持就把父BeanFacoty中的MessageSource设置进去。如果用户没有定义就设置一个默认的DelegatingMessageSource

8.initApplicationEventMulticaster();//初始化事件广播

主要实现是看用户是否自定了事件广播,如果定义了就设置为用户制定的事件广播,否知设置一个 默认的时间广播。

 

9.onRefresh()//这个方法是一个模板方法,是交给子类来扩展的。

 

10.registerListeners();//注册所有的Listener。

protected void registerListeners() {
   // Register statically specified listeners first.
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
   }

   // Do not initialize FactoryBeans here: We need to leave all regular beans
   // uninitialized to let post-processors apply to them!
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }

   // Publish early application events now that we finally have a multicaster...
   Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
   this.earlyApplicationEvents = null;
   if (earlyEventsToProcess != null) {
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
         getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}

这个过程首先是将  当前AbstractApplicationContext中有的所有的ApplicationListener注册广播器中的Listener中去。

然后将所有的用户定义的ApplicationListener注册到广播中去。

然后将所有的  this.earlyApplicationEvents 中的定义的事件进行广播。

11.finishBeanFactoryInitialization(beanFactory);//实例化所有的非延迟加载的单例Bean对象。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // Initialize conversion service for this context.
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no bean post-processor
   // (such as a PropertyPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
         @Override
         public String resolveStringValue(String strVal) {
            return getEnvironment().resolvePlaceholders(strVal);
         }
      });
   }

   // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // Allow for caching all bean definition metadata, not expecting further changes.
   beanFactory.freezeConfiguration();

   // Instantiate all remaining (non-lazy-init) singletons.
   beanFactory.preInstantiateSingletons();
}

首先为这个ApplicationContext设置 CONVERSIONSERVICE的BeanName。

然后对于 LoadTimeWeaverAware 的Bean进行加载。

将零时的ClassLoader设置为null。

清空缓存 frozenBeanDefinitionNames

初始化剩余的所有的 非懒加载的  单例Singleton的单实例。

12:finishRefresh();//  发布相关的事件

protected void finishRefresh() {
   // 为这个ApplicationContext设置LifecycleProcessor。
   initLifecycleProcessor();

   // Propagate refresh to lifecycle processor first.
   getLifecycleProcessor().onRefresh();

   // Publish the final event.
   publishEvent(new ContextRefreshedEvent(this));

   // Participate in LiveBeansView MBean, if active.
   LiveBeansView.registerApplicationContext(this);
}

第一步是如果当前容器中没有 定义LifeCleProcessor那么就使用一个默认的 DefaultLifecycleProcessor。关于LifeCycleProcessor的相关知识,会后面单独来将,这里按下不表。

第二步获取到所有的LifecycleProcessor对象,然后使用这个调用 onRefesh()对象来实现的。

第三步:发布最终实现的事件

第四步:不太了解,和LiveBeansView 相关。

13. resetCommonCaches()//将常用的cache都青空。

 

总结:

对于ApplicationContext的继承体系。

ClassPathXmlApplicationContext;类主要是确定当前的定义的Resource的位置。同时调用父类的方法来完成依赖注入的过程。

  1.   protected Resource[] getConfigResources()//继承AbstractXmlApplicationContext;这个方法会被父类调用。
  2.  public void setConfigLocations(String... locations)//调用了父类 AbstractRefreshableConfigApplicationContext的方法,这个方法是设置Resource对应的locations。

AbstractXmlApplicationContext:当前类主要是对于类的BeanDefinition的方法进行加载。

  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)//这个方法是继承自AbstractRefreshableApplicationContext:这个方法的主要是从底层方法指定的Resoure信息,和父类中指定的DefaultListableBeanFactory,使用一个合适的BeanDefinitionReader来将BeanDefinition的继承信息填到对应的BeanFactoy中。当前类是AbstractXmlApplicationContext,所以用XmlBeanDefinitionReader来读取。其实这个方法是一个来料加工的过程。
  2. protected String[] getConfigLocations()//在loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的方法中调用了AbstractRefreshableConfigApplicationContext;中的方法,其中对应的locations地址是在ClassPathXmlApplicationContext中设置的。

AbstractRefreshableConfigApplicationContext:这个方法主要维护locations的属性。以及其他的一些配置信息。

AbstractRefreshableApplicationContext :这个方法中主要是维护了BeanFactory。其中DefaultListableBeanFactory就是在这个类中创建的。

  1. protected final void refreshBeanFactory();//当前方法继承自AbstractApplicationContext;主要工作是初始化 了DefaultListableBeanFactory。
  2. protected void cancelRefresh(BeansException ex);//  关闭refres的过程,同时将异常形象传递过来。

 

AbstractApplicationContext:这个方法是ApplicationContext体系的核心,其中refresh()方法是核心中的关键,实现了 IOC的依赖注入的关键步骤。同时依赖子类实现BeanDefinion的信息加载的过程。然后根据BeanFactoy ,对BeanFactoyPostProcessor,MessageSource,Listener,BeanPostProcessor等功能的实现,以及对于所有的非延迟加载的BeanDefinition进行实例化。

© 著作权归作者所有

粉丝 0
博文 7
码字总数 19795
作品 0
私信 提问
Spring Boot 启动流程源码分析

本文从源码的角度上分析了Spring Boot的启动流程。内容仅供参考使用,有不足之处请及时指出,也欢迎大家交流探讨。 Spring Boot 启动类 Spring boot启动类相关代码 Spring boot启动类分析 Sp...

村头细雨忆流年
2018/07/24
0
0
Spring Boot 运作原理

1、Spring Boot 简介 SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义...

编程SHA
02/22
0
0
Spring IOC 容器源码分析(一)

Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器。既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文并不能让你成为 Spring...

小码哥的freestyle
04/05
0
0
给spring-boot测试提供unitils支持的开源项目

一、unitils测试框架优缺点介绍 在unitils的支持下,xml配置的spring项目在测试时,有如下好处: 1、利用注解@DataSet、@ExpectedDataSet来准备数据和校验结果数据,每次运行测试用例不用重新...

yangjianzhou
2018/07/09
0
0
深入理解Spring源码(一)-IOC容器的定位,载入,注册

前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰。下面开始正题 1.Spring...

Meet相识_bfa5
2018/05/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Redox OS 发布 0.5 版

Redox OS 是一个几乎完全以 Rust 语言编写的通用操作系统及周围生态(例如文件系统、显示服务器及 Rust 版本的 libc)。其遵循微内核架构,在一定程度上兼容于 POSIX。 该项目于日前发布了 ...

linuxCool
55分钟前
2
0
更新上传git/gitee项目时出现密码配置错误(incorrect username or password)的解决办法

1.输入【git remote add origin git地址】 命令时出现incorrect username or password的错误,这是因为之前弹出输入账户和密码时输错了,而且会一直默认错误的账户和密码,因此需要把之前输入...

west_coast
今天
0
0
Jenkins基础入门-1-Jenkins简单介绍和环境安装

如果在做自动化测试的朋友,应该熟悉Jenkins,或者至少使用过。如果一个人没有使用过Jenkins或者hudson,hudson是Jenkins的前身,他还说自己做过自动化测试,只能说,他只不过是在做半自动化...

shzwork
今天
0
0
linux上解压版安装jdk,tomcat

需要的安装包 1.vmware12 2.centos7版本 3.安装完成后需要xshell来连接远程虚拟机,虚拟机保证要联网,网络畅通。 4.xftp用来向linux传输文件用,一般来说xshell和xftp配套使用 5.对应的压缩...

architect刘源源
今天
29
0
使用 spring 的 IOC 解决程序耦合

工厂模式解耦 在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时...

骚年锦时
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部