从nacos-spring-boot-project角度全面看Springboot Starter
一、作用与原理
首先说说原理,我们知道使用一个公用的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
- <springBoot>(二)(入门篇)Eclipse中右击新增没有Spring Starter Project选项处理办法
- springboot学习1-使用 Spring Starter Project快速创建基于spring-boot的web项目
- 详解spring boot starter redis配置文件
- spring-boot-starter-actuator /info获取空信息
- springboot之自定义starter
- spring-boot-starter-log4j unkown 问题解决
- Spring Boot的启动器Starter详解
- jtwig-spring-boot-starter ,Defining prefix/suffix of the resolver, resource not found
- Springboot starter web service consume a soap message
- Spring Boot dubbo 通配符的匹配很全面, 但无法找到元素 'dubbo:registry' 的声明
- SpringBoot入坑笔记之spring-boot-starter-web 配置文件的使用
- spring-boot-starter-redis 集成
- Spring Boot中Starter是什么
- 最新史上最全面SpringBoot全套
- Spring Boot学习总结(11)——SpringBoot的Starter依赖包及作用
- spring-boot-starter-data-redis 翻译官方文档 8.4 - 8.6
- Spring Boot学习(二):Spring Boot的启动器Starter详解
- Spring Boot:定制自己的starter
- Spring Boot的启动器Starter详解
- spring-boot-starter-web更换默认Tomcat容器的方法