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

posted @ 2020-08-15 16:16  L1ng14  阅读(274)  评论(0编辑  收藏  举报