Spring IOC容器初始化过程 源码分析

本文主要记录Spring容器创建 源码分析过程。

首先贴上一张时序图,好久没画,忘的差不多了,画的不好,可以凑合看一下。
《Spring IOC容器初始化过程 源码分析》

接下来,贴上一份测试代码,这里使用AnnotationConfigApplicationContext来初始化Spring容器

	@Test
	public void test1() {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
		System.out.println("spring容器初始化结束");
		Person person = (Person) ctx.getBean("person");
		System.out.println(person.toString());
	}
@Configuration
public class AppConfig {

	@Bean(value="person")
	public Person getPerson() {
		Person person = new Person();
		person.setName("zhangsan");
		return person;
	}
}
public class Person {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + "]";
	}
}

代码看完了,直接进入正题,开始Debug。

Debug从AnnotationConfigApplicationContext的构造器方法开始,找到该构造方法。
《Spring IOC容器初始化过程 源码分析》
往下继续走,进入到AbstractApplicationContext的refresh()方法,Spring容器的初始化过程就在该方法中完成的。
本文会进入每个方法,看看方法里面的代码,但可能不会很细。
《Spring IOC容器初始化过程 源码分析》
《Spring IOC容器初始化过程 源码分析》

1、接下来,继续Debug,进入第一个方法prepareRefresh(),该方法主要是进行刷新前的预处理操作。

《Spring IOC容器初始化过程 源码分析》

2、进入第二个方法ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(),该方法主要是获取beanFactory。

《Spring IOC容器初始化过程 源码分析》
进入refreshBeanFactory(),会跳到GenericApplicationContext的refreshBeanFactory()方法。这里讲一下
GenericApplicationContext有个构造方法,会new一个新的DefaultListableBeanFactory。
《Spring IOC容器初始化过程 源码分析》
而进入refreshBeanFactory()先当于new一个DefaultListableBeanFactory,并设置一个序列化值。
《Spring IOC容器初始化过程 源码分析》

返回接在再看getBeanFactory()方法,跳到GenericApplicationContext的getBeanFactory(),返回上一步创建的DefaultListableBeanFactory
《Spring IOC容器初始化过程 源码分析》
最后将该beanFactory(DefaultListableBeanFactory)返回。

3、接着进入第三个方法prepareBeanFactory(beanFactory),该方法主要是beanFactory的预准备工作,也就是对beanFactory进行一些初始化后的设置;
1)、设置BeanFactory的类加载器、支持表达式解析器…
2)、添加部分BeanPostProcessor(ApplicationContextAwareProcessor)
3)、设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware等;
4)、注册可以解析的自动装配,我们能直接在任何组件中自动注入:
BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
5)、添加BeanPostProcessor【ApplicationListenerDetector】
6)、添加编译时的AspectJ;
7)、往BeanFactory中注册组件;environment、 systemProperties、systemEnvironment。

4、接着往下走,进入第4个方法postProcessBeanFactory(beanFactory),发现该方法是交给子类重写的,子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}

——————————–以上是BeanFactory的创建及预准备工作—————————–

5、继续进入第5个方法invokeBeanFactoryPostProcessors(beanFactory),该方法主要是调用beanFactory的后置处理器,在BeanFactory标准初始化之后执行的。这里我们主要跟踪一下AppConfig.class中person bean的生成。看下图,继续进入跟踪

《Spring IOC容器初始化过程 源码分析》
在processConfigBeanDefinitions(BeanDefinitionRegistry registry)中找到下图代码,该代码就是加载person bean定义的。往后就不继续了。
《Spring IOC容器初始化过程 源码分析》
执行完这句代码,可以看一下beanFactory中的beanDefinitionMap属性,已经多了一个person bean 定义
《Spring IOC容器初始化过程 源码分析》
考虑到篇幅,以下几个步骤就不展示代码,只说一下方法里面的操作。
6、registerBeanPostProcessors(beanFactory)。该方法主要往beanFactory注册Bean的后置处理器。
不同接口类型的BeanPostProcessor,在Bean创建前后的执行时机是不一样的。有以下几个步骤:
一、获取所有的 BeanPostProcessor;后置处理器都默认可以通过PriorityOrdered、Ordered接口来执行优先级
二、先注册PriorityOrdered优先级接口的BeanPostProcessor;
把每一个BeanPostProcessor;添加到BeanFactory中
beanFactory.addBeanPostProcessor(postProcessor);
三、再注册Ordered接口的
四、 最后注册没有实现任何优先级接口的
五、最终注册MergedBeanDefinitionPostProcessor;
六、注册一个ApplicationListenerDetector;来在Bean创建完成后检查是否是ApplicationListener,如果是
applicationContext.addApplicationListener((ApplicationListener<?>) bean);
7、initMessageSource()。该方法主要初始化MessageSource组件(国际化功能;消息绑定,消息解析)。有以下几个步骤:
一、 获取BeanFactory
二、看容器中是否有id为messageSource的,类型是MessageSource的组件
如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
三、 把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);
8、initApplicationEventMulticaster();该方法主要初始化事件派发器;有以下几个步骤:
一、获取BeanFactory
二、 从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;
三、如果上一步没有配置;创建一个SimpleApplicationEventMulticaster
四、 将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入
9、onRefresh();该方法主要在容器刷新的时候可以自定义逻辑,留给子类实现。
10、registerListeners();该方法主要在容器中将所有项目里面的ApplicationListener注册进来。有以下几个步骤:
一、从容器中拿到所有的ApplicationListener
二、 将每个监听器添加到事件派发器中;
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
三、派发之前步骤产生的事件;
11 、接下来重点看一下这个方法finishBeanFactoryInitialization(beanFactory);该方法作用是实例化所有剩下的懒加载单实例。
《Spring IOC容器初始化过程 源码分析》
重点看一下下面这段代码

	@Override
	public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		// 获取容器中的所有Bean定义名字
		List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			1.先获取bean定义
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			2.判断Bean是不是抽象的,是不是单实例的,是不是懒加载
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				 判断是否是FactoryBean;
				if (isFactoryBean(beanName)) {
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
					是否是实现FactoryBean接口的Bean
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							@Override
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {
				    //我们重点看一下这个方法。不是工厂bean,通过该方法获取创建bean实例
					getBean(beanName);
				}
			}
		}
		xxxxxx

当循环的beanName 为person的时候,我们debug进这个getBean(beanName)方法。
《Spring IOC容器初始化过程 源码分析》
继续debug,进入方法。因为person实例还没有创建,beanFactory中的singletonObjects肯定找不到。一些判断这里就不展示,直接跳过了。找到下面代码
《Spring IOC容器初始化过程 源码分析》
继续debug,进入createBean(beanName, mbd, args);
《Spring IOC容器初始化过程 源码分析》
找到下面代码,进入doCreateBean(beanName, mbdToUse, args);
debug了半天,终于要创建bean实例了。这里Spring容器使用BeanWrappe。BeanWrapper是对Bean的包装,大部分情况下是在spring ioc内部进行使用,用来设置获取被包装的bean对象,获取被包装bean的属性描述器等。
《Spring IOC容器初始化过程 源码分析》
Debug进入createBeanInstance(beanName, mbd, args);
《Spring IOC容器初始化过程 源码分析》
进入debug,进入。通过工厂方法来实例化
《Spring IOC容器初始化过程 源码分析》
继续进入,找到下面代码。beanFactory通过实例化策略来实例化bean
《Spring IOC容器初始化过程 源码分析》
继续debug,进入。找到图中的代码,箭头标记的方法,就是最后生成bean的方法,通过工作方法调用生成实例bean。
《Spring IOC容器初始化过程 源码分析》

执行完上图箭头代码factoryMethod.invoke(factoryBean, args),bean实例已经生成了,控制台会打印下面这句话。
《Spring IOC容器初始化过程 源码分析》
一路返回,返回到
AbstractAutowireCapableBeanFactory的 doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)方法。想方便点的话,断点直接打到箭头标记的那行,就不需要一路return回去了。这时候可以看到实例bean的信息。
《Spring IOC容器初始化过程 源码分析》
往下继续走,找到下图的代码,箭头中的方法是对实例bean进行赋值。里面有赋值之前的一些后置处理器的处理和利用setter方法等为属性进行赋值操作。这里简单介绍下。
《Spring IOC容器初始化过程 源码分析》
往下看到initializeBean(beanName, exposedObject, mbd)方法。在该方法中进行bean的初始化。里面也是一些后置处理器的操作。
然后,继续返回出去,来到AbstractBeanFactory的doGetBean方法。
《Spring IOC容器初始化过程 源码分析》
这个方法上面见过了,之前只对图中的createBean(beanName, mbd, args)进行分析。现在我们看一下getSingleton(…)这个方法。这个方法也简单,就是从beanFactory的singletonObjects(Map)属性中获取该beanName的值,如果存在,则返回;否则,将该beanName和对应的bean实例添加到singletonObjects属性中。
《Spring IOC容器初始化过程 源码分析》
《Spring IOC容器初始化过程 源码分析》

12、最后看一下refresh() 方法里面的最后一个方法finishRefresh()。该方法主要完成BeanFactory的初始化创建工作。这样IOC容器就创建完成了。

《Spring IOC容器初始化过程 源码分析》

经过上面n多步骤,spring ioc容器初始化过程就结束了

Person person = (Person) ctx.getBean("person");

接下来这个获取person bean就简单了。它的原理就是从beanFactory的singletonObjects(map)中,通过key,获取value了。源码上面也看过了。

学习源码的过程是枯燥的,由于是全英文,可能看着也比较累,但是学习完,感觉收获还是颇多了。由于这个初始化过程步骤比较多,涉及到的东西也比较多,这里看一下,那里看一下,多个类之间切来切去,可能看着也会比较乱一点。但是多看几遍,相信大家也会对这个过程比较了解了。

个人总结:从上面几个步骤可以看出,IOC容器初始化的过程,大部分都是往ioc容器的诸多Map中添加值,方便后续读取使用。读取的时候只需要从Map中根据key取值就可以了。

到此,本篇文章也就结束了。该篇文章主要是记录本人学习spring ioc容器源码的一个过程。中间可能存在一些问题,或有一些不够严谨完善的地方,希望大家体谅体谅。有问题的话,欢迎大家留意,交流交流。

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