一、项目启动方法做了什么?
@SpringBootApplication
public class DemoymlApplication {
public static void main(String[] args) {
SpringApplication.run(DemoymlApplication.class, args);
}
}
项目启动类中,可以看到DemoymlApplication类中只有两个需要注意的点
- main方法,在main方法中调用了SpringApplication.run(DemoymlApplication.class, args)。
- @SpringBootApplication注解。 根据这两点,一个一个进行分析。首先看看main方法中做了些什么东西。
1.1、SpringApplication.run(DemoymlApplication.class, args);做了什么?
1.1.1、main方法调用SpringApplication的静态run方法
main方法调用SpringApplication的静态run方法传递的参数是:当前启动类、main方法中的args。
run方法源码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
静态run方法中,创建了类数组和args为参数,以此参数调用了另一个静态run方法,方便区分我就叫run2吧
1.1.2、run2方法创建了SpringApplication类并执行SpringApplication的run方法
源码
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
run2方法根据接收到的类数组(数组中只有启动类一个类),调用了SpringApplication构造方法,创建出SpringApplication类。接下来我们看看这个构造方法做了什么?
1.1.2.1、new SpringApplication(primarySources)构造方法
源码
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
根据源码,看到这个构造方法需要两个参数:ResourceLoader(加载资源的策略接口)、类数组。ResourceLoader这个参数传的是null,类数组传的是只保护启动类的数组。可以看出这个构造方法给一些参数进行赋值。其中有两个需要注意一下。
- this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));获取初始化spring的回调接口实现类,并将这个回调接口实现类集合赋值给了initializers属性。
- this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));获取事件监听接口实现类,并把些类赋值给listeners属性。
- 初始化spring的回调接口实现类和事件监听接口实现类是从哪里来的呢?根据源码看到通过类加载器从各个包配置文件中获取
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
1.1.2.2、在创建 SpringApplication类后调用run方法
看看源码在这个run方法中做了什么
public ConfigurableApplicationContext run(String... args) {
//创建秒表,记录run方法运行时间
StopWatch stopWatch = new StopWatch();
//开始设置时间
stopWatch.start();
//初始化应用上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
设置java.awt.headless的值,默认为true
configureHeadlessProperty();
//获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//初始化默认参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//项目运行环境的预配置
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//创建spring容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//spring容器前置处理
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新spring容器,接下来重点跟踪一下这个方法
refreshContext(context);
//spring容器后置处理
afterRefresh(context, applicationArguments);
//停止秒表,统计时长
stopWatch.stop();
//打印启动日志
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布应用上下文信息
listeners.started(context);
//执行所有 Runner 运行器
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
1.1.2.3、 refreshContext(context)方法做了什么?
- 注册一个关机钩子
context.registerShutdownHook();
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
//创建一个以SpringContextShutdownHook为名的线程
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
@Override
public void run() {
synchronized (startupShutdownMonitor) {
//实际执行上下文关闭:发布一个ContextClosedEvent和销毁该应用程序上下文bean工厂中的单例。
doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
- refresh((ApplicationContext) context)
@Deprecated
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
refresh((ConfigurableApplicationContext) applicationContext);
}
- refresh((ConfigurableApplicationContext) applicationContext);
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
- applicationContext.refresh();刷新底层
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
- super.refresh();
@Override
public void refresh() throws BeansException, IllegalStateException {
//获得锁
synchronized (this.startupShutdownMonitor) {
//创建DefaultStartupStep类
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 准备用于刷新的上下文.
prepareRefresh();
// 告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 配置工厂的标准上下文特征,比如上下文的ClassLoader和post-processor
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 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();
contextRefresh.end();
}
}
}
1.1.3、 方法调用顺序图(部分)
程序执行的太多,就不一一跟踪了,这样太浪费时间,下面贴出调用方法的顺序
根据这些方法的调用顺序,进行翻找,发现在ConfigurationClassParser.processImports方法中发现了自己写的类,由于自己写的类都加了@Controller、@Service、@RestController等spring的注解,因此被扫描到了,进行了bean注册。现在我们看看这些自己写的使用注解的类是在什么时候被扫描到的?一直往前翻找,发现这些类在run方法的context.branFactory中就已经出现,接下来我们看看是怎么出现在这里的。
根据这个图片可以看到,程序执行到这里时,自己写的类还没有被扫描到只有5个spring的类,根据断点调试,发现在执行完prepareContext方法后,项目的启动类被扫瞄到了,那么就到prepareContext方法中看看。
一步步分析启动类是如何加载到beanFactory中的。
- prepareContext方法中的这部分代码
- getAllSources如何获取到启动类的
根据源码发现是从字段中获取的primarySources,那么就要看看这个字段什么时候赋值的。发现在执行main方法时就带着这个类了,在创建SpringApplication类时就将启动类放到了primarySources字段中
- 如何获取启动类找到了,那么如何将启动类放入beanFactory中,这中间调用了很多方法,就不一一跟踪了,下面贴出方法调用顺序
在DefaultListableBeanFactory.registerBeanDefinition方法中将启动类放到了beanFactory中。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// 还在启动注册阶段,在这里将启动类放入beanFactory中
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
现在启动类加载解决了,加下来看看自己写的其他类是什么时候加载到beanFactory中的
1.1.4 获取其他类的过程
- 从beanFactory中获取启动类 这次获取的类是使用了@Component注解的类
上图获取的基础包就是启动类所在的包
扫描基础包
基础包下的文件都会获取到,如下图
断点执行过程如下图
将扫描到的类放入到beanFactory中
到这里,自己写的类已经放入到beanFactory中了,后面还有一些spring的其他类陆续放入到beanFactory中,就不继续追踪了。