您的位置:首页 > 编程语言 > Java开发

从nacos-spring-boot-project角度全面看Springboot Starter

2019-05-11 17:02 891 查看
版权声明:阅读优秀源码,宛若一场探索未知的旅行,疑惑处惊奇,优雅处旖旎; 一切都是新奇的,千回百转与大师的心灵触碰,一场跨越时空的对话,涤荡了原有的愚昧,蜕变出更好的自己。 https://blog.csdn.net/FENGQIYUNRAN/article/details/90108694

一、作用与原理

首先说说原理,我们知道使用一个公用的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 包组成与命名

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

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

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

[code]provides: nacos-config-spring-boot-autoconfigure 提供。

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

[code]<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

如上图所示,在

resources/META-INF/
spring.factories
文件中如下内容:

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

是整个配置的关键部分,其指明了

AutoConfigure实现
类是 NacosConfigAutoConfiguration类。而该类的核心作用就是实现其注解中的相关bean在某些条件下的自动装配。

[code]@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类的如下:

[code]public interface NacosConfigConstants {

String ENDPOINT_PREFIX = "nacos-config";

String ENABLED = EnableNacosConfig.CONFIG_PREFIX + "enabled";

String PREFIX = "nacos.config";

}

有引用到了另一个注解

[code]@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的装配,那么最终依赖如下

[code]spring-boot-configuration-processor 也是必然的了。
[code]<!-- @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中依赖了哪些包?

[code]<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个包:

[code]spring-boot-autoconfigure
[code]spring-boot-configuration-processor

附加额外注解解释:

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

参考: 1. 编写自己的SpringBoot-starter

            2.实现自定义Spring Boot Starter

            3.nacos-spring-boot-project

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: