Spring Boot自动配置原理探究

文章目录

  • Spring Boot自动装配
  • 定义自己的条件配置
  • 定义自己的Starter
  • 总结

Spring Boot自动装配

在传统的Spring应用程序中,如果你需要配置H2数据库,该怎么配置?

  1. 配置JdbcTemplate的Bean

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource)
    }
    
  2. 配置DataSource的Bean

    @Bean
    public DataSource dataSource() {
        return (DataSource) new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScripts("schema.sql", "data.sql")
            .build();
    }
    

但是如果是用Spring Boot来做这一切呢?如果Spring Boot在应用程序的Classpath里发现了H2数据库的库,那么它就会自动配置一个嵌入式H2数据库。如果在Classpath里发现JdbcTemplate,那么它还会为你配置一个JdbcTemplate的Bean。也就是说你只需要在pom.xml或者是build.gradle中添加相应的依赖,告诉Spring Boot你需要做什么,有关的基本配置Spring Boot就会帮你解决,就不用再像上述那样手写两个Bean了。

dependencies {
	runtimeOnly 'com.h2database:h2'
}

那么Spring Boot是怎么做到的呢?

一切都要从这个注解开始:@SpringBootApplication
Spring Boot自动配置原理探究_第1张图片
Spring Boot自动配置原理探究_第2张图片
Spring Boot自动配置原理探究_第3张图片
getAutoConfigurationEntry方法的第三步中,Spring Boot加载了所有的配置类,但是并不是所有的配置类都是要被该程序使用到的,很多是当前的应用程序不会使用到的。

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

Spring Boot自动配置原理探究_第4张图片
然后经过第四步,通过一些条件配置的判断,过滤掉了很多不被使用的配置类。
Spring Boot自动配置原理探究_第5张图片
那Spring Boot到底是怎么做到根据应用的需求而裁剪掉一些不需要的配置呢?答案便是通过条件注解。

Spring Boot中有个名为spring-boot-autoconfigure的JAR文件,其中包含了各种各样的配置类。有用于Thymeleaf的配置,有用于Spring Data JPA的配置,也有用于Spring MVC的配置等等。但这些配置类在你的应用程序中并不是全部都会生效,而是会根据你的选择而生效,只有当满足某些条件,相应的自动配置才会生效。
Spring Boot自动配置原理探究_第6张图片

那Spring Boot是怎么像人脑一样来区分不同的条件呢?靠的就是条件注解可以根据条件注解,来判断是否需要创建某个特定的Bean

一些常见的条件注解为:

@Conditional 依赖的条件成立则该配置生效

@ConditionalOnBean 在某个Bean存在的条件下则该配置生效

@ConditionalOnMissingBean 在某个Bean不存在的条件下则该配置生效

@ConditionalOnClass 在某个Class存在的条件下则该配置生效

@ConditionalOnMissingClass 在某个Class不存在的条件下则该配置生效

比如我们看Spring Boot的一个自动配置类:DataSourceAutoConfiguration。里面分别有两个条件注解:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
	//......
}

第一个条件注解@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })表示只有当前的Classpath中有DataSource.class, EmbeddedDatabaseType.class时当前配置类才会生效;

第二个条件注解@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")则表示只有当前的Classpath中不存在io.r2dbc.spi.ConnectionFactory类型的Bean的时候,当前配置内才会生效。

对于这个配置类,只有当两个条件注解都同时成立时,该配置类才会生效。

定义自己的条件配置

在Spring Boot中定义自己的条件,只需要implements Condition接口即可(org.springframework.context.annotation中的Condition接口)。

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class JdbcTemplateCondition implements Condition {

  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    try {
      //当Classpath中存在JdbcTemplate时条件生效
      context.getClassLoader().loadClass(
              "org.springframework.jdbc.core.JdbcTemplate"
      );
      return true;
    } catch (Exception ex) {
      return false;
    }
  }
}

当用Java声明Bean的时候,可以这样使用自定义条件类:

@Conditional(JdbcTemplateCondition.class)
public MyService myService() {
    return new MyService();
}

在 JdbcTemplateCondition类里设置的条件是:当Classpath里有JdbcTemplate时,返回true,也就是说此时条件成立,否则条件不成立。

只有满足了JdbcTemplateCondition类里设置的条件,MyService Bean才会被创建成功。

定义自己的Starter

既然谈到Spring Boot,就不得不提Spring Boot的一个设计思想:约定大于配置。通过一些约定俗成的行为,让用户减少很多不必要的配置。

参考:GitChat - 精通 Spring Boot 42 讲

约定优于配置(Convention Over Configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量、获得简单的好处,而又不不失灵活性。
本质是说,开发人员仅需规定应用中不不符约定的部分。例如,如果模型中有个名为 User 的类,那么数据库中对应的表就会默认命名为 user。只有在偏离这一约定时,例如将该表命名为“user_info”,才需写有关这个名字的配置。如果用户按照约定编写代码,就将由Spring Boot来按照约定进行自动配置。
我们可以按照这个思路来设想,我们约定 Controller 层就是 Web 请求层可以省略略 MVC 的配置;我们约定在Service 结尾的类⾃自动注入事务,就可以省略了 Spring 的切面事务配置。
在 Spring 体系中,Spring Boot JPA 就是约定优于配置最佳实现之⼀,不需要关注表结构,我们约定类名即是表名,属性名即是表的字段,String 对应 varchar,long 对应 bigint,只有需要一些特殊要求的属性,我们再单独进行行配置,按照这个约定我们可以将以前的工作大大的简化。
Spring Boot 体系将约定优于配置的思想展现得淋淋尽致,小到配置文件,中间件的默认配置,大到内置容器器、生态中的各种 Starters 无不遵循此设计规则。Spring Boot 鼓励各软件组织方创建自己的 Starter,创建Starter 的核心组件之一就是 autoconfigure 模块,也是 Starter 的核⼼心功能,在启动的时候进行行自动装配,属性默认化配置。
可以说正是因为 Spring Boot 简化的配置和众多的 Starters 才让 Spring Boot 变得简单、易用、快速上手,也可以说正是约定优于配置的思想的彻底落地才让 Spring Boot 走向辉煌。Spring Boot 约定优于配置的思想让Spring Boot 项目非常容易上手,让编程变的更简单,其实编程本该很简单,简单才是编程的美。
Spring Boot 由众多 Starter 组成,随着版本的推移 Starter 家族成员也与日俱增。在传统 Maven 项目中通常将一些层、组件拆分为模块来管理理,以便相互依赖复用,在 Spring Boot 项目中我们则可以创建自定义Spring Boot Starter 来达成该目的。

通过Spring Boot Starter,我们就可以引入相关的配置,比如通过spring-boot-starter-web starter,我们就能引入web开发相关的基础依赖。我们只需要引入这一个starter,我们就能开发一个简单的web应用。如果不用starter,我们开发一个web应用,首先需要配置tomcat,然后要引入webmvc相关依赖,然后要引入web相关依赖,但是通过Spring Boot,我们只需要引入一个spring-boot-starter-web的依赖就可以了。
Spring Boot自动配置原理探究_第7张图片
如何自定义一个starter?
参考我的另一篇文章:
Spring Boot两大核心原理:自动配置以及Starter,详细讲述starter并实践自定义一个spring boot starter

总结

最后总结一下Spring Boot的自动配置。

Spring Boot的自动配置是一个运行时判断的过程,在运行的过程中,考虑了众多的因素,最终才决定Spring的配置应该用哪个,不该用哪个。在运行时,Spring Boot可能会这样做决策:

  • Spring的JdbcTemplate是不是在Classpath里?如果是,并且有DataSource的Bean,则自动配置一个JdbcTemplate的Bean。
  • Thymeleaf是不是在Classpath里?如果是,则配置Thymeleaf的模板解析器、视图解析器以及模板引擎。
  • Spring Security是不是在Classpath里?如果是,则进行一个非常基本的Web安全设置。

每当应用程序启动时,Spring Boot的自动配置都要做将近200个这样的决定,涵盖安全、集成、持久化、Web开发等诸多方面。所谓的自动配置就是对于这些比较基础,大多数情况下需要用到的配置,由Spring Boot自动完成,减轻人力物力。

你可能感兴趣的:(Spring,spring,java,spring,boot)