一、作用与原理

首先说说原理,我们知道使用一个公用的starter的时候,只需要将相应的依赖添加的Maven的配置文件当中即可,免去了自己需要引用很多依赖类,并且SpringBoot会自动进行类的自动配置。那么 SpringBoot 是如何知道要实例化哪些类,并进行自动配置的呢? 下面简单说一下。

首先,SpringBoot 在启动时会去依赖的starter包中寻找 resources/META-INF/spring.factories 文件,然后根据文件中配置的Jar包去扫描项目所依赖的Jar包,这类似于 Java 的 SPI 机制。

第二步,根据 spring.factories配置加载AutoConfigure类。

最后,根据 @Conditional注解的条件,进行自动配置并将Bean注入Spring Context 上下文当中。

我们也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class}) 指定自动配置哪些类。

二、nacos-spring-boot-project starter项目解读

官方提供的starter,大多包含两个jar包: 一个starter——没有任何实现,只用来管理依赖(spring.providers文件声明);一个autoconfigure包——包含所有具体实现,包括自动配置类,及META-INF/spring.factories文件。自定义starter的时候,可以合并写到一个。

官方提供的starter,命名遵循spring-boot-starter-xx; 自定义starter,命名遵循xx-spring-boot-starter。

1. nacos-spring-boot-project 包组成与命名

springboot 提供一个前缀且代理当前系统接口_spring

从项目的截图可以看出,包的拆分上采取了类似官方的2个jar的形式;命名上采取了 自定义starter,遵循xx-spring-boot-starter的命名方式。

2. 分析nacos-config-spring-boot-starter

springboot 提供一个前缀且代理当前系统接口_ci_02

如上图所示,这是一个starter——没有任何实现,只用来管理依赖(spring.providers文件声明), 该声明指明了依赖由

provides: nacos-config-spring-boot-autoconfigure 提供。

其pom.xml 内容如期望的一样:仅有业务依赖和基础starter依赖,而没有基础配置依赖,因为包含在了nacos-config-spring-boot-autoconfigure包中。

<dependencies>
        <!-- Spring Boot dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <scope>true</scope>
        </dependency>


        <!-- Nacos -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-spring-context</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-spring-boot-base</artifactId>
        </dependency>

       <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-config-spring-boot-autoconfigure</artifactId>
        </dependency>

    </dependencies>

3.分析nacos-config-spring-boot-autoconfigure

springboot 提供一个前缀且代理当前系统接口_自定义_03

如上图所示,在resources/META-INF/spring.factories文件中如下内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration

是整个配置的关键部分,其指明了AutoConfigure实现类是 NacosConfigAutoConfiguration类。而该类的核心作用就是实现其注解中的相关bean在某些条件下的自动装配。

@ConditionalOnProperty(name = NacosConfigConstants.ENABLED, matchIfMissing = true)
@ConditionalOnMissingBean(name = CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME)
@EnableNacosConfig
@EnableConfigurationProperties(value = NacosConfigProperties.class)
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
public class NacosConfigAutoConfiguration {

}

该类通过注解进行实现,其注解的含义如下:

  • @ConditionalOnClass,当classpath下发现该类的情况下进行自动配置。
  • @ConditionalOnMissingBean,当Spring Context中不存在该Bean时进行自动配置。
  • @ConditionalOnProperty(name = NacosConfigConstants.ENABLED, matchIfMissing = true)时进行自动配置。

其中NacosConfigConstants类的如下:

public interface NacosConfigConstants {

	String ENDPOINT_PREFIX = "nacos-config";

	String ENABLED = EnableNacosConfig.CONFIG_PREFIX + "enabled";

    String PREFIX = "nacos.config";

}

有引用到了另一个注解

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({NacosConfigBeanDefinitionRegistrar.class})
public @interface EnableNacosConfig {
    String CONFIG_PREFIX = "nacos.config.";
    String ENDPOINT_PLACEHOLDER = "${nacos.config.endpoint:${nacos.endpoint:}}";
    String NAMESPACE_PLACEHOLDER = "${nacos.config.namespace:${nacos.namespace:}}";
    String ACCESS_KEY_PLACEHOLDER = "${nacos.config.access-key:${nacos.access-key:}}";
    String SECRET_KEY_PLACEHOLDER = "${nacos.config.secret-key:${nacos.secret-key:}}";
    String SERVER_ADDR_PLACEHOLDER = "${nacos.config.server-addr:${nacos.server-addr:}}";
    String CONTEXT_PATH_PLACEHOLDER = "${nacos.config.context-path:${nacos.context-path:}}";
    String CLUSTER_NAME_PLACEHOLDER = "${nacos.config.cluster-name:${nacos.cluster-name:}}";
    String ENCODE_PLACEHOLDER = "${nacos.config.encode:${nacos.encode:UTF-8}}";

    NacosProperties globalProperties() default @NacosProperties(
    endpoint = "${nacos.config.endpoint:${nacos.endpoint:}}",
    namespace = "${nacos.config.namespace:${nacos.namespace:}}",
    accessKey = "${nacos.config.access-key:${nacos.access-key:}}",
    secretKey = "${nacos.config.secret-key:${nacos.secret-key:}}",
    serverAddr = "${nacos.config.server-addr:${nacos.server-addr:}}",
    contextPath = "${nacos.config.context-path:${nacos.context-path:}}",
    clusterName = "${nacos.config.cluster-name:${nacos.cluster-name:}}",
    encode = "${nacos.config.encode:${nacos.encode:UTF-8}}"
);
}

从上面的类和注解可以看出,其实,就相当于更为常见的 @ConditionalOnProperty(prefix = "nacos.config",value = "enabled",havingValue = "true")。

从上面可以看出,其毫无例外的都用都了基于前缀的自动配置进行bean的装配,那么最终依赖如下

spring-boot-configuration-processor 也是必然的了。
<!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

在最上面的红框中我们看到了 spring-configuration-metadata.json 核心作用是使用时给出自动给出IDE提示,需要料及而更多请参照:Springboot实现基于前缀的自定义配置和自动提示功能。

进一步,看下pom.xml中依赖了哪些包?

<dependencies>
        <!-- Spring Boot dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- Nacos -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-spring-context</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-spring-boot-base</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- Test Dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

最终,确实依赖到了如下2个包:

spring-boot-autoconfigure
spring-boot-configuration-processor

附加额外注解解释:

@ConditionalOnBean:当容器中有指定的Bean的条件下  
@ConditionalOnClass:当类路径下有指定的类的条件下  
@ConditionalOnExpression:基于SpEL表达式作为判断条件  
@ConditionalOnJava:基于JVM版本作为判断条件  
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置  
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下  
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下  
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下  
@ConditionalOnProperty:指定的属性是否有指定的值  
@ConditionalOnResource:类路径下是否有指定的资源  
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean @ConditionalOnWebApplication:当前项目是Web项目的条件下