面试官:能说下 SpringBoot 启动原理吗?

这是你的 SpringBoot ,启动,只需一键。

@SpringBootApplication
public class ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class,args);
    }
}

但这一键背后发生了什么?

挂着嘴边的 IOC 容器何时诞生,天天见的 @Autowired、@Service 如何实现,陪你一路的 Bean,穿林打叶, 始于何处又终于何方?

这次,从 SpringApplication.run 开始。

本文 SpringBoot Version : 2.1.15

SpringBoot

如图所示,大致可分为九步,其中第五步最重要。 

1. 要有光

SpringApplication.run下来:

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return (new SpringApplication(sources)).run(args);
}

首先是new SpringApplication(sources),构建 SpringApplication 实例,将 ServerApplication.class 存储在this.primarySources 属性中。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    // 把 ServerApplication.class 设置为属性存储起来
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 设置应用类型
    this.webApplicationType = deduceWebApplicationType();
    // 设置初始化器,最后会调用这些初始化器
    setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
    // 设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 根据堆栈,推断并设置主类
    this.mainApplicationClass = deduceMainApplicationClass();
}
2. run

有了实例,就要 run。这个方法看上去就知道做了一堆工作,一个个说。代码里已标出序号。

public ConfigurableApplicationContext run(String... args) {
    // 计时工具
    StopWatch stopWatch = new StopWatch();
    // 1:获取并启动监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 2:根据 SpringApplicationRunListeners 及参数来准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 打字:启动 Spring Boot的时候打印在控制台上的ASCII艺术字
        Banner printedBanner = printBanner(environment);
        // 3:创建 Spring 容器
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 4:容器前置处理
        prepareContext(context, environment, listeners, applicationArguments,printedBanner);
        // 5:刷新容器
        refreshContext(context);
     // 6:Spring容器后置处理
        afterRefresh(context, applicationArguments);
      // 7:发出结束执行的事件
        listeners.started(context);
        // 8:执行Runners
        this.callRunners(context, applicationArguments);
        stopWatch.stop();
        // 9. 返回容器
        return context;
    }
}
2.1. 监听器
		// 1:获取并启动监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();

首先看第一个,获取并启动监听器。会在 spring.factories 文件中寻找 SpringApplicationRunListener实现类名,反射构造实例,且持有SpringApplication的引用。再把实例放到SpringApplicationRunListeners容器来管理。

最后遍历执行listeners容器里所有SpringApplicationRunListener对象的starting方法,其实就是调用EventPublishingRunListenerstarting()启动。

2.2. 环境构建
// 2:根据 SpringApplicationRunListeners 及参数来准备环境   
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);

稍微跟一下

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    // 配置
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 发布环境已准备事件,这是第二次发布事件
    listeners.environmentPrepared(environment);
}

listeners.environmentPrepared(environment)发布事件,哪个监听器获取呢,有个很重要的,叫ConfigFileApplicationListener,顾名思义, 配置文件。

是的,项目中的 properties 和 yml 配置文件都是其内部类所加载。

场间休息
Banner printedBanner = printBanner(environment);

这个打印了启动时在控制台看到的 Spring Boot 艺术字。

2.3. 创建容器
// 3:创建 Spring 容器 
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
		}
	}

创建ApplicationContext, 容器类型根据webApplicationType判断。比如是SERVLET时,会通过反射装载对应的字节码,是AnnotationConfigServletWebServerApplicationContext。

需要注意,这里的三个类型全都是继承GenericApplicationContext。记住,后面要考。

2.4. 事前准备
// 4:容器前置处理
prepareContext(context, environment, listeners, applicationArguments,printedBanner);

这个准备包括了一个重要的操作:将启动类注入容器,为后续开启自动化配置奠定基础。

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 设置容器环境,包括各种变量
    context.setEnvironment(environment);
    // 执行容器后置处理
    postProcessApplicationContext(context);
    // 执行容器中的 ApplicationContextInitializer(包括 spring.factories 和自定义实例)
    applyInitializers(context);
   // 发送容器已经准备好的事件
    listeners.contextPrepared(context);
    // 注册启动参数bean,将容器指定的参数封装成bean,注入容器; 设置banner
    // 获取启动类指定的参数,可以是多个
    Set<Object> sources = getAllSources()
    // 加载启动类,将启动类注入容器
    load(context, sources.toArray(new Object[0]));
    // 发布容器已加载事件
    listeners.contextLoaded(context);
}

看一下getAllSources().

public Set<Object> getAllSources() {
    Set<Object> allSources = new LinkedHashSet();
    if (!CollectionUtils.isEmpty(this.primarySources)) {
        //获取 primarySources 属性
        allSources.addAll(this.primarySources);
    }
}

primarySources还记得不?在实例化SpringApplication时存了起来。也就是我们的启动类。

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

再顺着listeners.contextLoaded(context)一路向下,来到load方法。以注解方式,将启动类 bean 信息加载到 beanDefinitionMap 中,后续该启动类将作为开启自动化配置的入口。

private int load(Class<?> source) {
    if (isComponent(source)) {
        // 以注解方式,将启动类 bean 信息存入 beanDefinitionMap
        this.annotatedReader.register(source);
    }
}
2.5. 好戏登场
 refreshContext(context);

刷新容器。就像本节标题,前面其实都是 Spring Boot 的工作, Bean 是放在 Spring 的 IOC 容器里,Spring 在哪呢?这就是了。

这里调用的是org.springframework.context.support.AbstractApplicationContextrefresh

如果你在网上冲浪,搜索 Spring IOC 初始化,大抵都是从这开始的。当然,如果不用Spring Boot,用原生的 Spring、Spring Mvc ,那前面的流程就变了,这里就不说了。

SpringFramework

从前所述,Spring Boot 告一段落,来到 SpringFramework 的 refresh。

如图所示,大致分为四步。

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 准备工作,记录时间、标记状态、处理配置文件中的占位符等
      prepareRefresh();
      // 1.初始化 IOC 容器
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 2. 设置 BeanFactory 的类加载器,添加BeanPostProcessor后置处理器
      prepareBeanFactory(beanFactory);
      try {
         // 提供子类覆盖的额外处理
         postProcessBeanFactory(beanFactory);
         // 3. 调用BeanFactory后置处理器
         invokeBeanFactoryPostProcessors(beanFactory);          
         // 注册Bean后置处理器BeanPostProcessor 的实现类
         registerBeanPostProcessors(beanFactory);
         // 初始化当前 ApplicationContext 的 MessageSource,国际化处理
         initMessageSource();
         // 初始化当前 ApplicationContext 的事件广播器
         initApplicationEventMulticaster();
         // 模板方法(钩子),供子类实现,初始化一些特殊的 Bean
         onRefresh();
         // 注册事件监听器,监听器需实现 ApplicationListener 接口。
         registerListeners();
         // 4. 初始化所有(non-lazy-init)singleton beans
         finishBeanFactoryInitialization(beanFactory);
         // 广播事件,ApplicationContext 初始化完成
         finishRefresh();
      }
      catch (BeansException ex) {
         // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
         destroyBeans();

      }
   }
}

又是很多工作,但这里只重点关注部分内容。代码中也加上了序号。

1. 初始化 IOC 容器
// 1.初始化 IOC 容器
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

在没Spring Boot的岁月, 这一步会做很多重要工作,比如将 xml配置中的 bean配置信息,也就是Resource转成Document再转成BeanDefinition, 接着注册到 IOC 容器 BeanFactory。

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

有两个实现类,大多数文章会告诉你,下一步是AbstractRefreshableApplicationContext

	protected final void refreshBeanFactory() throws BeansException {
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			loadBeanDefinitions(beanFactory);
		}
	}

接着会有不同的实现类,一般是 XmlWebApplicationContext, 之后解析配置等等。

没错。在使用 Spring MVC,基于 xml 配置时,当启动 tomcat ,一路往下,初始化 Spring 应用上下文创建的是WebApplicationContext。(详见 ContextLoaderListener 父类 ContextLoader)

最终实例化的实现类默认值在ContextLoader.properties中配置:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

所以这种说法是正确的。

不过现在是基于 SpringBoot 的注解方式。难道是从 XmlWebApplicationContext换成了图中的AnnotationConfigWebApplicationContext,那它怎么知道动态用了注解呢?

SpringBoot 版本

后退一步,回到refreshBeanFactory(),看看另一个实现类GenericApplicationContext

protected final void refreshBeanFactory() throws IllegalStateException {
		if (!this.refreshed.compareAndSet(false, true)) 
		***
		this.beanFactory.setSerializationId(getId());
	}

几乎什么都没做,看起来像是没用的。但SpringBoot项目就是会走到这里,在第一部分SpringBoot.容器构建那里说过:

需要注意,这里的三个类型全都是继承GenericApplicationContext。记住,后面要考。

所以其实创建ApplicationContext时就选定了类型。这样的话,BeanDefinition在哪加载的?往下看。

2. 准备bean 容器
// 2. 设置 BeanFactory 的类加载器,添加BeanPostProcessor后置处理器 
prepareBeanFactory(beanFactory);

如果是原始的 Spring MVC 模式,上一步把 xml 配置的 bean 注册后,这里会手动注册特殊 bean。

也会对 BeanFactory 各种功能进行填充,如常用注解 @Autowired、 @Qualifier 等,添加后置处理器。

3. BeanFactory 处理器

这里我分为三步来说:解析、扫描和注册。

// 3. 调用BeanFactory后置处理器
invokeBeanFactoryPostProcessors(beanFactory);

调用BeanFactoryPostProcessor各实现类的 postProcessBeanFactory()回调方法。这个在SpringBoot里就有点重要了。

因为它实现了将 annotation 的 Bean 配置转化为 BeanDefinition ,以及注册。

是的,本章第 1 小结, SpringMvc 时期obtainFreshBeanFactory()的部分工作到了这里,刚才遗留的问题,在这听到了答案。

再一路到PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors, 有

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

这儿就是 BeanDefinition 的注册入口,注册 BeanDefinition 的后置处理器。

	private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanDefinitionRegistry(registry);
		}
	}

其实现类是ConfigurationClassPostProcessor, 再往下到processConfigBeanDefinitions

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    for (String beanName : candidateNames) {
        // 默认仅有主类被添加
        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
    }
    // 解析被 @Configuration 注解的类
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory,
            this.problemReporter,this.environment,this.resourceLoader,
            this.componentScanBeanNameGenerator,registry);
    do {
        // 解析的核心方法
        parser.parse(candidates);
    } 
}
(1)解析

看这个解析的核心方法

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();       
        // 主类的解析从这开始
	parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
       } 
}
protected final void parse(AnnotationMetadata metadata, String beanName) {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

主类被作为 BeanDefinition 加载到 BeanFactory 中,被 ConfigurationClassParser 解析器所解析。

protected void processConfigurationClass(ConfigurationClass configClass) {
    // 从 main方法所在主类开始,递归解析
    SourceClass sourceClass = asSourceClass(configClass);
    do {
        // 解析单个配置类
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
}

doProcessConfigurationClass负责解析的主要工作,会处理一个非常或者说必用的注解@ComponentScan,根据该注解信息得到扫描路径,开始扫描。

同时,对扫描出来的 Bean 又会返回个新的 sourceClass 用于继续解析。新 sourceClass 是上一个 sourceClass 的父类。所以该解析过程是个递归,主类开始,逐层向上。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) {
// 处理 @ComponentScan 注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
			for (AnnotationAttributes componentScan : componentScans) {
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
            // 处理扫描出来bean
	    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				}}}
}

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
	return scanner.doScan(StringUtils.toStringArray(basePackages));
}

现在,获得了扫描器和扫描路径, 调用doScan.

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 1. 扫描获取 BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            ***
           // 2. 注册 BeanDefinition 到 BeanFactory
           registerBeanDefinition(definitionHolder, this.registry);  
        }
    }
    return beanDefinitions;
}
(2)扫描

findCandidateComponents 方法将会根据扫描路径获取非常重要的 BeanDefinition

关于BeanDefinition,随口一提 : 在容器本身内,bean 定义表示为 BeanDefinition 对象。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
			return scanCandidateComponents(basePackage);
	}

好了,现在路径知道了,也开始扫描了,但哪个可爱的 Class 有幸被转化为 BeanDefinition 呢?

是的,肯定有一个判断。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
       // 类的元数据
       MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
       // 判断是否满足条件
       if (isCandidateComponent(metadataReader)) { 
				***
}
protected boolean isCandidateComponent(MetadataReader metadataReader) {
        for (TypeFilter tf : this.excludeFilters) {    
            if (tf.match(metadataReader, getMetadataReaderFactory())) {    
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {   
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }

在判断方法isCandidateComponent里,会进行很多的过滤检查处理。

其中ClassPathScanningCandidateComponentProvider#registerDefaultFilters方法,会给includeFilters添加默认的AnnotationTypeFilter,负责处理 @Component,@ManagedBean等注解。

最终的调用链是: AnnotationTypeFilter#match -> matchSelf.

protected boolean matchSelf(MetadataReader metadataReader) {
    AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();    
    return metadata.hasAnnotation(this.annotationType.getName()) ||    
            (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); 
}

这里会获取 Class 的注解元数据,检查是否有对应 annotationType, 简单来说,就是检查有没有那些我们常用的 @Service@Repository了,并检查嵌套注解是否有对应的 annotationType。

(3)注册

回到doScan,扫描出来的 BeanDefinition 将会调用 registerBeanDefinition 注册。

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder,BeanDefinitionRegistry registry)  {
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
}

这里的 registry,默认实现类是 DefaultListableBeanFactory 。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition)  {
    if (hasBeanCreationStarted()) {
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
       }} 
    }
}

最终,也就是将 BeanDefinition 添加到一个 key - value 的集合当中,这样就完成了注册工作。

4. 实例化Bean

前面得到了充满 BeanDefinition 的 BeanFactory 。来到最重要的地方,实例化 Bean。

// 4. 初始化所有(non-lazy-init)singleton beans
finishBeanFactoryInitialization(beanFactory);
	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  }

getBean方法的实现位于AbstractBeanFactory.doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args,
        boolean typeCheckOnly) throws BeansException {
    final String beanName = transformedBeanName(name);
    Object bean;
    // 获取已注册的单例 Bean
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        // 根据 name 和 beanName 判断应该返回 factoryBean 还是 bean
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
        // *** 判断循环依赖、类型检查、获取父 BeanFactory
        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 初始化依赖的Bean(@DependsOn)
            ***
            // 创建单例
            if (mbd.isSingleton()) {
                // 回调创建
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        // 创建 Bean
                        return createBean(beanName, mbd, args);
                    } 
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } else if (mbd.isPrototype()) {
                // 如果是 prototype scope 的
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    // 创建Bean
                    prototypeInstance = createBean(beanName, mbd, args);
                } ***
            } else {
                // 既不是单例Bean,也不是prototype,获取其 Scope,委托给相应的实现类来处理
            }
        } ***
    }
    // 类型检查
    return (T) bean;
}
循环依赖

这儿是 Spring 解决循环依赖的关键。

	// 尝试获取Bean
	Object sharedInstance = getSingleton(beanName);

根据 beanName 尝试从 singletonObjects 缓存中获取单例 Bean,获取不到则再尝试从earlySingletonObjects、singletonFactories 从获取。

创建Bean
createBean(beanName, mbd, args);

缓存中获取不到,就创建,

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

它会调用doCreateBean()

在这停顿,打这开始,又是一个热门问题:Spring Bean 生命周期

Spring Bean 生命周期

如图,谈到 Bean 生命周期,大概都是这张图的内容。对应的其实就是上文的doCreateBean

protected Object doCreateBean(
        final String beanName, final RootBeanDefinition mbd,
        final @Nullable Object[] args) throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        // 1. 说明不是 FactoryBean,实例化 Bean
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // *** 解决循环依赖等
    Object exposedObject = bean;
    try {
        // 2. 前面实例化后,属性装配,自动注入
        populateBean(beanName, mbd, instanceWrapper);
        // 3. 初始化。init-method、InitializingBean、BeanPostProcessor等,各种回调
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    } 
    try {
        // 4. 销毁-注册回调接口
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    return exposedObject;
}

这个方法也有点长,而且每个分支跟下去都更长,这里截取部分且不进行过多深究。

前面图中看着挺多,但实际在我理解其实也可以分四步:

1. 实例化
 instanceWrapper = createBeanInstance(beanName, mbd, args);
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
   // 校验类访问权限
   if (mbd.getFactoryMethodName() != null)  {
      // 工厂方法实例化
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
   // 非第一次创建,如prototype 。判断用无参构造,还是构造函数依赖注入来实例化
   boolean resolved = false;
   boolean autowireNecessary = false;
   ***
   if (resolved) {
      if (autowireNecessary) {
         // 构造函数依赖注入
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         // 无参构造函数
         return instantiateBean(beanName, mbd);
      }
    }
   // 是否采用有参构造函数
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null ||
         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
      // 构造函数依赖注入
      return autowireConstructor(beanName, mbd, ctors, args);
   }
   // (默认) 调用无参构造函数
   return instantiateBean(beanName, mbd);
}

默认会采用无参构造函数

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
   try {
      Object beanInstance;
      final BeanFactory parent = this;
      if (System.getSecurityManager() != null) {
        // 实例化
	beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
			getInstantiationStrategy().instantiate(mbd, beanName, parent),
			getAccessControlContext());
			}
      else {
         // 实例化
         beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
      }
      // 包装下返回
      BeanWrapper bw = new BeanWrapperImpl(beanInstance);
   } 
}

可以看到,关键在于getInstantiationStrategy().instantiate(mbd, beanName, parent)在进行实际的实例化。

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // 如果不存在需要被重写的方法,就不用cglib,使用反射
    if (!bd.hasMethodOverrides()) {
        // 通过构造方法实例化
        return BeanUtils.instantiateClass(constructorToUse);
    } else {
        // 存在方法覆写,通过cglib生成
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

到这里,Bean 初始化完成了。

2. 属性装配
populateBean(beanName, mbd, instanceWrapper);

属性装配,也就是注入。对已实例化好的 bean 的属性进行赋值, 如果 bean 中关联着其他 bean,也会帮建立 bean 间的关系,对的就是 Bean 间的依赖注入,说白点就是 @Autowored 注解。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // bean 实例的所有属性。获取待注入的property,配置文件中的<property>也将在这里被处理
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // 按照名字获取属性
        if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // 按照类型获取属性
        if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }
    if (hasInstAwareBpps) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
               // 后置处理器处理@Autowired @Resource等注解
               pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                }
                pvs = pvsToUse;
            }}}
    // 注入 <property> 属性
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

在对 Bean 赋值时,会先去处理该 Bean 中注入的 Bean,因此对于相互注入的 Bean 来说不用担心 Bean 的生成先后顺序问题。

3. 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);

属性注入完成后,这一步就是处理各种回调了。

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    ***
    else {
        // 1. 如bean实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 接口,回调
        invokeAwareMethods(beanName, bean);
    }
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 2. 执行BeanPostProcessor预初始化方法:postProcessBeforeInitialization 
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
        // 3. 处理自定义的init-method,如实现了InitializingBean接口执行afterPropertiesSet()
        invokeInitMethods(beanName, wrappedBean, mbd);
    }***
    if (mbd == null || !mbd.isSynthetic()) {
        // 执行BeanPostProcessor初始化后回调 postProcessAfterInitialization
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

可以看到,在Bean 生命周期图里各种初始化操作都是在这里完成的。

  1. 执行一些Aware方法,比如 BeanNameAware的setBeanName(beanName)

  2. 执行 BeanPostProcessor 预初始化方法,例如,@PostConstruct 注解的方法。

  3. 执行 afterPropertiesSet() 方法与自定义的 inti-method

  4. 执行 BeanPostProcessor 初始化后方法

4. 销毁

最后就是销毁。这个销毁当然不是指对象被 GC 的销毁。

registerDisposableBeanIfNecessary(beanName, bean, mbd);

register, 这里是在进行销毁逻辑的注册,在销毁时会执行。

比如使用@Bean(destroyMethod = "***")

SpringBoot 回来啦

好了,SpringFramework 的工作结束了,还记得开头的SpringApplication.run吗, 还没执行完呢。

        // 5:刷新容器
        refreshContext(context);
     // 6:Spring容器后置处理
        afterRefresh(context, applicationArguments);
      // 7:发出结束执行的事件
        listeners.started(context);
        // 8:执行Runners
        this.callRunners(context, applicationArguments);
        stopWatch.stop();
        // 9. 返回容器
        return context;

现在 步骤5 容器已经刷新了,之后还有:

(6). afterRefresh

扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理。

(7).listeners.started

发布结束执行事件

(8). this.callRunners

区分ApplicationRunnerCommandLineRunner, 执行run方法。

可以自定义 ApplicationRunner 或 CommandLineRunner,实现 run 方法,注入到容器中, 在SpringBoot 启动后,就会执行所有的 runner 的 run 方法。

(9). return context

终于 run 完了,返回容器。

总结

从 SpringApplication.run开始,我们所看到的简单的一行代码背后却有这么多弯弯绕。

以上很多地方只是浅尝辄止或一笔带过,主要谈了 IOC 和 Bean有关的主流程,得以对 SpringBoot、 SpringFramework 启动流程有个基本认识,尚有很多像如何解决循环依赖、为何 @Transactional 同类调用会失效等等一些称得上常见的问题,都可以在这里找到答案。但一叶知秋,透过这些不难看出Spring 的优秀巧妙的设计和思路。

参考

  • https://www.xxelin.com/2020/05/29/SpringBoot-BeanDefinition-loading/BeanDefinition 加载过程

  • https://javadoop.com/post/spring-ioc Spring IOC 源码分析

全文完


以下文章您可能也会感兴趣:

我们正在招聘 Java 工程师,欢迎有兴趣的同学投递简历到 rd-hr@xingren.com 。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值