小白都能听懂的spring boot自动化配置原理

spring boot最核心的特性就是他的自动化配置特性,极大的减少了构建一个spring web工程的工作量。那么你知道spring boot自动化配置的原理吗?

先直接自定义一个user-spring-boot-starter组件,感受下自动化配置的魅力。

构建user-spring-boot-starter

pom依赖

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.laowan</groupId>
    <artifactId>user-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>user-spring-boot-starter</name>
    <description>user-spring-boot-starter</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

添加配置属性

spring.user.enabled=false
spring.user.name=laowan

创建UserPorperties属性类使用配置属性

@Data
@ConfigurationProperties("spring.user")
public class UserPorperties {
    private String name;
}

利用属性类UserPorperties构建UserClient对象

public class UserClient {
    private UserPorperties userPorperties;
    public UserClient(){
    }

    public UserClient(UserPorperties userPorperties){
          this.userPorperties = userPorperties;
    }
    public String getName(){
        return userPorperties.getName();
    }
}

核心:创建UserAutoConfigure自动化配置类

@Configuration
@EnableConfigurationProperties(UserPorperties.class)
public class UserAutoConfigure {

    @Bean
    @ConditionalOnProperty(prefix = "spring.user",value ="enabled",havingValue = "true")
    public UserClient userClient(UserPorperties userPorperties){
        return new UserClient(userPorperties);
    }
}

说明:
@ConfigurationProperties(“spring.user”)
读取以spring.user为前缀的属性文件,配置实体类

@EnableConfigurationProperties(UserPorperties.class)
将使用了@ConfigurationProperties 注解的类注入到spring容器中。
如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。
简单来说@EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入。

@ConditionalOnProperty 属性条件判断,判断指定的属性是否有指定的值,满足条件才会初始化bean。

类似UserAutoConfigure这样的自动化配置类是所有自动化配置组件的核心入口
我们需要的就是在引入了user-spring-boot-starter的依赖后,在spring容器启动的时候,加载到这个自动化配置类,那么就可以初始化UserClient,完成自动化配置。

加载自动化配置类的三种方式

由于Spring boot默认扫描的是跟启动类平级的包。如果我们的Start跟启动类不在同一个主包下,那么就需要通过其他手段使得容器启动加载到UserAutoConfigure自动化配置类。
方式一:创建spring.factories属性配置文件
在user-spring-boot-starter工程下的/resources/META-INF目录下创建spring.factories属性配置文件,并在里面指定自动化属性配置类的全路径。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.laowan.user.autoconfig.UserAutoConfigure

这是由于在spring boot开启了自动化配置注解后,在容器启动时,会自动加载所有
/resources/META-INF下的spring.factories文件,读取org.springframework.boot.autoconfigure.EnableAutoConfiguration配置属性,组成一个集合,然后去遍历加载所有的自动化配置类。

建立demo工程测试:
1、引入user-spring-boot-starter的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>com.laowan</groupId>
    <artifactId>user-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

2、配置属性文件

spring.user.enabled=true
spring.user.name=laowan

3、编写单元测试类

@SpringBootTest
@Slf4j
class DemoApplicationTests {

    @Autowired
    UserClient userClient;

    @Test
    void userClientTest() {
        log.info(userClient.getName());
    }
}

4、执行结果
小白都能听懂的spring boot自动化配置原理_第1张图片
说明我们的自动化配置生效

方式二:通过定义@EnableXXX注解来加载自动化配置文件
在user-spring-boot-starter工程下,新建@EnableUserClient注解,其中最核心的是通过
@Import注解注入了UserAutoConfigure.class。这样在引用工程的启动类上只要添加了@EnableUserClient注解,那么就会加载到UserAutoConfigure自动化配置类


```javascript
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({UserAutoConfigure.class})
public @interface EnableUserClient {
}

测试:

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

先注释掉spring.factories中的配置项,然后执行单元测试,正确打印出配置属性spring.user.name的值,说明我们的自动化配置生效。

方式三:通过@SpringBootApplication注解指定扫描的基础包路径

在测试工程demo中,配置@SpringBootApplication的属性scanBasePackages

@SpringBootApplication(scanBasePackages = {"com.laowan.demo","com.laowan.user.autoconfig"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

通过@SpringBootApplication注解的scanBasePackages 属性,指定扫描的包路径。
这里注意,一定要首先指定自己工程的根路径,然后再执行自动化配置类的包路径。
不然就只会扫描自动化配置类的包路径,自己工程就不会扫描导致启动出错。

测试:
执行单元测试,发现配置属性spring.user.name的值仍然可以正常打印,说明我们的自动化配置成功。

三种方式的比较:
三种加载自动化配置的方式,其核心都是解决Spring boot工程启动只会默认扫描跟启动类平级的包,导致其他不同包下的自动化配置类XXXAutoConfigure.class加载不到的问题。
方式一是通过spring.factories文件中配置org.springframework.boot.autoconfigure.EnableAutoConfiguration属性指定自动化配置类XXXAutoConfigure.class的全路径,是最主流的方式。
方式二通过自定义@EnableXXX注解并结合@Import注解加载自动化配置文件,在很多组件中也很常见,比如:
@EnableResourceServer

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ResourceServerConfiguration.class})
public @interface EnableResourceServer {
}

@EnableDiscoveryClient

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableDiscoveryClientImportSelector.class})
public @interface EnableDiscoveryClient {
    boolean autoRegister() default true;
}

方式三主要是对自动化配置原理的一种验证,实际项目中不推荐。

自动化配置原理核心:
就是想办法在spring容器启动的时候,扫描到自动化配置类,然后根据属性类和条件注解去声明Bean,完成自动化配置。

实现配置项提示功能

在user-spring-boot-starter工程的/resources/META-INF目录下创建spring-configuration-metadata.json

{
  "properties": [
    { "name": "spring.user.name",
      "defaultValue": "laowan",
      "description": "用户名称.",
      "type": "java.lang.String"
    },
    { "name": "spring.user.enabled",
      "defaultValue": false,
      "description": "是否启用user组件.",
      "type": "java.lang.Boolean"
    }
  ]
}

这样在引用工程中配置相关属性,就会出现提示了。
在这里插入图片描述

通用规定

1、spring官方自己定义的starter组件,命名规则:spring-boot-starter-组件名
如:
spring-boot-starter-web
spring-boot-starter-jdbc
spring-boot-starter-security

非官方定义的start组件,命名规则:组件名-spring-boot-starter
如:
mybatis-spring-boot-starter

2、自动化配置类命名:XXXAutoConfiguration

3、starter组件的常规目录结构分析
一般分为2个工程,一个xxx-spring-boot-starter工程,通过spring.provides指定依赖服务模块;
一个xxx-spring-boot-starter-autoconfigure模块,通过定义spring.factories指定自动化配置文件加载路径,定义spring-configuration-metadata.json实现自动化配置。
小白都能听懂的spring boot自动化配置原理_第2张图片

核心注解

在构建starter的过程中,涉及到一些注解
@EnableAutoConfiguration 开启自动化配置功能,包含在@SpringBootApplication注解中,可以通过exclude属性,过滤掉一些不需要开启自动化配置的组件。

@Import 通过快速导入的方式实现把实例加入spring的IOC容器中,@Import只能用在类上

@ConfigurationProperties(“spring.user”) 加载前缀为spring.user的属性去配置当前类,但是并不会加载到spring容器中,需要配合@Component或者@EnableConfigurationProperties去使用。
@EnableConfigurationProperties(UserPorperties.class) 使 使用了@ConfigurationProperties注解配置的类生效,也就是注入到spring容器中

条件注解@Conditional
可以放在加了@Configuration的配置类上面,也可以放在使用@Bean定义bean的时候。用来判断是否开启配置或是否注入bean。
@ConditionalOnBean:当容器中有指定的Bean的条件下
@ConditionalOnClass:当类路径下有指定的类的条件下
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径下是否有指定的资源
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件下

总结

1、自动化配置的原理:
(1)、加载自动化配置类
通过@EnableAutoConfiguration开启自动化配置机制,原理是通过类加载器去扫描目录下所有spring.factories文件,读取org.springframework.boot.autoconfigure.EnableAutoConfiguration属性,然后去加载XXXAutoConfigure自动化配置类。这个是通用的加载方式,适合批量默认自动开启的组件。

针对某些特定组件,没有定义spring.factories文件,则需要通过在启动类上添加@EnableXXX的注解,通过@Import导入指定的自动化配置类,这种方式适合单一控制,默认不开启自动化配置的组件。

(2)、根据读取到的自动化配置类,完成相关配置过程
XXXAutoConfigure自动化配置类中根据spring boot相关注解,读取相关属性文件,并根据@Conditional条件注解判断是否开启自动化配置,是否实例化Bean。

2、自动化配置类加载的三种方式

3、怎么自定义一个starter组件以及相关的规范

4、自动化配置过程中,常见注解的说明

spring boot自动化配置的原理,你明白了吗?

觉得有用,记得点赞加关注。

实战代码Git地址:https://github.com/StarlightWANLI/auto-config.git

小白都能听懂的spring boot自动化配置原理_第3张图片

你可能感兴趣的:(spring,boot实战)