用过SpringBoot的都知道,如下代码就是一个最基本的SpringBoot主启动类,通过启动这个类,即可完成SpringBoot的启动装配等功能。

  1. @SpringBootApplication
  2. public class SpringBootMainApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SpringBootMainApplication.class, args);
  5. }
  6. }

所以,来看看,到底是如何实现的?
首先,可以看到,只有两处特殊的地方

  1. @SpringBootApplication
  2. SpringApplication.run(SpringBootMainApplication.class, args);

所以,我们就从这两处特殊的地方进行入手

一、@SpringBootApplication

首先,可以看到
第一个注解 @ComponentScan,进行包扫描(都知道,放在主启动类所在包,以及子包下的组件,都会被扫描进入容器中)
第二个注解 @EnableAutoConfiguration, 看名字就知道,启动启动装配,通过AOP/事务的套路之后,都知道@EnableXXX,会导入一些组件,进而实现一些功能

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @SpringBootConfiguration
  6. @EnableAutoConfiguration
  7. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  9. public @interface SpringBootApplication {}

所以,我们进入@EnableAutoConfiguration中看看,一看,果然如此,导入了一个ImportSelector,在ioc容器启动的时候,会调用其selectImports方法,往容器中添加组件

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import(AutoConfigurationImportSelector.class)
  7. public @interface EnableAutoConfiguration {}

AutoConfigurationImportSelector#selectImports

  1. @Override
  2. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  3. if (!isEnabled(annotationMetadata)) {
  4. return NO_IMPORTS;
  5. }
  6. AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
  7. .loadMetadata(this.beanClassLoader);
  8. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
  9. annotationMetadata);
  10. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  11. }

在 getAutoConfigurationEntry 中,进行关键的操作,

  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
  2. AnnotationMetadata annotationMetadata) {
  3. if (!isEnabled(annotationMetadata)) {
  4. return EMPTY_ENTRY;
  5. }
  6. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  7. // 在这,找寻候选的配置,实际上,读取resources文件夹下的spring.factories文件,进行自动装配
  8. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  9. configurations = removeDuplicates(configurations);
  10. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  11. checkExcludedClasses(configurations, exclusions);
  12. configurations.removeAll(exclusions);
  13. configurations = filter(configurations, autoConfigurationMetadata);
  14. fireAutoConfigurationImportEvents(configurations, exclusions);
  15. return new AutoConfigurationEntry(configurations, exclusions);
  16. }
  17. // getCandidateConfigurations,寻找的是,spring.factories中的 EnableAutoConfiguration配置
  18. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  19. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  20. getBeanClassLoader());
  21. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  22. + "are using a custom packaging, make sure that file is correct.");
  23. return configurations;
  24. }
  25. //
  26. protected Class<?> getSpringFactoriesLoaderFactoryClass() {
  27. return EnableAutoConfiguration.class;
  28. }

然后,执行完这个之后,自动装配就算完成了,会去读取所有导入的jar包中的spring.factories文件中的EnableAutoConfiguration

二、SpringApplication.run(SpringBootMainApplication.class, args);

run方法有多个重载,最终调用到的run方法如下,分为两步

  1. 创建SpringApplication对象
  2. 执行SpringApplication对象中的run方法

    1. public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    2. return new SpringApplication(primarySources).run(args);
    3. }

    new SpringApplication(primarySources)

    1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    2. // 设置资源加载器,默认为null,不管
    3. this.resourceLoader = resourceLoader;
    4. Assert.notNull(primarySources, "PrimarySources must not be null");
    5. // 把主启动类保存到SpringApplication对象中
    6. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    7. // 判断当前容器的环境状态,看是否是web环境/普通的Java环境
    8. this.webApplicationType = WebApplicationType.deduceFromClasspath();
    9. // 加载spring.factories中的 ApplicationContextInitializer
    10. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    11. // 加载spring.facotries中的 ApplicationListener
    12. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    13. // 找到main方法所在类,没有的话,返回null
    14. this.mainApplicationClass = deduceMainApplicationClass();
    15. }

    WebApplicationType.deduceFromClasspath();

    1. static WebApplicationType deduceFromClasspath() {
    2. // 判断是否有webflux环境,如果是,返回reactive模式
    3. if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
    4. && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
    5. return WebApplicationType.REACTIVE;
    6. }
    7. // SERVLET_INDICATOR_CLASSES中包含 Servlet/ConfigurableWebApplicationContext两个元素
    8. // 如果但凡导入了一个不存在,说明不是servlet环境,那就是一个普通Java环境
    9. for (String className : SERVLET_INDICATOR_CLASSES) {
    10. if (!ClassUtils.isPresent(className, null)) {
    11. return WebApplicationType.NONE;
    12. }
    13. }
    14. // 否则,就是一个web环境,需要创建web版的spring容器
    15. return WebApplicationType.SERVLET;
    16. }

    deduceMainApplicationClass();

    1. private Class<?> deduceMainApplicationClass() {
    2. try {
    3. StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
    4. for (StackTraceElement stackTraceElement : stackTrace) {
    5. if ("main".equals(stackTraceElement.getMethodName())) {
    6. return Class.forName(stackTraceElement.getClassName());
    7. }
    8. }
    9. }
    10. catch (ClassNotFoundException ex) {
    11. // Swallow and continue
    12. }
    13. return null;
    14. }

    run(args)

    1. public ConfigurableApplicationContext run(String... args) {
    2. StopWatch stopWatch = new StopWatch();
    3. stopWatch.start();
    4. ConfigurableApplicationContext context = null;
    5. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    6. configureHeadlessProperty();
    7. // 获取spring.factories中配置的 SpringApplicationRunListeners
    8. SpringApplicationRunListeners listeners = getRunListeners(args);
    9. listeners.starting();
    10. try {
    11. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    12. // 预准备环境
    13. ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    14. configureIgnoreBeanInfo(environment);
    15. // 打印logo
    16. Banner printedBanner = printBanner(environment);
    17. // 创建ioc容器
    18. context = createApplicationContext();
    19. exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    20. new Class[] { ConfigurableApplicationContext.class }, context);
    21. // 准备上下文
    22. prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    23. // 刷新上下文
    24. refreshContext(context);
    25. // 刷新上下文之后的处理工作
    26. afterRefresh(context, applicationArguments);
    27. stopWatch.stop();
    28. if (this.logStartupInfo) {
    29. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    30. }
    31. // 发布一个事件,ioc容器启动完成
    32. listeners.started(context);
    33. // 调用Runner的run方法
    34. callRunners(context, applicationArguments);
    35. }
    36. catch (Throwable ex) {
    37. handleRunFailure(context, ex, exceptionReporters, listeners);
    38. throw new IllegalStateException(ex);
    39. }
    40. try {
    41. // 发布一个事件,容器已经正在运行
    42. listeners.running(context);
    43. }
    44. catch (Throwable ex) {
    45. handleRunFailure(context, ex, exceptionReporters, null);
    46. throw new IllegalStateException(ex);
    47. }
    48. return context;
    49. }

    prepareEnvironment(listeners, applicationArguments); 预准备环境

    1. private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    2. ApplicationArguments applicationArguments) {
    3. // Create and configure the environment
    4. ConfigurableEnvironment environment = getOrCreateEnvironment();
    5. configureEnvironment(environment, applicationArguments.getSourceArgs());
    6. ConfigurationPropertySources.attach(environment);
    7. // 发布容器准备完成的事件
    8. listeners.environmentPrepared(environment);
    9. // 把environment绑定到SpringApplication对象中
    10. bindToSpringApplication(environment);
    11. if (!this.isCustomEnvironment) {
    12. environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
    13. deduceEnvironmentClass());
    14. }
    15. ConfigurationPropertySources.attach(environment);
    16. return environment;
    17. }

    context = createApplicationContext();

    1. protected ConfigurableApplicationContext createApplicationContext() {
    2. Class<?> contextClass = this.applicationContextClass;
    3. if (contextClass == null) {
    4. try {
    5. // 根据当前的环境,判断创建哪个ApplicationContext
    6. switch (this.webApplicationType) {
    7. case SERVLET:
    8. // mvc AnnotationConfigServletWebServerApplicationContext
    9. contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
    10. break;
    11. case REACTIVE:
    12. // webflux AnnotationConfigReactiveWebServerApplicationContext
    13. contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
    14. break;
    15. default:
    16. // 默认普通Java AnnotationConfigApplicationContext
    17. contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
    18. }
    19. }
    20. catch (ClassNotFoundException ex) {
    21. throw new IllegalStateException(
    22. "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
    23. }
    24. }
    25. return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    26. }

    prepareContext(context, environment, listeners, applicationArguments, printedBanner);

    1. private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    2. SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    3. // 设置 environment
    4. context.setEnvironment(environment);
    5. // 对容器进行后置处理,注册 beanNameGenerator/resourceLoader等
    6. postProcessApplicationContext(context);
    7. // 调用所有的ApplicationContextInitializer的initialize方法,进行初始化
    8. applyInitializers(context);
    9. // 发布容器准备完成事件
    10. listeners.contextPrepared(context);
    11. if (this.logStartupInfo) {
    12. logStartupInfo(context.getParent() == null);
    13. logStartupProfileInfo(context);
    14. }
    15. // Add boot specific singleton beans
    16. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    17. beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    18. if (printedBanner != null) {
    19. beanFactory.registerSingleton("springBootBanner", printedBanner);
    20. }
    21. if (beanFactory instanceof DefaultListableBeanFactory) {
    22. ((DefaultListableBeanFactory) beanFactory)
    23. .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    24. }
    25. if (this.lazyInitialization) {
    26. context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    27. }
    28. // Load the sources
    29. Set<Object> sources = getAllSources();
    30. Assert.notEmpty(sources, "Sources must not be empty");
    31. load(context, sources.toArray(new Object[0]));
    32. // 发布容器加载完成事件
    33. listeners.contextLoaded(context);
    34. }

    refreshContext(context)

    在这调用ioc的刷新方法,执行refresh的12步,其中有一步为onRefresh方法,在mvc环境中,重写了onRefresh方法

    1. private void refreshContext(ConfigurableApplicationContext context) {
    2. // 调用ApplicationContext的refresh方法,ioc刷新方法
    3. refresh(context);
    4. if (this.registerShutdownHook) {
    5. try {
    6. // 注册hook
    7. context.registerShutdownHook();
    8. }
    9. catch (AccessControlException ex) {
    10. // Not allowed in some environments.
    11. }
    12. }
    13. }

    在这,createWebServer,创建tomcat

    1. @Override
    2. protected void onRefresh() {
    3. super.onRefresh();
    4. try {
    5. createWebServer();
    6. }
    7. catch (Throwable ex) {
    8. throw new ApplicationContextException("Unable to start web server", ex);
    9. }
    10. }
    11. // createWebServer
    12. private void createWebServer() {
    13. WebServer webServer = this.webServer;
    14. ServletContext servletContext = getServletContext();
    15. if (webServer == null && servletContext == null) {
    16. ServletWebServerFactory factory = getWebServerFactory();
    17. //调用Servelet工厂获取WebServer服务
    18. this.webServer = factory.getWebServer(getSelfInitializer());
    19. }
    20. else if (servletContext != null) {
    21. try {
    22. getSelfInitializer().onStartup(servletContext);
    23. }
    24. catch (ServletException ex) {
    25. throw new ApplicationContextException("Cannot initialize servlet context", ex);
    26. }
    27. }
    28. initPropertySources();
    29. }
    30. // 以tomcat为例的话,就创建出一个tomcat对象,
    31. @Override
    32. public WebServer getWebServer(ServletContextInitializer... initializers) {
    33. if (this.disableMBeanRegistry) {
    34. Registry.disableRegistry();
    35. }
    36. Tomcat tomcat = new Tomcat();
    37. File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    38. tomcat.setBaseDir(baseDir.getAbsolutePath());
    39. Connector connector = new Connector(this.protocol);
    40. connector.setThrowOnFailure(true);
    41. tomcat.getService().addConnector(connector);
    42. customizeConnector(connector);
    43. tomcat.setConnector(connector);
    44. tomcat.getHost().setAutoDeploy(false);
    45. configureEngine(tomcat.getEngine());
    46. for (Connector additionalConnector : this.additionalTomcatConnectors) {
    47. tomcat.getService().addConnector(additionalConnector);
    48. }
    49. prepareContext(tomcat.getHost(), initializers);
    50. // 在这,就执行初始化,监听端口等
    51. return getTomcatWebServer(tomcat);
    52. }

    SpringBoot的大致启动流程至此结束