SpringBoot自动配置源码分析


1.Springboot 启动注解分析

springboot启动类

//SpringBootApplication注解用来标注一个主程序类,说明是一个springboot应用
@SpringBootApplication
public class DemoApplication {

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

查看@SpringBootApplication 注解:

/**
@ComponentScan:自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中

@SpringBootConfiguration:标注在某个类上,表示这是一个springboot的配置类。

@EnableAutoConfiguration:开启自动配置功能,之前在使用springboot的时候,springboot可以自动帮我们完成配置功能,@EnableAutoConfiguration告诉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 {
//
}

查看@EnableAutoConfiguration 注解:

/*
@AutoConfigurationPackage:自动配置包

@Import(AutoConfigurationImportSelector.class):导入哪些组件的选择器,它将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中,它会给容器中导入非常多的自动配置类,就是给容器中导入这个场景需要的所有组件,并配置好这些组件
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//
}

查看@AutoConfigurationPackage 注解:

/*
给容器导入一个组件,导入的组件由AutoConfigurationPackages.Registrar.class将主配置类(@SpringBootApplication标注的类)的所在包及包下面所有子包里面的所有组件扫描到spring容器
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    //
}

2.自动装配源码解析

org.springframework.boot.SpringApplication#run(java.lang.String...)

   /**
     * 运行Spring application, 创建并且更新ApplicationContext.
     * @param args 命令行参数
     * @return ApplicationContext
     */
public ConfigurableApplicationContext run(String... args) {
    // 用来记录当前springboot启动耗时
   StopWatch stopWatch = new StopWatch();
   // 记录启动开始时间
   stopWatch.start();
   // spring上下文的接口, 接收ApplicationContext实现
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   // 通过spring.factroies中读取了SpringApplicationRunListener 的组件,用来发布事件或者运行监听器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 触发ApplicationStartingEvent的监听者执行onApplicationEvent
   listeners.starting();
   try {
       // 根据命令行参数 实例化一个ApplicationArguments 
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      // 忽略beaninfo的bean
      configureIgnoreBeanInfo(environment);
      // 打印Banner
      Banner printedBanner = printBanner(environment);
      // 根据webApplicationType创建Spring上下文
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
     // 准备应用上下文,该步骤包含一个非常关键的操作,将启动类注入容器,为后续开启自动化配置提供基础
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      // 加载spring ioc 容器   **相当重要   由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
      //  加载自动配置类:invokeBeanFactoryPostProcessors ,  创建servlet容器onRefresh
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      // 发布ApplicationStartedEvent事件
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      // 发布ApplicationReadyEvent事件
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry

 /**
    * 获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费
     */
    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        // 获取是否有配置spring.boot.enableautoconfiguration属性,默认返回true
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        // 获得@Congiguration标注的Configuration类即被审视introspectedClass的注解数据,
        // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
        // 将会获取到exclude =和excludeName=""的注解数据
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 得到spring.factories文件配置的所有自动配置类
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        // 利用LinkedHashSet移除重复的配置类
        configurations = removeDuplicates(configurations);
        // 得到要排除的自动配置类,比如注解属性exclude的配置类
        // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
        // 将会获取到exclude = FreeMarkerAutoConfiguration.class的注解数据
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        // 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
        checkExcludedClasses(configurations, exclusions);
        // 将要排除的配置类移除
        configurations.removeAll(exclusions);
        // 因为从spring.factories文件获取的自动配置类太多,如果有些不必要的自动配置类都加载进内存,会造成内存浪费,因此这里需要进行过滤
        // 注意这里会调用AutoConfigurationImportFilter的match方法来判断是否符合@ConditionalOnBean,@ConditionalOnClass或@ConditionalOnWebApplication
        configurations = filter(configurations, autoConfigurationMetadata);
        // 获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent事件,
        // 目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
        // 该事件什么时候会被触发?--> 在刷新容器时调用invokeBeanFactoryPostProcessors后置处理器时触发
        fireAutoConfigurationImportEvents(configurations, exclusions);
        // 将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
        return new AutoConfigurationEntry(configurations, exclusions);
    }

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations

/**
将类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//将类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值取入集合中
        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;
    }

3.自定义自动配置类

1)新建一个springboot工程 xxx-spring-boot-starter 自定义配置类 建议命名
2)新建一个配置类 在配置类中创建一个Bean 对象

//必须声明为一个配置类
@Configuration
//可以定义配置生成条件
//@ConditionalOnWebApplication //web应该生效
@ConditionalOnJava(value = JavaVersion.THIRTEEN)
//可以引入自定义参数配置
@EnableConfigurationProperties(TestProperties.class)
public class TestServiceAutoConfiguration {

    @Autowired
    TestProperties testProperties;

    @Bean
    public TestService testService() {
        TestService service = new TestService();
        service.setTestProperties(testProperties);
        return service;
    }
}

参数配置类:

//定义参数配置前缀
@Data
@ConfigurationProperties(prefix = "demo")
public class TestProperties {
    private String prefix;
    private String suffix;
}

3)在 在resources目录下按照SpringBoot自动配置目录、文件名称的规则,如下图,新建一个自己的spring.factories文件。

并在 spring.factories文件 中添将自定义的自动配置类声明为名称为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,“\”的作用:换行,如果我们有多个自定义的自动配置类,这里可以用逗号分隔

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.isvmspace.demo.mystarter.TestServiceAutoConfiguration

自定义的自动配置类就创建好了

4)目标项目中只要引入对应自定义配置模块

        <!-- 引入自动配置模块 -->
        <dependency>
            <groupId>com.isvmspace.demo.mystarter</groupId>
            <artifactId>test-spring-boot-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

附录-常用注解:

@Conditional:自动配置类在一定条件下才能生效

注解 作用
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean
@ConditionalOnMissingBean 容器中不存在指定Bean
@ConditionalOnExpression 满足SpEL表达式
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项

声明:微默网|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - SpringBoot自动配置源码分析


不以物喜,不以己悲! 不忘初心,方得始终!