通常我们再使用 Spring Boot 的时候,这样简单的一段代码就实现了 Spring Boot 的启动,那么 Spring Boot 到底是如何实现的呢?
@SpringBootApplicationpublic class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}
@SpringBootApplication
我们先看 @SpringBootApplication 隐藏了那些细节
@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 的注解,的确他就是集成了 @Configuration 注解,那么就好办了,我们所定义的 App.class 其实也是一个@Configuration 的注解的 bean
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {}
@ComponentScan
这个注解不陌生了,就是定义包扫描路径,这里默认会扫描当前 App.class 所在的路径与其子包的所有 bean,但是我们并没有发现他定义了某个扫描路径,只是排除了一些 filter
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@EnableAutoConfiguration
我们发现这个注解通过 @Import 注册了一个 AutoConfigurationImportSelector.class 的 类
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {}
AutoConfigurationImportSelector.class 又实现了 ImportSelector 接口,而 ImportSelector 接口会通过 selectImports 方法返回一个 String[] 数组,这个数组里面包含类的全限定名,并注册到 Spring 容器中去
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 核心方法AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}
我们可以看出来核心方法是 getAutoConfigurationEntry(annotationMetadata); 会返回一个配置信息
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);// 核心方法,从 spring.factory 中读取自动装配的信息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);}
获取候选的自动装配配置信息 getCandidateConfigurations(annotationMetadata, attributes); ,这里会返回 META-INF/spring.factories 中的所有信息作为候选对象,这里也是 Spring SPI 的体现
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations =SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;}
通过 getSpringFactoriesLoaderFactoryClass() 获取现在要加载的 FactoryClass,也就是 spring.factories 文件中的 key 对应的值
protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;}
所以这里要加载的就是 EnableAutoConfiguration.class 的全限定名下的所有类
# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\...
@AutoConfigurationPackage
再跟到这个注解中,我们一样发现了 @Import 注册了一个 AutoConfigurationPackages.Registrar.class 的类,这个类实现了ImportBeanDefinitionRegistrar 接口,所以回调用 registerBeanDefinitions 方法来手工注册 bean
我们看一下 AutoConfigurationPackages.Registrar.class 的 registerBeanDefinitions 方法到底做了些什么?
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}
我们可以看到它通过 new PackageImports(metadata).getPackageNames().toArray(new String[0]) 来获取 packageName,而 packageName 如果我们不手工提供 @ComponentScan 注解的话,那么默认的 packageName 是如何来的呢,我们继续往下看
private static final class PackageImports {private final List<String> packageNames;PackageImports(AnnotationMetadata metadata) {AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));List<String> packageNames = new ArrayList<>();for (String basePackage : attributes.getStringArray("basePackages")) {packageNames.add(basePackage);}for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {packageNames.add(basePackageClass.getPackage().getName());}if (packageNames.isEmpty()) {packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));}this.packageNames = Collections.unmodifiableList(packageNames);}}
我们可以看到,首先先找自定义的路径,最后如果没有找到的话,会使用当前 App.class 所在的根路径,这也就是为什么我们可以不定义 @ComponentScan ,那么默认的 packageName 就是当前 App.class 所在的同级目录及子目录的原因
SpringApplication.run
我们再看 SpringApplication.run(App.class, args); 这段代码,这段代码往下跟
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");// 将 App.class 赋值给了 this.primarySourcesthis.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 此时我们把 App.class 传递给了一个类变量 primarySources
我们继续跟 run 方法,其中有一个 prepareContext 方法中,会通过 load 来加载 App.class
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);postProcessApplicationContext(context);applyInitializers(context);listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// Load the sources// 从 primarySources 中读取 App.classSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);}
然后调用 load 方法来加载类
private int load(Object source) {Assert.notNull(source, "Source must not be null");if (source instanceof Class<?>) {return load((Class<?>) source);}if (source instanceof Resource) {return load((Resource) source);}if (source instanceof Package) {return load((Package) source);}if (source instanceof CharSequence) {return load((CharSequence) source);}throw new IllegalArgumentException("Invalid source type " + source.getClass());}
其底层就是将 App.class,注册到 Spring 容器中去
private int load(Class<?> source) {if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {// Any GroovyLoaders added in beans{} DSL can contribute beans hereGroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);load(loader);}if (isEligible(source)) {// 将 App.class 注册到 Spring 容器中去this.annotatedReader.register(source);return 1;}return 0;}
