zoukankan      html  css  js  c++  java
  • SpringBoot自动配置原理

    SpringBoot自动配置原理

    在SpringBoot启动主配置类时,@SpringBootApplication 注解发挥作用进行自动配置。

    @SpringBootApplication  //主程序自动配置
    public class SpringBoot02ConfigApplication {
        
        public static void main(String[] args) {
            SpringApplication.run(SpringBoot02ConfigApplication.class, args);
        }
    }
    

    @SpringBootApplication 注解中,实际发挥作用的是 @EnableAutoConfiguration 注解。

    @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 中,导入了一个自动配置选择器类 AutoConfigurationImportSelector。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class})  //导入自动配置选择器
    public @interface EnableAutoConfiguration {
    

    在AutoConfigurationImportSelector 类中有一个selectImports 方法, getAutoConfigurationEntry() 获取配置信息条目。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry
                 = this.getAutoConfigurationEntry(annotationMetadata);   //获取配置信息条目
                return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
            }
        }
    

    这个方法里面的 getCandidateConfigurations() 执行的是获取候选的配置。

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

    按住Ctrl,继续点击追查 getCandidateConfigurations() 方法,可以看到

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
          this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        //SpringFactoriesLoader 配置要在这里获取?
        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;
    }
    

    这时候看到一个字符串,似乎越来越接近真相了。

    "No auto configuration classes found in META-INF/spring.factories. 
    If you are using a custom packaging, make sure that file is correct."
    

    还有一句很关键的语句。

    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
    this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    

    SpringFactoriesLoader ? 配置要在这里获取? 看来还要继续深入。

    image-20200810180101778

    到这里可以确定是要读取在 META-INF/spring.factories 这个配置文件,这时候,我们可以去看 jar 包里的这个配置文件

    image-20200810174643416

    名为 or.spingframework. borin-booto atonfigre.2.REEASE 的 jar 包下,有这样一个 spring.factories 配置文件。

    image-20200810180537264
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    

    EnableAutoConfiguration 属性下的值所代表的配置自动都加入到容器中,不再需要我们像刚学习 Spring 时,
    在 xml 配置文件中一个个去配置。极大地提高了开发效率。

    下面以分析自动配置 DataSourceAutoConfiguration(数据源) 为例解释自动配置原理:

    @Configuration(
        proxyBeanMethods = false
    )     //注解表明这是一个配置类,同时关闭 proxyBeanMethods ,表示这个配置类不再被代理,
    //相当于用 final 修饰,不能在通过 @Bean来调用该配置类
    @ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class}) 
    //判断当前项目是否有数据源类,有则配置生效,查看DataSource.class源码可知是用于获取数据库连接  On表示判断是都包含
    @ConditionalOnMissingBean(
        type = {"io.r2dbc.spi.ConnectionFactory"}
    )   //判断当前项目是否不包括这些Bean  OnMissing表示判断是否不包含
    @EnableConfigurationProperties({DataSourceProperties.class})///启动指定类的ConfigurationProperties功能;
    //将配置文件中对应的值和DataSourceProperties绑定起来;并把DataSourceProperties加入到ioc容器中
    @Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
    public class DataSourceAutoConfiguration {
    

    在 DataSourceProperties.class 中,我们看到了熟悉的属性。

    @ConfigurationProperties(
        prefix = "spring.datasource"
    )    //从配置文件获取对应的属性值,进行绑定
    public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
        private ClassLoader classLoader;
        private String name;
        private boolean generateUniqueName = true;
        private Class<? extends DataSource> type;
        private String driverClassName;
        private String url;
        private String username;
        private String password;
        		...
    

    因此,我们可以在项目中的 application.properties 中配置相关属性。

    spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver.
    # Auto-detected based on the URL by default.   驱动名
    spring.datasource.url= # JDBC url of the database.  连接需要的url
    spring.datasource.username= # Login user of the database.  用户名
    spring.datasource.password= # Login password of the database.   密码口令
    

    总结:
    1)、SpringBoot启动会加载大量的自动配置类
    2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
    3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
    4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;

    xxxxAutoConfigurartion:自动配置类;给容器中添加组件

    对于想要自定义配置,或者是需要自行配置的相关属性,可以再 xxx.properties 文件中配置修改。

    既然自动配置需要一定条件判断才能生效,那么怎么查看 SpringBoot 已经帮助我们配置了哪些类呢?

    在项目中的 application.properties 中配置

    image-20200810215123152

    image-20200810215258538

    运行主程序,控制台会输出一个自动配置的报告

    2020.08.15 更新

    SpringBoot 2.0 + 使用的 DataSource 数据源 为 : com.zaxxer.hikari.HikariDataSource

    1.0 + : 版本为 : 默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源。

    HikariDataSource 的优点:(来源网络)

    • 字节码精简 :优化代码,直到编译后的字节码最少,这样,CPU缓存可以加载更多的程序代码;

    • 优化代理和拦截器:减少代码,例如HikariCP的Statement proxy只有100行代码,只有BoneCP(另一数据连接池)的十分之一;

    • 自定义数组类型(FastStatementList)代替ArrayList:避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描;

    • 自定义集合类型(ConcurrentBag):提高并发读写的效率;

    • 其他针对BoneCP缺陷的优化,比如对于耗时超过一个CPU时间片的方法调用的研究(但没说具体怎么优化)。

    现有连接池性能比较 :hikari>druid>tomcat-jdbc>dbcp>c3p0

    img

  • 相关阅读:
    iOS学习——键盘弹出遮挡输入框问题解决方案
    知识扩展——Git和GitHub的区别
    iOS项目——项目开发环境搭建
    iOS学习——iOS项目Project 和 Targets配置详解
    iOS扩展——Objective-C开发编程规范
    iOS学习——Xcode9上传项目到GitHub
    Mac OS Sierra如何打开任何来源
    iOS学习——UIAlertController详解
    iOS学习——获取iOS设备的各种信息
    Drag and drop folder to a TextBox in C#
  • 原文地址:https://www.cnblogs.com/l1ng14/p/13472692.html
Copyright © 2011-2022 走看看