由于该系统是底层系统,以微服务形式对外暴露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方法中也是可以被获取到的,不要急我们继续向下分析