//依赖管理    org.springframework.boot    spring-boot-starter-parent    2.3.4.RELEASE//他的父项目 org.springframework.boot    spring-boot-dependencies    2.3.4.RELEASE  //在spring-boot-dependencies中几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制`

在pom文件中,我们可以见到很多 spring-boot-starter-*

只要引入starter,这个场景的所有常规需要的依赖我们都自动引入

*-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器

所有场景启动器最底层的依赖都是spring-boot-starter

  • 项目初始化创建完毕后,可以看到SpringBoot项目给我们生成的程序入口
@SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }}`

一切我们都从@SpringBootApplication开始讲起

  • 追踪@SpringBootApplication我们可以发现这是一个组合注解,它主要是由
  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan

以上三个注解组成。下面进行逐一分析。

  • @SpringBootConfiguration
@Configurationpublic @interface SpringBootConfiguration {    @AliasFor(        annotation = Configuration.class    )    boolean proxyBeanMethods() default true;}`

通过查看源码,发现其本质就是对@Configuration的简单封装,代表当前是一个配置类。

  • @ComponentScan
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })`

该注解的功能开启组件扫描,可以自动扫描指定包路径下的Spring注解。

  • @EnableAutoConfiguration(*)
@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {`

这是SpringBoot自动装配的主要注解,提供了强大的自动依赖功能。由@AutoConfigurationPackage

  • @AutoConfigurationPackag
@Import(AutoConfigurationPackages.Registrar.class)public @interface AutoConfigurationPackage {`

我们进入此方法,打上断点,用debug模式运行项目,可以追踪到AutoConfigurationPackages.Registrar类下的registerBeanDefinitions方法

@Override        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));        }`

其中 new PackageImports(metadata).getPackageNames().toArray(new String[0]) 是根据程序上下文来获取到的SpringBoot启动项所在的包名。从而利用Registrar给容器中导入一系列组件将指定的包下的所有组件导入进来。例如在我的这个项目中




idea 去掉maven自动编译 idea取消maven自动导入_spring


  • @Import(AutoConfigurationImportSelector.class)

该注解引入了一个AutoConfigurationImportSelector类,其中最主要的一个方法为getAutoConfigurationEntry()

protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);        fireAutoConfigurationImportEvents(configurations, exclusions);        return new AutoConfigurationEntry(configurations, exclusions);    }`

该方法是给容器中批量导入一些组件,其中的关键是

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;    }`

该方法获取到所有需要导入到容器中的配置类


idea 去掉maven自动编译 idea取消maven自动导入_idea 去掉maven自动编译_02


此处通过debug可以发现,SpringBoot将127个配置类导入到容器中。(都为xxxxAutoConfiguration)

那么此处的127个自动配置类是从何处而来呢?我们可以分析该方法中的主要方法SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());

public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {        String factoryTypeName = factoryType.getName();        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());    }`

其中loadSpringFactories方法是重点可以得到所有的组件

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {        MultiValueMap result = cache.get(classLoader);        if (result != null) {            return result;        }        try {            Enumeration urls = (classLoader != null ?                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));                ...                public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";`

这个url就是自动配置类的来源,默认扫描所有/META-INF/spring.factories这个文件


idea 去掉maven自动编译 idea取消maven自动导入_idea 去掉maven自动编译_03


这个文件中写死了127个配置类的类路径,至此我们知道了SpringBoot自动配置类的来源

  • 产生问题—我们有些配置类不需要导入,例如不需要Redis,他会不会给我们导入呢?

答案是不会的。我们可以去查看RedisCacheConfiguration的源码

@ConditionalOnClass(RedisOperations.class)@EnableConfigurationProperties(RedisProperties.class)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })public class RedisAutoConfiguration {`

决定配置类是否生效的一个重要因素就是@Conditional注解

只有符合要求才能决定配置类是否生效

下面附上一个常用条件注解

@ConditionalOnBean(上下文中存在某个对象时,才会实例化一个Bean)@ConditionalOnMissingBean(上下文中不存在某个对象时,才会实例化一个Bean)@ConditionalOnClass(某个class位于类路径,才会实例化一个Bean)@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)@ConditionalOnNotWebApplication(不是web应用)