Spring Boot 启动流程详解(二)
使用SpringBoot已经有好几个月了,一直对框架当中Bean的实例化过程有很感兴趣,比如Spring是如何去加载.class文件,并解析其中我们通过注解@Configuration, @PropertySource,@ComponentScan,@Import,@Bean等产生我们自定义的Bean的。下面我们就来看看,框架当中是如何实现Bean的加载与创建的。
下面是SpringApplication函数的run函数:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
注意,在完成了context的创建之后,会调用refreshContext函数,refreshContext函数中会调用refresh函数,在该函数中又会先调用祖先类AbstractApplicationContext类的refresh函数。该函数如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 初始化BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 注册部分Bean后处理器,这部分Bean后处理器主要是通过new方法创建
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//在子类中注册部分Bean后处理器,这部分Bean后处理器主要是通过new方法创建
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//调用Bean工厂后处理器,加工BeanDefinitionRegistry注册表中的所有BeanDefinition对象
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册Bean后处理器,这部分Bena后处理器大部分是通过beanFactory的getBean方法(其内部主要使用了策略模式及反射机制)获取的
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化信息源
initMessageSource();
// Initialize event multicaster for this context.
// 初始化应用上下文时间广播
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 初始化其他特殊的Bean,由具体的子类来实现
onRefresh();
// Check for listener beans and register them.
// 注册事件监听器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化所有非懒加载的单例Bean,大部分是通过beanFactory的getBean方法(其内部主要使用了策略模式及反射机制)获取的
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新并发布容器刷新事件
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.
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();
}
}
}
在函数调用过程当中,调用了函数invokeBeanFactoryPostProcessors,在该函数的调用流程当中,调用链为PostProcessorRegistrationDelegate ->ConfigurationClassPostProcessor -> ConfigurationClassParser,在类ConfigurationClassParser类的doProcessConfigurationClass函数中,将启动类(即@SpringBootApplication标注的类,主要是@SpringBootApplication注解中的@Configuration注解的作用)做为入口,读取我们所定义的Bean,将其转化为一个个的BeanDefinition。下面,我们来看一下该函数的定义:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
在该代码块中我们可以看到,函数首先处理由注解@PropertySource,该注解用于将我们自定义的属性文件加载到上下文环境当中。然后对@ComponentScan注解定义的包结构进行扫描,将启动类所在包下的子包中Bean文件(不包括子文件夹中的Bean文件)定义转化为BeanDefinition,如果该Bean定义的文件中也使用了@Configuration对其进行了注解,那么将会进行递归。之后,解析由注解@Import和@ImportResource注解导入的Bean定义文件,最后是直接由@Bean注解的成员函数。之后所有需要实例化的Bean都已经完成了解析,都转化为BeanDefinition,将由refresh函数中的finishBeanFactoryInitialization根据这些信息对Bean进行实例化。