1. 程式人生 > 其它 >SpringBoot的啟動類是如何將自己交給spring管理的?

SpringBoot的啟動類是如何將自己交給spring管理的?

技術標籤:日常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管理的地方了!