• SpringBoot自动配置原理学习


    介绍

    构建Springboot项目时我们会创建一个启动类

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

    1. 在之前使用Spring框架时,我们一般会创建web.xml和spring-context.xml等文件配置组件扫描、调度器、视图解析器等。

    2. 而在SpringBoot中则简单了很多,这里就有自动配置发挥作用。如默认用的内嵌式容器是 Tomcat ,端口默认设置为 8080

    正式进入流程解析

    首先看@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) })
    @ConfigurationPropertiesScan
    public @interface SpringBootApplication {
    
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
        String[] excludeName() default {};
    
    }

    @EnableAutoConfiguration的作用是开启自动配置

    核心是@EnableAutoConfiguration注解

    这就是Spring Boot自动配置实现的核心入口,重头戏是@Import注解,这个注解导入了AutoConfigurationImportSelector类,利用AutoConfigurationImportSelector(自动配置导入选择器)将所有符合自动装配条件的bean注入到IOC容器中

    查看AutoConfigurationImportSelector类代码,重头戏是selectImports方法

    selectImports何时被执行

    Springboot应用启动过程中使用ConfigurationClassParser分析配置类时,发现注解中存在@Import(ImportSelector)的情况。

    就会创建一个相应的ImportSelector对象, 并调用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata)

    selectImports()方法流程

    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
            ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
        // 省略。。
    
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            // 1 判断是否开启自动配置,为false直接返回空字符数组
        
    if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }
         // 2 loadMetadata()去加载Spring预先定义的自动配置的依赖信息,
    下面会具体说明
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);
         // 3 该方法返回的就是配置项信息,下面会具体说明 AutoConfigurationEntry autoConfigurationEntry
    = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } // 省略。。。 }

    2. loadMetadata()去加载Spring预先定义的自动配置的依赖信息

     1 static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
     2         try {
     3             Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
     4                     : ClassLoader.getSystemResources(path);
     5             Properties properties = new Properties();
     6             while (urls.hasMoreElements()) {
     7                 properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
     8             }
     9             return loadMetadata(properties);
    10         }
    11         catch (IOException ex) {
    12             throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
    13         }
    14     }

    path的地址是

    protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

    里面的自动配置文件(部分)

    org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.batch.core.launch.JobLauncher
    org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

      2.1 根据path获取文件的URL对象

      2.2 遍历URL节点,将spring-autoconfigure-metadata.properties中的依赖信息加载到properties对象中

      2.3 调用loadMetadata()方法把properties存储到PropertiesAutoConfigurationMetadata类中

    3. 在这里面会调用一个方法getAutoConfigurationEntry(),该方法返回的就是配置项信息,进入这个方法

     1 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
     2             AnnotationMetadata annotationMetadata) {
               // 3.1
    3
    if (!isEnabled(annotationMetadata)) { 4 return EMPTY_ENTRY; 5 } // 3.2
    6
    AnnotationAttributes attributes = getAttributes(annotationMetadata);       // 3.3
    7
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 3.4
    8
    configurations = removeDuplicates(configurations); // 3.5
    9
    Set<String> exclusions = getExclusions(annotationMetadata, attributes); 10 checkExcludedClasses(configurations, exclusions); 11 configurations.removeAll(exclusions); 12 configurations = filter(configurations, autoConfigurationMetadata);        // 3.6
    13 fireAutoConfigurationImportEvents(configurations, exclusions);        // 3.7
    14 return new AutoConfigurationEntry(configurations, exclusions); 15 }

      3.1 判断是否开启自动配置

      3.2 getAttributes()方法获取@SpringBootApplication(主要是为了获取@EnableAutoConfiguation)注解上的属性

      3.3 加载META-INF/spring.factories文件, 这个文件配置了具有哪些自动配置类,文件内容如下 ,具体代码如下    

    getCandidateConfigurations方法如下

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

        3.3.1 getSpringFactoriesLoaderFactoryClass方法返回EnableAutoConfiguration.class

        3.3.2 SpringFactoriesLoader.loadFactoryNames会加载META-INF/spring.factories中EnableAutoConfiguration的值中定义的jar包,并将其封装到一个List中返回

      3.4 removeDuplicates() 用LinkedHashSet去重,在包装成ArrayList

      3.5 getExclusions()、checkExcludedClasses()、removeAll()、filter() 过滤注解中要排除的自动配置

      3.6 fireAutoConfigurationImportEvents() 将自动配置导入监听

      3.7 返回配置的AutoConfigurationEntry包装类

    自动配置原理(简化版)

    通过 @EnableAutoConfiguration 核心注解初始化,并扫描 ClassPath 目录中自动配置类。根据项目中需要添加的默认配置,如springMVC,就按一定规则获取默认配置并自动初始化所需要的 Bean。

    1. 核心是@EnableAutoConfiguration
    2. 里面会通过@Import注解导入(AutoConfigurationImportSelector.class)类
    3. 调用importSelect方法,进行自动配置

      3.1 判断是否开启了自动配置
      3.2 getAutoConfigurationEntry方法会使用
        SpringFactoriesLoader.loadFactoryNames方法加载classpath中spring.factories文件中EnableAutoConfiguration的配置类
      3.3 经过去重,排除后会把这些配置导入监听

    重点关注

    @EnableAutoConfiguration

    @Import

    AutoConfigurationImportSelector类

    selectImports方法

    SpringFactoriesLoader.loadFactoryNames() 核心方法读取 ClassPath 目录下面的 META-INF/spring.factories 文件。

    spring.factories 文件中存放springBoot自动配置类, 如Servlet、jpa

    【示例】(JDBC)的DataSourceAutoConfiguration配置

    在spring.factories中org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguratio

    作用是:SpringBoot 自动配置DataSource

    // 表示这是一个配置类
    @Configuration(proxyBeanMethods = false)
    // 判断当前项目classpath有没有这两个类 DataSource EmbeddedDatabaseType
    @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
    // 将配置文件中对应的值和 DataSourceProperties 绑定起来,并把DataSourceProperties的属性值放到ioc容器中
    @EnableConfigurationProperties(DataSourceProperties.class)
    // 导入数据源注册类、数据源初始化类
    @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
    public class DataSourceAutoConfiguration {
      // 省略。。
    }

    查看DataSourceProperties属性,提供的这些属性,就可以获取到在application.yml中提供的值来设置数据源了

    @ConfigurationProperties(prefix = "spring.datasource")
    public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    
        private ClassLoader classLoader;
    
        /**
         * Name of the datasource. Default to "testdb" when using an embedded database.
         */
        private String name;
    
        /**
         * Whether to generate a random datasource name.
         */
        private boolean generateUniqueName;
    
        /**
         * Fully qualified name of the connection pool implementation to use. By default, it
         * is auto-detected from the classpath.
         */
        private Class<? extends DataSource> type;
    
        /**
         * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
         */
        private String driverClassName;
    
        /**
         * JDBC URL of the database.
         */
        private String url;
    
        /**
         * Login username of the database.
         */
        private String username;
    
        /**
         * Login password of the database.
         */
        private String password;
    
        /**
         * JNDI location of the datasource. Class, url, username & password are ignored when
         * set.
         */
        private String jndiName;
    
            。。。省略
    }

    总结

    Spring Boot 内部提供了很多自动化配置的类, 配置类会更加classpath下是否又相关依赖类,来判断是否要配置字节,所以当我们在pom中引入了相关依赖,就可以自动配置了

     

    ======  【多学一点,for Better】======

  • 相关阅读:
    Spring Boot启动时数据库初始化spring.datasource(转)
    shell脚本中报错dirname:无效选项 b(转)
    java中ftpClient.listFiles()结果为空问题解决方案
    Visual Prompt Tuning AHU
    Weakly AlignmentFree RGBT Salient Object Detection With Deep Correlation Network AHU
    autocad2022中文破解版
    test
    openGauss WDR报告详细解读
    vscode 配置C/C++环境 出现问题 could not find the task 'g++' / 'gcc'
    OceanBase分布式事务剖析
  • 原文地址:https://www.cnblogs.com/milicool/p/11718099.html
Copyright © 2020-2023  润新知