SpringBoot启动流程解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: SpringBoot启动流程解析

由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图keepRunning方法)。


 本文以调试一个实际的SpringBoot启动程序为例,参考流程中主要类类图,来分析其启动逻辑和自动化配置原理。

总览:

 上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心,在后面的分析中会详细讨论。在下面的启动程序中我们会串联起结构中的主要功能。

启动:

 每个SpringBoot程序都有一个主入口,也就是main方法,main里面调用SpringApplication.run()启动整个spring-boot程序,该方法所在类需要使用@SpringBootApplication注解,以及@ImportResource注解(if need),@SpringBootApplication包括三个注解,功能如下:@EnableAutoConfiguration:SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置


@SpringBootConfiguration(内部为@Configuration):被标注的类等于在spring的XML配置文件中(applicationContext.xml),装配所有bean事务,提供了一个spring的上下文环境


@ComponentScan:组件扫描,可自动发现和装配Bean,默认扫描SpringApplication的run方法里的Booter.class所在的包路径下文件,所以最好将该启动类放到根包路径下

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

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

// 构建SpringApplication
  public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 探测webApplicationType 类型,根据是否出现"javax.servlet.Servlet",
    // "org.springframework.web.context.ConfigurableWebApplicationContext"
    // 等判断
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // getSpringFactoriesInstances => SpringFactoriesLoader.loadFactoryNames() => loadSpringFactories(),解释见下面
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
  }

SpringFactoriesLoader# loadSpringFactories()会扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。

  private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
      return result;
    }

    try {
      Enumeration<URL> urls = (classLoader != null ?
          classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
          ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      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();
          for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
            result.add(factoryTypeName, factoryImplementationName.trim());
          }
        }
      }
      cache.put(classLoader, result);
      return result;
    }
    catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
          FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
  }

result中的值为:

getSpringFactoriesInstances(ApplicationContextInitializer.class)和getSpringFactoriesInstances(ApplicationListener.class))都是从刚才扫描结果获取到相应的值。

getSpringFactoriesInstances(ApplicationContextInitializer.class)的值:

getSpringFactoriesInstances(ApplicationListener.class))的值

   接下来回到SpringApplication# run()

  public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
    }

    try {
      listeners.running(context);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
    }
    return context;
  }

该方法中实现了如下几个关键步骤:

1.创建了应用的监听器SpringApplicationRunListeners并开始监听

2.prepareEnvironment()加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment,类图如下

可以看出,*Environment最终都实现了PropertyResolver接口,我们平时通过environment对象获取配置文件中指定Key对应的value方法时,就是调用了propertyResolver接口的getProperty方法


3.配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)


4.创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文),我们可以看一下创建方法:

  protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
      try {
        switch (this.webApplicationType) {
        case SERVLET:
          contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
          break;
        case REACTIVE:
          contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
          break;
        default:
          contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
        }
      }
      catch (ClassNotFoundException ex) {
        throw new IllegalStateException(
            "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
      }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  }

方法会先获取显式设置的应用上下文(applicationContextClass),如果不存在,再加载默认的环境配置(通过是否是web environment判断),默认选择AnnotationConfigApplicationContext注解上下文(通过扫描所有注解类来加载bean),最后通过BeanUtils实例化上下文对象,并返回,ConfigurableApplicationContext类图如下:

主要看其继承的两个方向:

LifeCycle:生命周期类,定义了start启动、stop结束、isRunning是否运行中等生命周期空值方法

ApplicationContext:应用上下文类,其主要继承了beanFactory(bean的工厂类)

   接下来继续回到SpringApplication# run()中,先看prepareContext()方法

  public ConfigurableApplicationContext run(String... args) {
    ...
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
    ... 
    return context;
  }

prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联,同时会执行applyInitializers(context);

protected void applyInitializers(ConfigurableApplicationContext context) {
    // getInitializers()的值是在上文中setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));设置进入的
    for (ApplicationContextInitializer initializer : getInitializers()) {
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
          ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      initializer.initialize(context);
    }
  }

接下来的refreshContext(context)

refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作。

其调用顺序:refreshContext(context) => refresh(context) => ((AbstractApplicationContext) applicationContext).refresh()

refresh方法配置结束后,Springboot做了一些基本的收尾工作,返回了应用环境上下文。回顾整体流程,Springboot的启动,主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext),并基于以上条件,在容器中开始实例化我们需要的Bean,至此,通过SpringBoot启动的程序已经构造完成,接下来我们来探讨自动化配置是如何实现。

总结下springboot启动流程

根据配置信息决定创建什么类型的applicationContext(AnnotationConfigServletWebServerApplicationContext)

=> 然后刷新 refresh(), 刷新主要做了以下几件事:

1、做整备工作

2、invokeBeanFactoryPostProcessors执行BeanFactoryPostProcessor的方法,其中一步是执行ConfigurationClassPostProcessor

3、ConfigurationClassPostProcessor是一个BeanDefinitionRegistryPostProcessor**

**它会解析完成所有的@Configuration配置类,然后所有@Bean、@ComponentScan等等Bean定义都会搜集进来了。它解析的对象(beans),要么从xml解析出来的(包含扫描),要么(springboot)是 @SpringBootApplication 扫描出来的。

4、registerBeanPostProcessors注册BeanPostProcessor(Bean的后置处理器)

5、finishBeanFactoryInitialization(beanFactory);初始化所有剩下的单实例bean

附几张数据

BeanDefinition

0 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
1 = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"
2 = "org.springframework.context.annotation.internalCommonAnnotationProcessor"
3 = "org.springframework.context.event.internalEventListenerProcessor"
4 = "org.springframework.context.event.internalEventListenerFactory"
5 = "demoApplication"
6 = "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"
7 = "anotherConfig"
8 = "appConfig"
9 = "thirdConfig"
10 = "errorController"
11 = "helloController"
12 = "myExceptionHandler"
13 = "helloFilter"
14 = "anotherBean"
15 = "myBean"
16 = "thirdBean"
17 = "org.springframework.boot.autoconfigure.AutoConfigurationPackages"
18 = "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration"
19 = "propertySourcesPlaceholderConfigurer"
20 = "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration"
21 = "websocketServletWebServerCustomizer"
22 = "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration"
23 = "org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat"
24 = "tomcatServletWebServerFactory"
25 = "org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration"
26 = "servletWebServerFactoryCustomizer"
27 = "tomcatServletWebServerFactoryCustomizer"
28 = "org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor"
29 = "org.springframework.boot.context.internalConfigurationPropertiesBinderFactory"
30 = "org.springframework.boot.context.internalConfigurationPropertiesBinder"
31 = "org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator"
32 = "org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata"
33 = "server-org.springframework.boot.autoconfigure.web.ServerProperties"
34 = "webServerFactoryCustomizerBeanPostProcessor"
35 = "errorPageRegistrarBeanPostProcessor"
36 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration"
37 = "dispatcherServlet"
38 = "spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties"
39 = "spring.http-org.springframework.boot.autoconfigure.http.HttpProperties"
40 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration"
41 = "dispatcherServletRegistration"
42 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration"
43 = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration"
44 = "taskExecutorBuilder"
45 = "applicationTaskExecutor"
46 = "spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties"
47 = "org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration"
48 = "defaultValidator"
49 = "methodValidationPostProcessor"
50 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration"
51 = "error"
52 = "beanNameViewResolver"
53 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration"
54 = "conventionErrorViewResolver"
55 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration"
56 = "errorAttributes"
57 = "basicErrorController"
58 = "errorPageCustomizer"
59 = "preserveErrorControllerTargetClassPostProcessor"
60 = "spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties"
61 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration"
62 = "requestMappingHandlerAdapter"
63 = "requestMappingHandlerMapping"
64 = "welcomePageHandlerMapping"
65 = "mvcConversionService"
66 = "mvcValidator"
67 = "mvcContentNegotiationManager"
68 = "mvcPathMatcher"
69 = "mvcUrlPathHelper"
70 = "viewControllerHandlerMapping"
71 = "beanNameHandlerMapping"
72 = "routerFunctionMapping"
73 = "resourceHandlerMapping"
74 = "mvcResourceUrlProvider"
75 = "defaultServletHandlerMapping"
76 = "handlerFunctionAdapter"
77 = "mvcUriComponentsContributor"
78 = "httpRequestHandlerAdapter"
79 = "simpleControllerHandlerAdapter"
80 = "handlerExceptionResolver"
81 = "mvcViewResolver"
82 = "mvcHandlerMappingIntrospector"
83 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter"
84 = "defaultViewResolver"
85 = "viewResolver"
86 = "requestContextFilter"
87 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration"
88 = "formContentFilter"
89 = "org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration"
90 = "mbeanExporter"
91 = "objectNamingStrategy"
92 = "mbeanServer"
93 = "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration"
94 = "springApplicationAdminRegistrar"
95 = "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration"
96 = "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration"
97 = "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration"
98 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration"
99 = "standardJacksonObjectMapperBuilderCustomizer"
100 = "spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties"
101 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration"
102 = "jacksonObjectMapperBuilder"
103 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration"
104 = "parameterNamesModule"
105 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration"
106 = "jacksonObjectMapper"
107 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration"
108 = "jsonComponentModule"
109 = "org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration"
110 = "stringHttpMessageConverter"
111 = "org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration"
112 = "mappingJackson2HttpMessageConverter"
113 = "org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration"
114 = "org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration"
115 = "messageConverters"
116 = "org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration"
117 = "spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties"
118 = "org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration"
119 = "spring.security.oauth2.resourceserver-org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties"
120 = "org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration"
121 = "taskSchedulerBuilder"
122 = "spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties"
123 = "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafJava8TimeDialect"
124 = "java8TimeDialect"
125 = "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebMvcConfiguration$ThymeleafViewResolverConfiguration"
126 = "thymeleafViewResolver"
127 = "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebMvcConfiguration"
128 = "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafDefaultConfiguration"
129 = "templateEngine"
130 = "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$DefaultTemplateResolverConfiguration"
131 = "defaultTemplateResolver"
132 = "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration"
133 = "spring.thymeleaf-org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties"
134 = "org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration"
135 = "restTemplateBuilder"
136 = "org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration"
137 = "tomcatWebServerFactoryCustomizer"
138 = "org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration"
139 = "org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration"
140 = "characterEncodingFilter"
141 = "localeCharsetMappingsCustomizer"
142 = "org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration"
143 = "multipartConfigElement"
144 = "multipartResolver"
145 = "spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties"

Singleton

"characterEncodingFilter" -> {OrderedCharacterEncodingFilter@6077} 
"org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration" -> {MultipartAutoConfiguration@6079} 
"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration" -> {DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration@6081} 
"preserveErrorControllerTargetClassPostProcessor" -> {ErrorMvcAutoConfiguration$PreserveErrorControllerTargetClassPostProcessor@6083} 
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor" -> {ConfigurationClassPostProcessor@6084} 
"propertySourcesPlaceholderConfigurer" -> {PropertySourcesPlaceholderConfigurer@6086} 
"springApplicationArguments" -> {DefaultApplicationArguments@6088} 
"contextAttributes" -> {Collections$UnmodifiableMap@6090}  size = 7
"methodValidationPostProcessor" -> {MethodValidationPostProcessor@6091} "proxyTargetClass=true; optimize=false; opaque=false; exposeProxy=false; frozen=false"
"org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration" -> {EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration@6093} 
"tomcatServletWebServerFactoryCustomizer" -> {TomcatServletWebServerFactoryCustomizer@6095} 
"server-org.springframework.boot.autoconfigure.web.ServerProperties" -> {ServerProperties@6097} 
"websocketServletWebServerCustomizer" -> {TomcatWebSocketServletWebServerCustomizer@6099} 
"autoConfigurationReport" -> {ConditionEvaluationReport@6101} 
"org.springframework.context.event.internalEventListenerFactory" -> {DefaultEventListenerFactory@6103} 
"servletWebServerFactoryCustomizer" -> {ServletWebServerFactoryCustomizer@6105} 
"org.springframework.boot.context.internalConfigurationPropertiesBinder" -> {ConfigurationPropertiesBinder@6107} 
"webServerFactoryCustomizerBeanPostProcessor" -> {WebServerFactoryCustomizerBeanPostProcessor@6108} 
"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration" -> {ServletWebServerFactoryAutoConfiguration@6110} 
"systemEnvironment" -> {Collections$UnmodifiableMap@6112}  size = 45
"org.springframework.context.event.internalEventListenerProcessor" -> {EventListenerMethodProcessor@6113} 
"spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties" -> {WebMvcProperties@6115} 
"localeCharsetMappingsCustomizer" -> {HttpEncodingAutoConfiguration$LocaleCharsetMappingsCustomizer@6117} 
"multipartConfigElement" -> {MultipartConfigElement@6119} 
"formContentFilter" -> {OrderedFormContentFilter@6121} 
"requestContextFilter" -> {OrderedRequestContextFilter@6123} 
"org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator" -> {ConfigurationPropertiesBeanDefinitionValidator@6125} 
"org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration" -> {HttpEncodingAutoConfiguration@6127} 
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor" -> {AutowiredAnnotationBeanPostProcessor@6128} 
"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry" -> {ConfigurationClassParser$ImportStack@6130}  size = 0
"org.springframework.boot.context.ContextIdApplicationContextInitializer$ContextId" -> {ContextIdApplicationContextInitializer$ContextId@6132} 
"org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory" -> {SharedMetadataReaderFactoryContextInitializer$SharedMetadataReaderFactoryBean@5793} 
"applicationEventMulticaster" -> {SimpleApplicationEventMulticaster@5139} 
"environment" -> {StandardServletEnvironment@4853} "StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, StubPropertySource {name='servletConfigInitParams'}, ServletContextPropertySource {name='servletContextInitParams'}, PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}]}"
"spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties" -> {MultipartProperties@6137} 
"org.springframework.context.annotation.internalCommonAnnotationProcessor" -> {CommonAnnotationBeanPostProcessor@6138} 
"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat" -> {ServletWebServerFactoryConfiguration$EmbeddedTomcat@6140} 
"spring.http-org.springframework.boot.autoconfigure.http.HttpProperties" -> {HttpProperties@6142} 
"springBootLoggingSystem" -> {LogbackLoggingSystem@6144} 
"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration" -> {ErrorMvcAutoConfiguration@6146} 
"org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration" -> {WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration@6148} 
"org.springframework.boot.context.internalConfigurationPropertiesBinderFactory" -> {ConfigurationPropertiesBinder$Factory@6150} 
"servletContext" -> {ApplicationContextFacade@5736} 
"dispatcherServlet" -> {DispatcherServlet@6153} 
"org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration" -> {WebMvcAutoConfiguration@6155} 
"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor" -> {ConfigurationPropertiesBindingPostProcessor@6156} 
"contextParameters" -> {Collections$UnmodifiableMap@6158}  size = 0
"systemProperties" -> {Properties@6160}  size = 67
"errorPageRegistrarBeanPostProcessor" -> {ErrorPageRegistrarBeanPostProcessor@6161} 
"errorPageCustomizer" -> {ErrorMvcAutoConfiguration$ErrorPageCustomizer@6163} 
"tomcatWebServerFactoryCustomizer" -> {TomcatWebServerFactoryCustomizer@6165} 
"springBootBanner" -> {SpringApplicationBannerPrinter$PrintedBanner@6167} 
"dispatcherServletRegistration" -> {DispatcherServletRegistrationBean@6169} "dispatcherServlet urls=[/]"
"tomcatServletWebServerFactory" -> {TomcatServletWebServerFactory@6171} 
"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration" -> {DispatcherServletAutoConfiguration$DispatcherServletConfiguration@6173} 
"messageSource" -> {DelegatingMessageSource@5032} "Empty MessageSource"
"helloFilter" -> {HelloFilter@6175} 
"springBootLoggerGroups" -> {LoggerGroups@6177} 

自动化配置:

  之前的启动结构图中,我们注意到无论是应用初始化还是具体的执行过程,都调用了SpringBoot自动配置模块

SpringBoot自动配置模块

 该配置模块的主要使用到了SpringFactoriesLoader,即Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader,即需要传入上图中的工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件,传入的工厂类为接口,而文件中对应的类则是接口的实现类,或最终作为实现类,所以文件中一般为如下图这种一对多的类名集合,获取到这些实现类的类名后,loadFactoryNames方法返回类名集合,方法调用方得到这些集合后,再通过反射获取这些类的类对象、构造方法,最终生成实例

工厂接口与其若干实现类接口名称

下图有助于我们形象理解自动配置流程

SpringBoot自动化配置关键组件关系图

 mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。

之前我们提到了EnableAutoConfiguration注解,其类图如下

可以发现其最终实现了ImportSelector(选择器)和BeanClassLoaderAware(bean类加载器中间件),重点关注一下AutoConfigurationImportSelector#selectImports()方法

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
    }
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
        .loadMetadata(this.beanClassLoader);
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
        annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  }

该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表。我们知道,如果获取到类信息,spring自然可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组建的类信息在select方法中也是可以被获取到的,不要急我们继续向下分析

目录
相关文章
|
3月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
294 0
|
26天前
|
监控 安全 开发工具
鸿蒙HarmonyOS应用开发 | HarmonyOS Next-从应用开发到上架全流程解析
HarmonyOS Next是华为推出的最新版本鸿蒙操作系统,强调多设备协同和分布式技术,提供丰富的开发工具和API接口。本文详细解析了从应用开发到上架的全流程,包括环境搭建、应用设计与开发、多设备适配、测试调试、应用上架及推广等环节,并介绍了鸿蒙原生应用开发者激励计划,帮助开发者更好地融入鸿蒙生态。通过DevEco Studio集成开发环境和华为提供的多种支持工具,开发者可以轻松创建并发布高质量的鸿蒙应用,享受技术和市场推广的双重支持。
292 11
|
3月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
288 2
|
2天前
|
存储 Java 数据安全/隐私保护
SpringBoot整合Flowable【03】- 通过Flowable-UI体验一个简单流程
本文介绍了如何使用Flowable 7.0以下版本的flowable-ui进行流程建模、发布和执行。首先,通过解压并启动flowable-ui war包,访问http://localhost:8080/flowable-ui/idm/#/login登录系统。接着,创建并绘制一个简单的绩效流程模型,包含开始节点、任务节点(自评、上级评、隔级评)和结束节点,并为各节点分配处理人。然后,创建应用并发布绩效流程。最后,通过创建a、b、c三个用户分别完成各节点任务,演示了整个流程的执行过程。本文旨在帮助读者理解Flowable的基本操作和流程元素,后续将介绍通过Java代码控制流程的方法。
21 0
SpringBoot整合Flowable【03】- 通过Flowable-UI体验一个简单流程
|
2天前
|
XML 前端开发 Java
SpringBoot整合Flowable【04】- 通过代码控制流程流转
本文介绍了如何使用Flowable的Java API控制流程流转,基于前文构建的绩效流程模型。首先,通过Flowable-UI导出模型文件并部署到Spring Boot项目中。接着,详细讲解了如何通过代码部署、启动和审批流程,涉及`RepositoryService`、`RuntimeService`和`TaskService`等核心服务类的使用。最后,通过实际操作演示了流程从部署到完成的全过程,并简要说明了相关数据库表的变化。本文帮助读者初步掌握Flowable在实际业务中的应用,后续将深入探讨更多高级功能。
17 0
|
30天前
|
域名解析 弹性计算 安全
阿里云服务器租用、注册域名、备案及域名解析完整流程参考(图文教程)
对于很多初次建站的用户来说,选购云服务器和注册应及备案和域名解析步骤必须了解的,目前轻量云服务器2核2G68元一年,2核4G4M服务器298元一年,域名注册方面,阿里云推出域名1元购买活动,新用户注册com和cn域名2年首年仅需0元,xyz和top等域名首年仅需1元。对于建站的用户来说,购买完云服务器并注册好域名之后,下一步还需要操作备案和域名绑定。本文为大家展示阿里云服务器的购买流程,域名注册、绑定以及备案的完整流程,全文以图文教程形式为大家展示具体细节及注意事项,以供新手用户参考。
|
2月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
62 12
|
2月前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
91 2
|
3月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
58 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
3月前
|
JavaScript 前端开发 UED
Vue执行流程及渲染解析
【10月更文挑战第5天】

推荐镜像

更多