欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot 自动配置探究

程序员文章站 2024-02-26 20:06:10
...

pom.xml

当我们初始化一个 SpringBoot 工程,查看 pom.xml配置,发现有如下 依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.5RELEASE</version>
</parent>

根据 maven 的继承依赖关系,点进去发现还会继续依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.1.5.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

再点进去可以看到最终依赖了spring-boot-dependencies依赖文件,也就是说 SpringBoot的核心依赖在父工程中,这个文件定义了好多jar 包以及各个 Jar 包的 版本,我们写或引入一些 SpringBoot 依赖的时候不需要指定版本就因为有这些版本库。

注意我上面用的是 2.1.5.RELEASE 版本,可以直接查看依赖继承关系,如果是 2.2.2.RELEASE(最新版)点不了,改成从 spring-boot-starter-web 启动器进源码才可以查看

启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

主程序

@SpringBootApplication
public class SpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootApplication.class, args);
    }
}
  • @SpringBootApplication 这个注解它就标注了这是一个 SpringBoot 的应用

  • @SpringBootApplication 是一个组合注解,点进去发现由多个注解组成

    @SpringBootConfiguration
    @EnableAutoConfiguration
    //扫描当前启动类同等级的包
    @ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
    ), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})})
    public @interface SpringBootApplication {
    }
    
  • @SpringBootConfiguration 表明它是一个 SpringBoot 配置,查看源码发现核心注解由 @Configuration 组成,而 @Configuration 是个 Spring 注解,查看 @Configuration 源码发现最终是个 @Component (组件)

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {
    }
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
        @AliasFor(annotation = Component.class)
        String value() default "";
    }
    
  • @EnableAutoConfiguration 自动配置,查看其源码

    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    }
    

    @AutoConfigurationPackage 就是自动配置包,查看其源码,发现使用 @Import 引入了 Registrar 这个类

    @Import({Registrar.class})
    public @interface AutoConfigurationPackage {
    }
    

    查看 Registrar 类源码

    public abstract class AutoConfigurationPackages {
        private static final Log logger = LogFactory.getLog(AutoConfigurationPackages.class);
        private static final String BEAN = AutoConfigurationPackages.class.getName();
    
        public AutoConfigurationPackages() {
        }
    
        public static boolean has(BeanFactory beanFactory) {
            return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty();
        }
    
        public static List<String> get(BeanFactory beanFactory) {
            try {
                return ((AutoConfigurationPackages.BasePackages)beanFactory.getBean(BEAN, AutoConfigurationPackages.BasePackages.class)).get();
            } catch (NoSuchBeanDefinitionException var2) {
                throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
            }
        }
    
        public static void register(BeanDefinitionRegistry registry, String... packageNames) {
            if (registry.containsBeanDefinition(BEAN)) {
                BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
                ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
                constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
            } else {
                GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
                beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
                beanDefinition.setRole(2);
                registry.registerBeanDefinition(BEAN, beanDefinition);
            }
    
        }
        static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
            Registrar() {}
            public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
            }
            public Set<Object> determineImports(AnnotationMetadata metadata) {
                return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
            }
        }
    }
    
  • @Import(AutoConfigurationImportSelector.class) 则引入了 AutoConfigurationImportSelector 类

    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
        private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry();
        private static final String[] NO_IMPORTS = new String[0];
        private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
        private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
        private ConfigurableListableBeanFactory beanFactory;
        private Environment environment;
        private ClassLoader beanClassLoader;
        private ResourceLoader resourceLoader;
    
        public AutoConfigurationImportSelector() {
        }
    	//选择我们在 pom.xml 文件中引入的 jar 包或 组件
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
                return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
            }
        }
        //获取自动配置实体
        protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            } else {
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                //获取候选配置
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
            }
        }
        //META-INF/spring.factories 是自动配置的核心文件
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
        }
        //EnableAutoConfiguration 这个类就是我们上文分析过的自动导入配置类,
        //这样就可以获取到被这个注解标注的类的所有配置
        //而 @SpringBootApplication 注解就继承了这个注解
        //也即获取了主启动类加载的所有组件(绕了很远...)
        protected Class<?> getSpringFactoriesLoaderFactoryClass() {
            return EnableAutoConfiguration.class;
        }
    }
    

    关于spring.factories: 它是一个properties文件,它位于classpath:/META-INF/目录里面,每个jar包都可以有spring.factories的文件。Spring 提供工具类 SpringFactoriesLoader 负责加载、解析文件,如spring-boot-auto-configure-2.2.0.RELEASE.jar里面的META-INF目录里面就有spring.factories文件

    # Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
    
    # Auto Configuration Import Listeners
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
    org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
    
    # Auto Configuration Import Filters
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
    
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
    .....
    

    再看看 getCandidateConfigurations()方法中的 SpringFactoriesLoader.loadFactoryNames() 源码

    public final class SpringFactoriesLoader {
      //loadFactoryNames方法获取所有的加载配置
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
          String factoryTypeName = factoryType.getName();
          return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
      }
      private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
          MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
          if (result != null) {
              return result;
          } else {
              try {
                  //加载项目资源
                  Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                  LinkedMultiValueMap result = new LinkedMultiValueMap();
                  //遍历了所有的自动配置,封装成 properties 供我们使用
                  while(urls.hasMoreElements()) {
                      URL url = (URL)urls.nextElement();
                      UrlResource resource = new UrlResource(url);
                      Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                     。。。。。。
                  }
                  cache.put(classLoader, result);
                  return result;
              }
          }
      }
    }
    

    自动配置包
    SpringBoot 自动配置探究

@ConditionalOnXXXX 注解

spring.factories 中那么多的配置类,有的并没有生效,需要导入对应的 starter 才能有作用,比如随便找个 AopAutoConfiguration 这个配置类,查看其源码,只有满足了 @ConditionalOnXXXX 注解里面的条件才会生效

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnProperty(
    prefix = "spring.aop",
    name = {"auto"},
    havingValue = "true",
    matchIfMissing = true
)
public class AopAutoConfiguration {
}

再比如的源码,只要满足 @ConditionalOnClass 条件即会生效

@Configuration(  proxyBeanMethods = false)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
}

yml 文件的配置是如何生效的?

根据上文分析,SpringBoot 会加载 spring.factories 的所有自动类,以 DataSourceAutoConfiguration 这个配置类来说,如果 pom 文件有对应的启动类,它就会生效,那么就会通过 @EnableConfigurationProperties 加载默认的配置文件,从源码可以看出默认的配置文件是 DataSourceProperties ,假如我们在 yml 中重新配置了某些属性,比如 jdbc.name、jdbc.username、jdbc.password…,SpringBoot 会读取这些属性并覆盖默认属性。

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    private ClassLoader classLoader;
    private String name;
    private boolean generateUniqueName;
    private Class<? extends DataSource> type;
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private String jndiName;
    ....
}

总结

SpringBoot 所有自动配置都是在启动的时候扫描并加载,spring.factories 所有的自动类配置都得了这里,但是不一定会生效,要判断条件是否成立(通过 @ConditionalOnXXXX 注解),只要导入了对应的 starter 就有对应的启动器了,有了启动器,自动装配就会生效,然后就配置成功。

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