Spring源码剖析-IOC启动流程

Posted 墨家巨子@俏如来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码剖析-IOC启动流程相关的知识,希望对你有一定的参考价值。

前言

上一章节我们讲的是Spirng中的一些核心类,包括IOC容器工厂,和配置解析的一些类,这一章主要是跟一下IOC启动流程。这里我先贴一下IOC的启动部分流程图,在后面的源码分析就是在走这张图,为什么是部分流程图,因为我先分多篇文章来写IOC启动流程,太长了看起来费劲。我把IOC启动流程分为4个阶段:容器创建 -> 配置加载 -> Bean的解析 -> Bean的注册,如下:
在这里插入图片描述

ClasspathXmlApplicationContext 容器

源码分析入口从 ClasspathXmlApplicationContext 开始,通过它来加载一个配置

//加载Spring配置文件,拿到Spring容器
ApplicationContext context = new  ClassPathXmlApplicationContext("配置文件.xml")
//从容器中拿到对象实例
MyBean myBean = context.getBean(MyBean.class);

进入ClassPathXmlApplicationContext构造器可以看到,该构造器接收一个配置文件,构造器的注释是这一样描述的:创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义并自动刷新上下文。

/**
	创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义并自动刷新上下文。
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * @param configLocation resource location
	 * @throws BeansException if context creation failed
	 */
	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

	/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		//调用父类的构造器
		super(parent);
		//设置位置文件地址
		setConfigLocations(configLocations);
		if (refresh) {
			//刷新容器【重点】
			refresh();
		}
	}

在ClasspathXmlApplication的构造器中做了如下事情:

  • 调用了父容器的构造器方法,目的是加载设置Bean的资源加载器 ResourcePatternResolver
  • 然后通过setConfigLocations方法保存好配置文件地址,
  • 最后调用refresh()刷新容器

ResourcePatternResolver 资源加载器

ResourcePatternResolver是Bean的资源加载器 ,通过父容器 AbstractApplicationContext 中的构造方法创建:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext

	public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		//加载 resourcePatternResolver 
		this();
		//
		setParent(parent);
	}
	
	/**
	 * Create a new AbstractApplicationContext with no parent.
	 */
	 //创建一个AbstractApplicationContext容器工厂,并构建一个ResourcePatternResolver
	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}
	//获取 PathMatchingResourcePatternResolver
	protected ResourcePatternResolver getResourcePatternResolver() {
		return new PathMatchingResourcePatternResolver(this);
	}
	
	public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		//资源加载器
		this.resourceLoader = resourceLoader;
	}
	

父容器AbstractApplicationContext 继承了 DefaultResourceLoader ,拥有资源加载的能力,在构造器中中创建了ResourcePatternResolver,使用的是PathMatchingResourcePatternResolver作为实现,它能够将指定的资源位置路径解析为一个或多个匹配的资源。
在这里插入图片描述
下面是ResourceLoader 源码:

public interface ResourceLoader {
	//默认从classpath中加载资源文件
	/** Pseudo URL prefix for loading from the class path: "classpath:". */
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
	//把资源文件转换成Resource
	Resource getResource(String location);
	ClassLoader getClassLoader();
}

public interface ResourcePatternResolver extends ResourceLoader {

	//从classpath加载资源
	String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

	//把文件转换成Resource[] ,对ResourceLoader做了扩展
	Resource[] getResources(String locationPattern) throws IOException;
}

setConfigLocations 保存配置地址

然后就是保存配置地址 ,从源码可以看出,我们是可以传入多个配置文件给容器的。

public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
		implements BeanNameAware, InitializingBean {
	//地址保存到这里
	@Nullable
	private String[] configLocations;
	
	/**
	 * Set the config locations for this application context.
	 * <p>If not set, the implementation may use a default as appropriate.
	 */
 	//可以传入多个配置
	public void setConfigLocations(@Nullable 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;
		}
	}

Refresh() 刷新容器

ClasspathXmlApplication调用 AbstractApplicationContext#refresh 方法刷新容器,该方法中实现了IOC容器的整个初始化过程。

@Override
   public void refresh() throws BeansException, IllegalStateException {
   	synchronized (this.startupShutdownMonitor) {
   		// Prepare this context for refreshing.
   		//准备刷新工作 ,记录开始时间,初始化属性,校验配置文件,准备事件的存储Set
   		prepareRefresh();

   		// Tell the subclass to refresh the internal bean factory.
   		//告诉子类,刷新Bean工厂,销毁旧beanFactory,创建新beanFactory,默认DefaultListableBeanFactory
   		//从子容器的refreshBeanFactory方法中载入Bean的资源文件
   		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

   		// Prepare the bean factory for use in this context.
   		//准备工厂,配置工厂的下文特性, 例如上下文的 ClassLoader 和后处理器。Bean表达式解析器,
   		//BeanPostProcessor和 Aware类的自动装配等
   		prepareBeanFactory(beanFactory);

   		try {
   			// Allows post-processing of the bean factory in context subclasses.
   			//BeanFactory初始化完成的后置工作,这是一个空方法,留给三方框架或者自己配置,作用是允许对beanFoctory进行扩展处理
   			postProcessBeanFactory(beanFactory);

   			// Invoke factory processors registered as beans in the context.
   			//调用BeanFactory的后置处理器BeanFactoryPostProcessor,在 bean定义注册之后bean实例化之前调用
   			invokeBeanFactoryPostProcessors(beanFactory);

   			// Register bean processors that intercept bean creation.
   			//注册Bean的后置处理器BeanPostProcessor,在Bean初始化前,后执行
   			registerBeanPostProcessors(beanFactory);

   			// Initialize message source for this context.
   			//初始化信息源,国际化相关
   			initMessageSource();

   			// Initialize event multicaster for this context.
   			//初始化容器事件传播器
   			initApplicationEventMulticaster();

   			// Initialize other special beans in specific context subclasses.
   			//空方法,该方法子类实现,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
   			onRefresh();

   			// Check for listener beans and register them.
   			//注册事件监听器,注册实现了ApplicationListener接口的监听器bean,
   			//这些监听器是注册到ApplicationEventMulticaster中的
   			registerListeners();

   			// Instantiate all remaining (non-lazy-init) singletons.
   			//实例化所有剩余的(非延迟初始化)单例的Bean
   			finishBeanFactoryInitialization(beanFactory);

   			// Last step: publish corresponding event.
   			//完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
   			finishRefresh();
   		}

   		catch (BeansException ex) {
   			if (logger.isWarnEnabled()) {
   				logger.warn("Exception encountered during context initialization - " +
   						"cancelling refresh attempt: " + ex);
   			}
   			// Destroy already created singletons to avoid dangling resources.
   			//销毁已经创建的单例Bean。
   			destroyBeans();
   			// Reset 'active' flag.
   			//取消容器刷新
   			cancelRefresh(ex);
   			// Propagate exception to caller.
   			throw ex;
   		}
   		finally {
   			// Reset common introspection caches in Spring's core, since we
   			// might not ever need metadata for singleton beans anymore...
   			//重置缓存
   			resetCommonCaches();
   		}
   	}
   }

refresh()方法主要是通过子类 refreshBeanFactory()方法加载Bean信息,然后就是一些列的容器生命周期事件。这里其实是用到了模板设计模式,在refresh()方法中指定容器刷新流程,很多的细节步骤由子类去实现。

工厂的创建:obtainFreshBeanFactory

	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//刷新工厂,有子类实现
		refreshBeanFactory();
		//通过子类返回工厂,默认 DefaultListableBeanFactory
		return getBeanFactory();
	}

这里只是定义了抽象方法,refreshBeanFactory由子类实现,见:AbstractRefreshableApplicationContext#refreshBeanFactory

AbstractRefreshableApplicationContext#refreshBeanFactory

@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			//如果已经有BeanFactory,销毁Bean,关闭容器
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//创建IOC容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//定制BeanFactory,如设置启动参数,开启注解的自动装配等
			customizeBeanFactory(beanFactory);
			//载入Bean,由子类实现
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

该方法中先判断如果已经存在BeanFactory就销毁掉重新创建,默认使用的是DefaultListableBeanFactory作为BeanFactory,并loadBeanDefinitions方法加载Bean,方法由子类 AbstractXmlApplicationContext#loadBeanDefinitions实现。

加载Bean:AbstractXmlApplicationContext#loadBeanDefinitions

@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		//创建 XmlBeanDefinitionReader ,用来从XML中读取Bean
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		//把Environment 和 ResourceLoader 设置给beanDefinitionReader
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//设置Sax解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		//初始化Bean的读取器,启用 Xml 的校验机制 , 允许子类自定义初始化读取器
		initBeanDefinitionReader(beanDefinitionReader);
		//加载Bean, XmlBeanDefinitionReader真正实现加载逻辑
		loadBeanDefinitions(beanDefinitionReader);
	}

loadBeanDefinitions方法是用来加载Bean的,创建了XmlBeanDefinitionReader 基于XML的Bean的读取器,最终会调用 XmlBeanDefinitionReader.(configLocations)从配置中加载Bean,见:AbstractXmlApplicationContext#loadBeanDefinitions(.XmlBeanDefinitionReader)

	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//获取配置的Resource
		Resource[] configResources = getConfigResources();
		//如果有Resrouce就调用 XmlBeanDefinitionReader.loadBeanDefinitions 加载Bean
		if (configResources != null) {
			//Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 配置资源
			reader.loadBeanDefinitions(configResources);
		}
		// 如果子类中获取的 Bean 配置资源Resource为空,
		// 则获取 ClassPathXmlApplicationContext  构造方法中 setConfigLocations 方法设置的资源
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 配置资源
			reader.loadBeanDefinitions(configLocations);
		}
	}

这里先尝试获取配置资源Resource,如果为空就通过指定配置reader.loadBeanDefinitions(configLocations);加载Bean,最终调用父类:AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)方法

AbstractBeanDefinitionReader#loadBeanDefinitions(

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//资源加载器,在初始化IOC容器的时候创建的
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				//把指定位置的配置文件解析成Resource,加载多个
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//委派 XmlBeanDefinitionReader加载Bean
				int count = loadBeanDefinitions(resources);
				if (actualResources != null) {
					Collections.addAll(actualResources, resources);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
				}
				return count;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			//把指定位置的配置文件解析成Resource,加载单个
			Resource resource = resourceLoader.getResource(location);
			//委派 XmlBeanDefinitionReader加载Bean
			int count = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
			}
			return count;
		}
	}

在该方法中显示得到初始化容器时创建的ResourceLoader,通过ResourceLoader.getResource(location)得到Resource资源对象后,调用loadBeanDefinitions(resources);方法,其实是委派XmlBeanDefinitionReader去加载Bean。程序最终来到XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)方法

解析Bean:XmlBeanDefinitionReader#loadBeanDefinitions

//对 Resource进行了编码
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
	

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}
		//当前正在加载的资源
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			//从Resource中得到输入流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				//从输入流中得到XML配置文件源
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//加载BeanDefinitions , 加载Bean的核心方法
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				//输入流关闭
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			//删除Resource
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}

XmlBeanDefinitionReader#loadBeanDefinitions 对配置文件做了编码处理后,从Resource中得到输入流,然后包装成 InputSource(XML数据源),调用 doLoadBeanDefinitions方法去加载Bean,见:XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			//将 XML 文件转换为 Document 对象,通过 documentLoader来解析
			Document doc = doLoadDocument(inputSource, resource);
			//【重要】解析和注册Bean的消息流程
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		...省略...

XmlBeanDefinitionReader 通过 DefaultDocumentLoader#loadDocument 把InputResource转成Document对象,然后委派 BeanDefinitionParserDelegate 去解析Document然后注册Bean。

文章就先到这里结束把,下一章接上Bean的解析和Bean的注册,如果喜欢就给个好评吧,你的肯定是我最大的动力~ 文章数量突破100啦~

以上是关于Spring源码剖析-IOC启动流程的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码剖析-基于注解的IOC启动流程

Spring源码剖析-基于注解的IOC启动流程

Spring源码剖析1:初探Spring IOC核心流程

Spring IOC源码剖析:Spring IOC容器初始化主体流程

Spring IOC源码剖析:BeanFactory创建流程及Bean创建流程

深入浅出Spring原理及实战「原理分析专题」不看源码就带你剖析IOC容器核心流程以及运作原理