Spring Boot实现了 auto-configuration 自动配置,降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题。它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用。
启动类注解
整个启动的过程可以概括为:
- 读取所有依赖的META-INF/spring/%s%s.imports文件,该文件指明了哪些依赖可以被自动加载
- 根据importSelector类选择加载哪些依赖,使用conditionOn系列注解排除掉不需要的配置文件
- 将剩余的配置文件所代表的bean加载到IOC容器中
在spring-boot包中 spring.factories 文件定义了加载哪些类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| # PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader
# ConfigData Location Resolvers org.springframework.boot.context.config.ConfigDataLocationResolver=\ org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\ org.springframework.boot.context.config.StandardConfigDataLocationResolver
# ConfigData Loaders org.springframework.boot.context.config.ConfigDataLoader=\ org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\ org.springframework.boot.context.config.StandardConfigDataLoader
# Application Context Factories org.springframework.boot.ApplicationContextFactory=\ org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContextFactory,\ org.springframework.boot.web.servlet.context.ServletWebServerApplicationContextFactory
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
|
这此内容会被解析为Map<K, List>这种格式,键和值都是一个类的全限定名。整个SpringBoot的启动过程都是通过 @SpringBootApplication 注解和 SpringApplication.run 方法来实现的。
1 2 3 4 5 6 7 8 9 10 11 12
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { // 扫描路径设置 @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { ... }
|
几个重要注解说明:
- @SpringBootConfiguration:继承了Configuration,表示当前是注解类
- @EnableAutoConfiguration:开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助收集和注册特定场景相关的Bean定义
- @ComponentScan:扫描路径设置
@EnableAutoConfiguration
功能:从classpath中搜寻所有的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
1 2 3 4 5 6 7 8 9 10
| @SuppressWarnings("deprecation") @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... }
|
重要的注解与类:
- @AutoConfigurationPackage
- AutoConfigurationImportSelector.class
AutoConfigurationPackage
注册当前启动类的根package注册 org.springframework.boot.autoconfigure.AutoConfigurationPackages 的BeanDefinition。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { public static void register(BeanDefinitionRegistry registry, String... packageNames) { if (registry.containsBeanDefinition(BEAN)) { addBasePackages(registry.getBeanDefinition(BEAN), packageNames); } else { RootBeanDefinition beanDefinition = new RootBeanDefinition(BasePackages.class); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); addBasePackages(beanDefinition, packageNames); registry.registerBeanDefinition(BEAN, beanDefinition); } } }
|
最后通过 @Import(AutoConfigurationPackages.Registrar.class) 注册BeanDefinition到IOC窗口中:
1 2 3 4 5 6 7 8 9 10 11 12
| static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0])); }
@Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImports(metadata)); } }
|
AutoConfigurationImportSelector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; private ConfigurationClassFilter configurationClassFilter; @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } }
|
借助AutoConfigurationImportSelector,让SpringBoot应用将所有符合条件的bean定义都加载到当前SpringBoot创建并使用的IoC容器。

SpringApplication#run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
|
SpringApplication#run方法,首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
- 根据classpath里面是否存在某个特征类org.springframework.web.context. ConfigurableWebApplicationContext来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
- 推断并设置main方法的定义类。
SpringApplication实例初始化完成并且完成设置后,会开始执行run方法的逻辑。run方法中会初始化如下模块:
- SpringApplicationRunListeners:应用启动监听器模块
- ConfigurableEnvironment:配置环境模块
- ConfigurableApplicationContext:应用上下文模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public ConfigurableApplicationContext run(String... args) { Startup startup = Startup.create(); if (this.registerShutdownHook) { SpringApplication.shutdownHook.enableShutdownHookAddition(); }
DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); startup.started(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup); } listeners.started(context, startup.timeTakenToStarted()); callRunners(context, applicationArguments); } catch (Throwable ex) { throw handleRunFailure(context, ex, listeners); } try { if (context.isRunning()) { listeners.ready(context, startup.ready()); } } catch (Throwable ex) { throw handleRunFailure(context, ex, null); } return context; }
|
上面代码有几个关键步骤:
SpringApplicationRunListeners listeners = getRunListeners(args), 创建了应用的监听器SpringApplicationRunListeners并开始监听
- 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner)配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
refreshContext(context) 注册 ConfigurableApplicationContext context 到 spring ioc