SpringBoot的啟動類是如何將自己交給spring管理的?
阿新 • • 發佈:2021-01-31
技術標籤:日常javaspring bootspring面試
SpringBoot的啟動類是如何將自己交給spring管理的
一、發現問題:
在學習SpringBoot的自動裝配的時候發現問題,啟動Application.java類的run()方法時,Application類本身是如何交給spring管理的?
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(StadiumGatewayApplication. class, args);
}
}
二、猜測
1、跟@SpringBootApplication有關?
2、是run方法的內部邏輯
三、分析實踐
1、首先分析是否是跟@SpringBootApplication有關,檢視註解
@SpringBootApplication
@Target(ElementType.TYPE)//修飾自定義註解,指定該自定義註解的註解位置,類還是方法,或者屬性
@Retention(RetentionPolicy.RUNTIME)//被它所註解的註解保留多久,註解的生命週期。可選的引數值在列舉型別 RetentionPolicy 中,一般如果需要在執行時去動態獲取註解資訊,那隻能用 RUNTIME 註解;如果要在編譯時進行一些預處理操作,比如生成一些輔助程式碼(如 ButterKnife),就用 CLASS註解;如果只是做一些檢查性的操作,比如 @Override 和 @SuppressWarnings,則可選用 SOURCE 註解。
@Documented//將此註解包含在 javadoc 中 ,它代表著此註解會被javadoc工具提取成文件。在doc文件中的內容會因為此註解的資訊內容不同而不同。相當與@see(後面可以跟類路徑等引數實現連結跳轉 Ctrl跳轉),@param(註釋引數) 等。
@Inherited//修飾自定義註解,該自定義註解註解的類,被繼承時,子類也會擁有該自定義註解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
沒有找到將本身注入spring容器裡的程式碼。
2、分析是否與run()方法有關
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
再次點進run()方法後就是真正的邏輯,根據方法名猜測方法的作用,是看原始碼的一個小技巧。
找到了prepareContext()方法。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//根據方法名判斷這是在啟動容器前做的事,大概就是這裡面了
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
再進入load()方法,一直往底下找!
private int load(Class<?> source) {
if (this.isGroovyPresent() && BeanDefinitionLoader.GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
BeanDefinitionLoader.GroovyBeanDefinitionSource loader = (BeanDefinitionLoader.GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, BeanDefinitionLoader.GroovyBeanDefinitionSource.class);
this.load(loader);
}
if (this.isComponent(source)) {
this.annotatedReader.register(new Class[]{source});
return 1;
} else {
return 0;
}
}
連蒙靠猜就找到了啟動類交給spring管理的地方了!