准备

Person实例

@Data
public class Person {
private String name;
private int age;
}

xml bean配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="person" class="com.gcdd1993.spring.framework.base.domain.Person"/>
</beans>

入口

AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("config.xml");
applicationContext.getBean("person");

使用Debug进入ClassPathXmlApplicationContext构造函数,源码如下

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

super(parent)

一步步向上调用父类构造函数,路径为

ClassPathXmlApplicationContext -> AbstractXmlApplicationContext -> AbstractRefreshableConfigApplicationContext -> AbstractRefreshableApplicationContext -> AbstractApplicationContext

历经整个继承体系,最终到达AbstractApplicationContext:

public AbstractApplicationContext(ApplicationContext parent) {
this();
setParent(parent);
}

最后会设置当前ApplicationContext的父级ApplicationContext

setConfigLocations(configLocations)

设置配置文件路径,解析的细节参照官方文档Resource一节,不是本文讨论的重点,在此略过。

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;
}
}

refresh()

此方法是Spring容器的核心方法,源码(精简了try catch部分)如下:

public void refresh() throws BeansException, IllegalStateException {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); // Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
}

此处可以看到Spring编码方式近似于流程图的,重点部分都抽出为了单独的方法,流程清晰,易于理解。我们一步步看:

prepareRefresh()

上下文刷新前预热

protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true); if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
} // Initialize any placeholder property sources in the context environment
initPropertySources(); // Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
  1. 设置上下文基本信息,如startupDate(启动时刻)、closed(是否关闭)、active(是否存活)等等。
  2. 解析占位符资源,并验证标记为required的资源是否可用

obtainFreshBeanFactory()

初始化beanFactory(bean工厂,实际存放bean的就是它了)

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

核心方法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);
}
}
  1. createBeanFactory();
  2. 设置beanFactory属性
  3. loadBeanDefinitions(beanFactory);

loadBeanDefinitions(beanFactory)

解析bean定义,有几个bean就有几个BeanDefinition。注意,Spring并不是拿到配置就直接用反射实例化bean,而是先将bean配置解析为BeanDefinition。

BeanDefinition保存了实例化bean需要的一切信息,包括属性,依赖等。以ConcurrentHashMap<String, BeanDefinition>保存在DefaultListableBeanFactory的beanDefinitionMap里。

prepareBeanFactory(beanFactory)

设置beanFactory的其余属性

postProcessBeanFactory(beanFactory)

空实现,给子类一个机会,自定义beanFactory后置处理器

BeanFactoryPostProcessor定义:

public interface BeanFactoryPostProcessor {

    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

invokeBeanFactoryPostProcessors(beanFactory)

执行上一步中的beanFactory后置处理器的回调方法void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

registerBeanPostProcessors(beanFactory)

注册bean后置处理器,实现bean初始化前后的自定义逻辑

BeanPostProcessor定义:

public interface BeanPostProcessor {
// 在bean实例化前调用
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// 在bean实例化后调用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

initMessageSource()

注册国际化相关bean

initApplicationEventMulticaster()

初始化Spring事件发布相关bean

onRefresh()

空实现,给子类一个机会,初始化特殊bean

registerListeners()

注册监听器

finishBeanFactoryInitialization(beanFactory)

实例化所有非懒加载的bean

直到这里,才开始真正实例化bean

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 1. 实例化bean的类型转换器
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));
} // 2. 实例化属性占位符解析器
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
return getEnvironment().resolvePlaceholders(strVal);
}
});
} // 3. 实例化LoadTimeWeaverAware
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
} // 4. 停止使用临时ClassLoader进行类型匹配
beanFactory.setTempClassLoader(null); // 5. 禁止再修改bean定义
beanFactory.freezeConfiguration(); // 6. 实例化所有非懒加载单例bean
beanFactory.preInstantiateSingletons();
}

preInstantiateSingletons()

  1. 根据每一个bean定义,实例化bean
  2. 为每一个实现SmartInitializingSingleton的bean执行回调方法

实例化bean部分的代码:

for (String beanName : beanNames) {
// 获取bean定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 只有不是abstract、单例且不是懒加载的bean才在这里实例化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 如果是FactoryBean
if (isFactoryBean(beanName)) {
// 先实例化实例对应的FactoryBean
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
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) {
// 使用FactoryBean的getObject()方法返回真正的实例
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}

getBean(String name)

该方法调用了一个doGetBean,doGetBean代码较长,而且有部分代码是为了解决并发场景下单例的生成,我们挑出重点的看:

  1. 从父BeanFactory检查是否存在该bean的定义,如果存在,委托父BeanFactory来实例化
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
  1. 获得bean定义,如果存在依赖,先实例化每一个依赖bean,注意:不允许循环依赖
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
//如果存在依赖,先实例化每一个依赖bean
if (dependsOn != null) {
// 实例化每一个依赖bean
for (String dep : dependsOn) {
// 检查循环依赖
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 实例化依赖bean
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
  1. 实例化bean

方法调用流程:

createBean > doCreateBean > populateBean

其中doCreateBean:

  1. 从BeanDefinition生成BeanWrapper
  2. 将BeanWrapper和BeanDefinition.getPropertyValues() 传给populateBean,实例化bean

finishRefresh()

protected void finishRefresh() {
// 初始化生命周期处理器
initLifecycleProcessor(); // 刷新生命周期处理器状态 running = true
getLifecycleProcessor().onRefresh(); // 发布上下文初始化完成事件ContextRefreshedEvent
publishEvent(new ContextRefreshedEvent(this)); // 如果处于活动状态,将自己注册到LiveBeans
LiveBeansView.registerApplicationContext(this);
}

总结

Spring IoC Container时序图

Spring IoC Container源码分析(二)-bean初始化流程的更多相关文章

  1. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  2. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  3. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  4. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  5. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

  6. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  7. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  8. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

  9. 最简 Spring IOC 容器源码分析

    前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...

随机推荐

  1. Speech Super Resolution Generative Adversarial Network

    博客作者:凌逆战 博客地址:https://www.cnblogs.com/LXP-Never/p/10874993.html 论文作者:Sefik Emre Eskimez , Kazuhito K ...

  2. MySQL中遍历查询结果的常用API(c)

    本中所使用的table: MySQL中的错误处理函数 unsigned int mysql_errno(MYSQL *mysql) const char *mysql_error(MYSQL *mys ...

  3. 数据清洗:按照进行数据清洗,并将清洗后的数据导入hive数据库中。

    虚拟机: hadoop:3.2.0 hive:3.1.2 win10: eclipse 两阶段数据清洗: (1)第一阶段:把需要的信息从原始日志中提取出来 ip:    199.30.25.88 ti ...

  4. MySQL优化---主从复制

    主机所有写的数据都会生成二进制SQL日志执行文件,从机只需要将SQL日志执行文件获取到,然后进行数据同步即可 一.MySQL环境搭建(一主一从相同操作) MySQL-master:192.168.33 ...

  5. Golang的sync.WaitGroup 实现逻辑和源码解析

    在Golang中,WaitGroup主要用来做go Routine的等待,当启动多个go程序,通过waitgroup可以等待所有go程序结束后再执行后面的代码逻辑,比如: func Main() { ...

  6. [redis读书笔记] 第一部分 数据结构与对象 链表

    二 链表 1.链表节点使用ListNode结构,是一个双向的链表,同时,还实现了一个控制所有ListNode的结构list: typedef struct listNode { // 前置节点 str ...

  7. sql 忘记密码 解决方法(window cmd命令解决)

    cd wamp\bin\mysql\mysql5.6.17\bin mysqld --skip-grant-tables

  8. spark 性能优化 数据倾斜 故障排除

    版本:V2.0 第一章       Spark 性能调优 1.1      常规性能调优 1.1.1   常规性能调优一:最优资源配置 Spark性能调优的第一步,就是为任务分配更多的资源,在一定范围 ...

  9. 设置允许外网访问MySQL

    1:设置mysql的配置文件     /etc/mysql/my.cnf      找到 bind-address  =127.0.0.1  将其注释掉://作用是使得不再只允许本地访问:  重启my ...

  10. Nginx之常用基本配置

    上一篇博客我们大概介绍了一下nginx,nginx的架构,nginx编译安装和nginx命令的用法,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/1236680 ...