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

  • 相关阅读:
    LeetCode 230. 二叉搜索树中第K小的元素(Kth Smallest Element in a BST)
    LeetCode 216. 组合总和 III(Combination Sum III)
    LeetCode 179. 最大数(Largest Number)
    LeetCode 199. 二叉树的右视图(Binary Tree Right Side View)
    LeetCode 114. 二叉树展开为链表(Flatten Binary Tree to Linked List)
    LeetCode 106. 从中序与后序遍历序列构造二叉树(Construct Binary Tree from Inorder and Postorder Traversal)
    指针变量、普通变量、内存和地址的全面对比
    MiZ702学习笔记8——让MiZ702变身PC的方法
    你可能不知道的,定义,声明,初始化
    原创zynq文章整理(MiZ702教程+例程)
  • 原文地址:https://www.cnblogs.com/l1ng14/p/13472692.html
Copyright © 2011-2022 走看看