@SpringbootApplication

这个注解标记的类为Springboot程序的主配置类,进入这个注解

@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** @EnableAutoConfiguration

根据字面意思为 Springboot配置 和 自动配置

@SpringBootConfiguration

进入这个注解

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {}

发现有@Configuration注解 该注解在Spring中也有接触就是表示一个配置类的意思 javaConfig来代替applicationContext.xml这种配置文件的

再进入@Configuration

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Configuration {}

发现有@Conponent注解 这是注解会被Spring自动装载成bean组件

所以可以知道@SpringbootApplication这个注解标记的类是一个Spring的配置类,且会被注册成bean

@EnableAutoConfiguration

使能够自动装配

进入该注解

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {}

有一个@AutoConfigurationPackage 和导入了一个类@Import(AutoConfigurationImportSelector.class)

自动配置包注解 自动配置导入选择器类

@AutoConfigurationPackage

进入该注解

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(AutoConfigurationPackages.Registrar.class)public @interface AutoConfigurationPackage {}

导入了一个AutoConfigurationPackages.Registrar.class

自动配置包类的静态内部类 注册器类 打个的断点debug

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {    //将注解元数据进行注册 getPackageName@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {register(registry, new PackageImport(metadata).getPackageName());}@Overridepublic Set determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImport(metadata));}}




springboot控制台程序不自动关闭 springboot启动自动关闭_List


可以发现已经能够获取到包名

说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。


springboot控制台程序不自动关闭 springboot启动自动关闭_List_02


@Import(AutoConfigurationImportSelector.class)

进入该类

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = filter(configurations, autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}

List configurations = getCandidateConfigurations(annotationMetadata, attributes);

可发现配置都通过这个方法获得

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List 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;}protected Class> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;}protected ClassLoader getBeanClassLoader() {return this.beanClassLoader;}//进入SpringFactoriesLoader.loadFactoryNames()方法public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}//进入  loadSpringFactories(classLoader)private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration urls = (classLoader != null ?                                                                          //public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";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);}}//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

通过ClassLoader加载使用了这个**@EnableAutoConfiguration**注解的类

这个方法会加载jar包中 META-INF/spring.factories 文件中配置的配置对象


springboot控制台程序不自动关闭 springboot启动自动关闭_List_03


SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。

spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:


springboot控制台程序不自动关闭 springboot启动自动关闭_List_04


接下来看看都导入了哪些组件被添加到容器中

selectImports方法

@Overridepublic 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());}//进入 getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata) 打个断点看看;

可以看见每个要导入的组件都以全类名的方法返回


springboot控制台程序不自动关闭 springboot启动自动关闭_List_05


@Conditional xxx

条件注解,通过判断类路径下有没有相应配置的 jar 包来确定是否加载和自动配置这个类。

具体几个@Conditon*注解的含义

@ConditionalOnBean : 仅仅在当前上下文中存在某个对象时,才会实例化一个Bean

@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效

@ConditionalOnClass : 某个class位于类路径上,才会实例化一个Bean),该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类

@ConditionalOnMissingClass : classpath中不存在该类时起效

@ConditionalOnExpression :当表达式为true的时候,才会实例化一个Bean

@ConditionalOnMissingBean :仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean,该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean,可以给该注解传入参数例如

@ConditionOnMissingBean(name = "example"),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行

@ConditionalOnMissingClass :某个class类路径上不存在的时候,才会实例化一个Bean

@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效

@ConditionalOnProperty : 参数设置或者值一致时起效

@ConditionalOnResource : 指定的文件存在时起效

@ConditionalOnJndi : 指定的JNDI存在时起效

@ConditionalOnJava : 指定的Java版本存在时起效

@ConditionalOnWebApplication : Web应用环境下起效

@ConditionalOnNotWebApplication : 非Web应用环境下起效

结论

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

SpringApplication

这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {    // ......    this.webApplicationType = WebApplicationType.deduceFromClasspath();    this.setInitializers(this.getSpringFactoriesInstances();    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));    this.mainApplicationClass = this.deduceMainApplicationClass();}

run 方法流程分析


springboot控制台程序不自动关闭 springboot启动自动关闭_spring_06