Spring源码分析之IOC容器创建过程

我们知道,Spring框架基于IOC容器能够实现依赖注入,使代码之间不在具有高度的耦合关系.解耦给问我们带来很多的好处,不仅会使我们的代码更加容易扩展维护,同时也更加方便测试。

在Spring中,我们常使用ClassPathXmlApplicationContext(类路径下读取配置),XmlWebApplicationContext(web环境下加载配置),AnnotationConfigApplicationContext(基于注解的配置读取),FileSystemXmlApplicationContext(基于文件系统中的配置读取)。而本文,将以ClassPathXmlApplicationContext为切入点来进行分析。整个入口也很简单,只有一行代码,如下:

  ClassPathXmlApplicationContext bb = new ClassPathXmlApplicationContext("beanConf.xml");

读取beanConf.xml配置文件,从而持有IOC容器,我们来看看Spring底层做了一些什么。

public ClassPathXmlApplicationContext(String configLocation)  throws BeansException {
		this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  throws BeansException {
		//调用父类的构造器 获得资源加载器
		super(parent);   
		//对资源文件的定位
		setConfigLocations(configLocations);  
		if (refresh) {
		    //容器的刷新 ,核心所在
			refresh();    
		}
  }

在super(parent)中,实际调用的为AbstractApplicationContext的构造方法。

public AbstractApplicationContext() {
        //获取资源加载器
	this.resourcePatternResolver = getResourcePatternResolver();  
}
protected ResourcePatternResolver getResourcePatternResolver() {
        //生成一个加载器
	return new PathMatchingResourcePatternResolver(this);
}
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		this.resourceLoader = resourceLoader;
}

而setConfigLocations(configLocations)中,设置配置资源。

public void setConfigLocation(String location) {
       多个配置文件可以以,;以及\t\n分割开。
       setConfigLocations(StringUtils.tokenizeToSringArray(location, CONFIG_LOCATION_DELIMITERS));
}
public void setConfigLocations(String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
			    //获取配置文件路径并保存
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
	}
}

在此方法中 ,我们可以看到多个配置文件可以用,;以及\t\n分割,同时配置文件也可以以数组的方式传入:new String[] {configLocation}。

下面再来看看refresh容器的刷新做了什么。

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
		
		//一:为容器的刷新做准备,
		//1:设置容器的启动时间,同步标志。
		//2:initPropertySources :初始化属性设置,子类来自定义实现。
		//3:validateRequiredProperties:属性的校验
		//4:this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); 早期事件的初始化。
		prepareRefresh();
			
			
		//二:根据配置获取刷新的BeanFactory。此方法主要涉及到对配置文件的 定位,解析以及注册。
		//1:定位包括从类路径,文件系统,url完成资源的定位。
		//2: 解析主要为对XML文件的解析,由配置文件解析为BeanDefinitions
		//3: 注册即把对应bean的信息存在在map中。
		// 这一步也BeanFactory初始化的过程,此方法完成后则对应的bean的信息都保存在BeanDefinitions中,bean实例化的时候就是基于解析出的bean的定义信息来完成的.此步骤也是很关键的,将在下一篇中详细进行描述。
		[Spring源码分析之BeanFactory初始化](https://blog.csdn.net/qq_28051453/article/details/84946397)
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			
			
		//三:BeanFactory的配置工作。
		//1:设置类加载器,表达式解析器。
		//2:添加部分beanPostProcessor等等 
		prepareBeanFactory(beanFactory);

		try {
			
			//四:自定义后置BeanFactory的配置。子类重写的此方法可以自定义一些配置的装载,个性化的设置。
			postProcessBeanFactory(beanFactory);
				

			//五:执行BeanFactoryPostProcessors(BeanFactory的后置处理器)
			//1:执行BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的方法。这里应该注意的是BeanDefinitionRegistryPostProcessor继承于BeanFactoryPostProcessor。执行的顺序为先执行BeanDefinitionRegistryPostProcessor中的方法,后执行BeanFactoryPostProcessor。
			//2:具体执行的顺序为:先执行PriorityOrdered类型的,再执行Ordered类型的,最后执行其他的。
			//3:自定义bean实现BeanFactoryPostProcessor或者BeanDefinitionRegistryPostProcessor接口,实现其方法,则可以对bean工厂进行操作。
			invokeBeanFactoryPostProcessors(beanFactory);
				

			//六:注册Bean的后置处理器 监听容器所触发的相关事件
			registerBeanPostProcessors(beanFactory);

			//七:初始化MessageSource相关的组件(国际化,消息的绑定与解析的功能)
			//1:获取beanFactory,判断容器中是否有MessageSource组件,没有的话则创建一个并注册到容器中。
			initMessageSource();
                     
                     
			//八:初始化事件的派发器。
			//获取beanFactory,得到applicationEventMulticaster,将创建好的注册到容器中。
			initApplicationEventMulticaster();


			//九:容器的刷新,留给子类自定义的实现。
			onRefresh();


			//十:注册事件的相关监听器
			//1:拿到容器中所有的ApplicationListeners,为每个事件添加监听器。
			registerListeners();


			//十一:初始化所有非懒加载的单例bean,核心。这个方法即是bean的实例化过程。包括读取bean的定义信息,获取bean的依赖关系。实例化bean以及设置。这个方法也将在下篇中详细的描述。
			//1:根据bean的定义信息(BeanDefinitions)实例化bean.
			//2:populateBean方法给bean的属性赋值。
			//3:初始化bean(initializeBean)
			     //3.1:执行Aware接口的相关方法
			     //3.2:执行后置处理器初始化前的方法:applyBeanPostProcessorsBeforeInitialization
			     //3.3:执行初始化方法:invokeInitMethods
			     //3.4:执行后置处理器初始化后的方法:applyBeanPostProcessorsAfterInitialization
			//4:注册bean的销毁方法,注意,这里只是注册。
			//5:单例的bean添加至缓存中,下次再次获取则直接从缓存中获取,从而保证是同一个对象。
			finishBeanFactoryInitialization(beanFactory);


			//十一:完成beanFactory的创建工作,IOC容器创建完成。
			//1:初始化容器生命周期相关的后置处理器,发布容器初始化完成的时间。
			finishRefresh();
				
		}
		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			//十二:销毁单实例bean
			destroyBeans();

			//十三:重置容器同步的标识
			cancelRefresh(ex);
			throw ex;
		}
		finally {
			resetCommonCaches();
		}
	}
}

至此,整个IOC容器创建完成。通过以上分析,总结可知,IOC容器其实就相当于一个一个的Map,Map中保存了相关的bean的定义信息以及实例化信息。对于单实例的bean而言,首次获取的时候总是从缓存中获取,没有则再创建,并加载到缓存中。默认的单实例在容器启动的时候创建,在容器关闭的时候销毁。

以下是相关其他Spring源码分析文章
Spring源码分析之BeanFactory初始化

    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/qq_28051453/article/details/84800672
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞