Spring IOC源码--总结梳理

Spring IOC源码--总结梳理

Scroll Down

大美女.jpg
鸽了很久了的 Spring 源码,今天花了点时间好好读一下,刚读完 IOC 相关部分,一开始是懵圈,读完后是真香。

通过阅读IOC相关源码,可以获得以下懂得以下知识点:

  1. Spring bean 的生命周期
  2. Spring 是如何解决循环依赖
  3. bean 的整个创建过程

今天先采取从上到下的阅读源码方式,把 bean 的创建过程先梳理以下,需要说明的是,本篇文章只会抽离出关键代码,不会全部贴,不然显得很杂。

准备工作

利用Spring boot 建一个 maven 工程,然后在pom文件中引入

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.11.RELEASE</version>
</dependency>

然后新建一个类,main方法写入

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application-context.xml");

还有一种方式,是直接去spring官网下载好spring源码,然后用gradle编译下就好了,看你喜欢。

入口

ClassPathXmlApplicationContext类的

public ClassPathXmlApplicationContext(
		String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
		throws BeansException {

	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		//重点是这个方法
		refresh();
	}
}

脉络

跟进refresh()方法,跳到了AbstractApplicationContext类,整个方法可以看到整个bean创建的脉络

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		//启动前的准备工作,记录下启动时间,标记下启动状态
		prepareRefresh();

		//往beanFactory里面注册解析好的bean,注意了,此时bean还没开始初始化,仅仅是拿到了一些bean的信息
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		//这里是spring可以手动注册一些bean
		prepareBeanFactory(beanFactory);

		try {
			//如果有bean实现BeanFactoryPostProcessor接口,那就执行他们的postProcessBeanFactory方法
			postProcessBeanFactory(beanFactory);

			//调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 回调方法
			invokeBeanFactoryPostProcessors(beanFactory);

			//注册BeanPostProcessors的实现类,BeanPostProcessors有两个方法,
			//1.postProcessBeforeInitialization是在bean初始化之前执行
			//2.postProcessAfterInitialization是在bean初始化之后执行
			//只是注册还没有调用
			registerBeanPostProcessors(beanFactory);

			//省略一部分代码

			//重点:初始化所有的非lazy-init的singleton beans。
			finishBeanFactoryInitialization(beanFactory);

			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// 销毁已经创建的资源,避免占用资源
			destroyBeans();

			// 修改启动状态
			cancelRefresh(ex);
			
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

这里总结下,bean创建的大体流程:

  1. 加载bean的注册信息到beanFactory中
  2. 注册下bean初始化,会调用到的BeanPostProcessor接口
  3. 执行初始化

如何加载bean的注册信息

bean的注册信息指的是

<bean id="people" name="chp" class="com.chp.People" init-method="init" destroy-method="destory">
	<constructor-arg>
		<value type="int">34</value>
	</constructor-arg>
	<property name="age" value="123"></property>
</bean>

接着往下看吧,注册调用的是AbstractApplicationContext类的obtainFreshBeanFactory方法,一起看看里面到底干了什么。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	refreshBeanFactory();
	return getBeanFactory();
}

跟进到AbstractRefreshableApplicationContext的refreshBeanFactory方法

protected final void refreshBeanFactory() throws BeansException {
	//看当前的ApplicationContext是否已经存在beanFactory,存在就销毁
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		//这个beanFactory就是ApplicationContext内部持有的工厂,看下面的参数就知道,传的都是它
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		//是否允许bean循环依赖,以及是否允许bean覆盖
		customizeBeanFactory(beanFactory);
		//重点,加载bean到BeanFactory bean以beanDefinitions形式包装
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

ApplicationContext内部持有一个BeanFactory,先简单看下customizeBeanFactory方法

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
	if (this.allowBeanDefinitionOverriding != null) {
		beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.allowCircularReferences != null) {
		beanFactory.setAllowCircularReferences(this.allowCircularReferences);
	}
}

就是设置了是否允许bean覆盖,还有是否允许循环依赖。
重点,我们继续跟进loadBeanDefinitions方法,跳到了AbstractXmlApplicationContext类

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// Configure the bean definition reader with this context's
	// resource loading environment.
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	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);
	loadBeanDefinitions(beanDefinitionReader);
}

看到关键字xml,就可以猜到接下来,应该是加载xml文件,然后读取xml文件的bean信息了。

跟进到AbstractXmlApplicationContext的loadBeanDefinitions方法

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	//加载xml文件
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		//开始解析
		reader.loadBeanDefinitions(configResources);
	}
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
}

具体怎么解析,这里就不详细说了,这里会跳着说,不会把跟的每一个方法都列出来,不然看起来又抓不住重点。

跟进loadBeanDefinitions方法后,一直走,走的流程贴个图把
加载bean的一些中间流程.png
来到DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法

protected void doRegisterBeanDefinitions(Element root) {
	
	//BeanDefinitionParserDelegate负责解析bean,ps:delegate代表的意思,beanDefinition解析代表一目了然
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent);

	if (this.delegate.isDefaultNamespace(root)) {
		//看根节点有没有profile属性
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			// We cannot use Profiles.of(...) since profile expressions are not supported
			// in XML config. See SPR-12458 for details.
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}
	//钩子方法,可以看出用的模板模式这里
	preProcessXml(root); 
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);

	this.delegate = parent;
}

跟进DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	if (delegate.isDefaultNamespace(root)) {
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element) {
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {
					//解析bean的 <import />、<alias />、<bean />、<beans /> 标签
					parseDefaultElement(ele, delegate);
				}
				else {
					//xml头有对应的 xmlns引入,解析如下非默认标签
					//<mvc />、<task />、<context />、<aop />等
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}

跟进 parseDefaultElement 方法

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

这里4个分支,对应不同的默认标签,我这里只看processBeanDefinition方法,跟进去
可以看到这里有两步

  1. 解析bean,并且把Bean包装成BeanDefinitionHolder
  2. 解析好了bean,开始注册bean,注册指的是把bean存放进一个map里面
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.
			//解析完bean,开始注册bean
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// Send registration event.
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

跟进BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
	String id = ele.getAttribute(ID_ATTRIBUTE);
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

	List<String> aliases = new ArrayList<>();
	//name属性可以有多个,拿出来当作别名
	if (StringUtils.hasLength(nameAttr)) {
		String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		aliases.addAll(Arrays.asList(nameArr));
	}
	//beanName唯一标识一个bean,一般有id就直接id,没有id,用别名的第一个做beanName
	String beanName = id;
	if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
		beanName = aliases.remove(0);
	}

	if (containingBean == null) {
		checkNameUniqueness(beanName, aliases, ele);
	}
	//这步成功创建一个 BeanDefinition 实例,意味着一个bean标签解析完毕
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		if (!StringUtils.hasText(beanName)) {
			try {
				if (containingBean != null) {
					beanName = BeanDefinitionReaderUtils.generateBeanName(
							beanDefinition, this.readerContext.getRegistry(), true);
				}
				else {
					//id和name都没有在标签出现,那么beanName就是类似 类的全路径名#1格式
					beanName = this.readerContext.generateBeanName(beanDefinition);
				
					String beanClassName = beanDefinition.getBeanClassName();
					if (beanClassName != null &&
							beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
							!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
						aliases.add(beanClassName);
					}
				}
			}
			catch (Exception ex) {
				error(ex.getMessage(), ele);
				return null;
			}
		}
		String[] aliasesArray = StringUtils.toStringArray(aliases);
		//包装成BeanDefinitionHolder,holder可以拿到beanDefinition,beanName, aliasesArray信息
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}

	return null;
}

可以看到最后是包装成了BeanDefinitionHolder,holder可以拿到beanDefinition,beanName, aliasesArray信息

这里看完,我们再看是怎么注册bean的,别跟丢了,这里对应的是DefaultBeanDefinitionDocumentReader类的processBeanDefinition方法里面的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
跟进去看下

public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {

	// Register bean definition under primary name.
	String beanName = definitionHolder.getBeanName();
	//beanName和beanDefinition弄成映射,存放进beanDefinitionMap中
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// Register aliases for bean name, if any.
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			//根据beanName也能获取到全部别名
			registry.registerAlias(beanName, alias);
		}
	}
}

看到这里,相信大家都能知道个大概了,注册bean最终是有一个map存放的beanName和BeanDefinition的映射

休息下,我们要转到脉络里面的bean初始化过程了

bean的初始化

未完待续......