run方法是springboot启动过程中非常重要的步骤。
SpringBootMytestApplication#main →
SpringApplication#run(java.lang.Class>, java.lang.String...) → SpringApplication#run(java.lang.Class>[], java.lang.String[]) →
SpringApplication#run(java.lang.String…)
SpringApplication#run(java.lang.String…)
/**
* 运行spring引用,创建并刷新一个ApplicationContext。
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// 记录执行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// ConfigurableApplicationContext应用上下文,Spring IoC容器的一种实现。
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 1、获取启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
///2、构造上下文环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
///3、初始化应用上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 4、刷新上下文的准备阶段
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 5、刷新上下文
refreshContext(context);
// 6、刷新上下文后的扩展
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
SpringApplication#refreshContext
→
SpringApplication#refresh
→
AbstractApplicationContext#refresh
AbstractApplicationContext#refresh
refresh方法,这个方法其实标志着IoC容器初始化过程的正式启动。
在Spring IoC容器初始化过程简单说过。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 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);
try {
// 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();
}
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();
}
}
}
AbstractApplicationContext#invokeBeanFactoryPostProcessors
→
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, java.util.List)
跳进ConfigurationClassPostProcessor的实现
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor#processConfigBeanDefinitions
parser.parse(candidates)是我们重点要关注的方法。
parser.parse(candidates)
// Parse each @Configuration class,解析被@Configuration注解修饰的配置类。
ConfigurationClassParser#parse(java.util.Set
ConfigurationClassParser#parse(org.springframework.core.type.AnnotationMetadata, java.lang.String)
→
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
→
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass中
处理@ComponentScan注解,这个sourceClass是springboot启动类。
允许多个@ComponentScan。
注意这一行this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
org.springframework.context.annotation.ComponentScanAnnotationParser#parse
因为我们没有配置@ComponentScan的basePackages,
会取ClassUtils.getPackageName(clazz)作为basePackages;
有了basePackages之后,接下来执行scanner.doScan()方法,
org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
ClassPathScanningCandidateComponentProvider#findCandidateComponents
找basePackages下的Components
ClassPathScanningCandidateComponentProvider#scanCandidateComponents
由basePackages拼接packageSearchPath = classpath*:com/duohoob/springbootmytest/**/*.class
扫描到com.duohoob.springbootmytest.controller.TestController
封装为ScannedGenericBeanDefinition。
我们再回到ClassPathBeanDefinitionScanner#doScan
BeanDefinitionReaderUtils#registerBeanDefinition
这一步是将BeanDefinition注册到Spring注册到容器。
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass中
getImports(sourceClass),获取通过@Import注解导入的组件。
再回到ConfigurationClassParser#parse(java.util.Set
ConfigurationClassParser.DeferredImportSelectorHandler#process
这是springboot自动配置第三方依赖中bean的入口。
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
这是获取META-INF\spring.factories中的配置类。
再回到ConfigurationClassPostProcessor#processConfigBeanDefinitions
this.reader.loadBeanDefinitions(configClasses);
这一步是把META-INF\spring.factories中配置类的@Bean加载到容器中。