SpringBoot源碼分析

第一步,構建SpringApplication

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 = new HashSet();
    this.isCustomEnvironment = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //通過加入的jar判斷當前環境類型 servlet / reactive / NONE
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

第二步,執行SpringApplication.run()

public ConfigurableApplicationContext run(String... args) {
    //準備啓動計時
    StopWatch stopWatch = new StopWatch();
    //開始計時
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    //讀取配置文件 初始容器配置 #1.1
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    //循環去啓動監聽
    listeners.starting();
    Collection exceptionReporters;
    try {
        //包裹main函數傳入的參數而已
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //準備當前的環境 dev or test and prod
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        //打印logo
        Banner printedBanner = this.printBanner(environment);
        //創建上下文對象 #1.3
        context = this.createApplicationContext();
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //刷新上下文對象.實則在做什麼事情呢? 此時我們會想到我的內置tomcat 哪去了? #1.4
        this.refreshContext(context);
        //this.afterRefresh 點進去一看 空方法?那就是給我們後續擴展的
        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);
    }
}

1.1

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
    // this.getSpringFactoriesInstances #1.2
    return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

1.2

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = this.getClassLoader();
    //加載 springboot & autoconfigure jar包下的spring.factories
    //names:
    //org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    //org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    //org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    //org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    //org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
    //org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //創建這些類名的實例
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

1.3

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch(this.webApplicationType) {
            case SERVLET:
                #我們看一下這個類 AnnotationConfigServletWebServerApplicationContext # 1.3.1
                contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                break;
            case REACTIVE:
                contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                break;
            default:
                contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        } catch (ClassNotFoundException var3) {
            throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
        }
    }

    return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

1.3.1

//ServletWebServerApplicationContext 有點眼熟 
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry{}
// ServletWebServerApplicationContext類:
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
    private static final Log logger = LogFactory.getLog(ServletWebServerApplicationContext.class);
    // 熟悉的dispatcherServlet
    public static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
    private volatile WebServer webServer;
    private ServletConfig servletConfig;
    private String serverNamespace;
}

1.4 SpringApplication.refreshContext()

private void refreshContext(ConfigurableApplicationContext context) {
    //#1.4.1
    this.refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException var3) {
        }
    }

}

1.4.1

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    //實際這裏就是調用的applicationContext 的refresh() ,多態 ServletWebServerApplicationContext #1.4.2
    ((AbstractApplicationContext)applicationContext).refresh();
}

1.4.2 : ServletWebServerApplicationContext.onRefresh()

protected void onRefresh() {
    super.onRefresh();

    try {
        //1.4.2.1
        this.createWebServer();
    } catch (Throwable var2) {
        throw new ApplicationContextException("Unable to start web server", var2);
    }
}

1.4.2.1 此時實際上就是在創建Tomcat 容器

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = this.getServletContext();
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = this.getWebServerFactory();
        this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
    } else if (servletContext != null) {
        try {
            this.getSelfInitializer().onStartup(servletContext);
        } catch (ServletException var4) {
            throw new ApplicationContextException("Cannot initialize servlet context", var4);
        }
    }

    this.initPropertySources();
}

猜想+debug+源碼 ,只有自己感覺爽了,才能一步一步的看源碼~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章