From 83873e5fbefbe17ad6fce57f3ddecade9e1dce11 Mon Sep 17 00:00:00 2001 From: Freeman Lau Date: Fri, 14 Jan 2022 20:06:18 +0800 Subject: [PATCH 01/12] feature: support spring.config.import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持 spring boot >= 2.4.0 通过 spring.config.import 方式引入 nacos 配置 --- .../nacos-config-2.4.x-example/pom.xml | 53 +++ .../nacos-config-2.4.x-example/readme-zh.md | 105 ++++++ .../cloud/imports/examples/Application.java | 35 ++ .../examples/controller/UserController.java | 26 ++ .../imports/examples/model/UserConfig.java | 75 +++++ .../src/main/resources/application.yml | 17 + spring-cloud-alibaba-examples/pom.xml | 1 + .../pom.xml | 2 + .../nacos/NacosConfigAutoConfiguration.java | 3 + .../nacos/client/NacosPropertySource.java | 2 +- .../configdata/ConfigServiceIndexes.java | 32 ++ .../nacos/configdata/NacosBootstrapper.java | 119 +++++++ .../configdata/NacosConfigDataLoader.java | 144 +++++++++ .../NacosConfigDataLocationResolver.java | 303 ++++++++++++++++++ .../configdata/NacosConfigDataResource.java | 197 ++++++++++++ .../main/resources/META-INF/spring.factories | 10 +- 16 files changed, 1122 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/readme-zh.md create mode 100644 spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/Application.java create mode 100644 spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/controller/UserController.java create mode 100644 spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/model/UserConfig.java create mode 100644 spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/resources/application.yml create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/ConfigServiceIndexes.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosBootstrapper.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosConfigDataLoader.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosConfigDataLocationResolver.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosConfigDataResource.java diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/pom.xml new file mode 100644 index 000000000..09b031af6 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/pom.xml @@ -0,0 +1,53 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../pom.xml + + 4.0.0 + + nacos-config-2.4.x-example + Spring Cloud Starter Alibaba Nacos Config 2.4.x Example + Example demonstrating how to use nacos config in spring boot 2.4.x + jar + + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + true + + + + + + diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/readme-zh.md b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/readme-zh.md new file mode 100644 index 000000000..363a4cb50 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/readme-zh.md @@ -0,0 +1,105 @@ +# Nacos Config 2.4.x Example + +## 项目说明 + +Spring Boot 2.4.0 版本开始默认不启动 bootstrap 容器 +本项目演示如何在 Spring boot >= 2.4.0 版本不启用 bootstrap 容器情况下如何使用 nacos + +[Nacos](https://github.com/alibaba/Nacos) 是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 + +## 示例 + +### 如何接入 + +1. 首先,修改 pom.xml 文件,引入 Nacos Config Starter +```xml + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + +``` + + +2. 在应用的 /src/main/resources/***application.yml*** 配置文件中配置 Nacos Config 元数据 +```yaml +server: + port: 8888 +spring: + application: + name: nacos-config-import-example + cloud: + nacos: + config: + name: test.yml + file-extension: yml + # 2.4.0 新增配置 spring.config.import + config: + import: + - optional:nacos:localhost:8848 +``` + +3. 在 nacos 创建 test.yml +```yaml +configdata: + user: + age: 21 + name: freeman + map: + hobbies: + - art + - programming + intro: Hello, I'm freeman + users: + - name: dad + age: 20 + - name: mom + age: 18 +``` + +4. 完成上述操作后,应用会从 Nacos Config 中获取相应的配置,并添加在 Spring Environment 的 PropertySources 中 +```java +// controller +@RestController +public class UserController { + + @Autowired + private UserConfig userConfig; + + @GetMapping + public String get() throws JsonProcessingException { + return new ObjectMapper().writeValueAsString(userConfig); + } + +} + +// ConfigurationProperties +@ConfigurationProperties(prefix = "configdata.user") +public class UserConfig { + private String name; + private Integer age; + private Map map; + private List users; + // getters and setters ... + + public static class User { + private String name; + private Integer age; + // getters and setters ... + } +} +``` + +验证动态刷新 +访问 http://localhost:8888 +再从 nacos 修改配置, 再次访问即可验证动态配置生效 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/Application.java b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/Application.java new file mode 100644 index 000000000..7fd605331 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/Application.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.cloud.imports.examples; + +import com.alibaba.cloud.imports.examples.model.UserConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * @author freeman + */ +@SpringBootApplication +@EnableConfigurationProperties(UserConfig.class) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/controller/UserController.java b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/controller/UserController.java new file mode 100644 index 000000000..88275cd15 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/controller/UserController.java @@ -0,0 +1,26 @@ +package com.alibaba.cloud.imports.examples.controller; + +import com.alibaba.cloud.imports.examples.model.UserConfig; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * + * + * @author freeman + */ +@RestController +public class UserController { + + @Autowired + private UserConfig userConfig; + + @GetMapping + public String get() throws JsonProcessingException { + return new ObjectMapper().writeValueAsString(userConfig); + } + +} diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/model/UserConfig.java b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/model/UserConfig.java new file mode 100644 index 000000000..12df483d5 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/java/com/alibaba/cloud/imports/examples/model/UserConfig.java @@ -0,0 +1,75 @@ +package com.alibaba.cloud.imports.examples.model; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; +import java.util.Map; + +/** + * + * + * @author freeman + */ +@ConfigurationProperties(prefix = "configdata.user") +public class UserConfig { + private String name; + private Integer age; + private Map map; + private List users; + + public static class User { + private String name; + private Integer age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + + +} diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/resources/application.yml new file mode 100644 index 000000000..1478397bf --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-2.4.x-example/src/main/resources/application.yml @@ -0,0 +1,17 @@ +server: + port: 8888 +spring: + application: + name: nacos-config-import-example + cloud: + nacos: + config: + name: test.yml + file-extension: yml + group: DEFAULT_GROUP + config: + import: + - optional:nacos:localhost:8848 + - optional:nacos:localhost:8848/group_02 + - optional:nacos:localhost:8848/group_03/test.yml + - optional:nacos:localhost:8848/group_04/test.yml?refreshEnabled=false diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml index f590ce1e2..bababc199 100644 --- a/spring-cloud-alibaba-examples/pom.xml +++ b/spring-cloud-alibaba-examples/pom.xml @@ -27,6 +27,7 @@ sentinel-example/sentinel-spring-cloud-gateway-example nacos-example/nacos-discovery-example nacos-example/nacos-config-example + nacos-example/nacos-config-2.4.x-example nacos-example/nacos-gateway-example seata-example/business-service seata-example/order-service diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/pom.xml index 5815b8a46..ea0f67e25 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/pom.xml +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/pom.xml @@ -41,6 +41,8 @@ org.springframework.boot spring-boot + + 2.4.5 true diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java index 60075c9c1..a6666cb25 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java @@ -21,7 +21,9 @@ import com.alibaba.cloud.nacos.refresh.NacosRefreshHistory; import com.alibaba.cloud.nacos.refresh.NacosRefreshProperties; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -34,6 +36,7 @@ import org.springframework.context.annotation.Configuration; public class NacosConfigAutoConfiguration { @Bean + @ConditionalOnMissingBean(value = NacosConfigProperties.class, search = SearchStrategy.CURRENT) // if no import nacos:xxx, use default public NacosConfigProperties nacosConfigProperties(ApplicationContext context) { if (context.getParent() != null && BeanFactoryUtils.beanNamesForTypeIncludingAncestors( diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java index 2abdc3499..91e7fd043 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java @@ -62,7 +62,7 @@ public class NacosPropertySource extends MapPropertySource { this.isRefreshable = isRefreshable; } - NacosPropertySource(List> propertySources, String group, + public NacosPropertySource(List> propertySources, String group, String dataId, Date timestamp, boolean isRefreshable) { this(group, dataId, getSourceMap(group, dataId, propertySources), timestamp, isRefreshable); diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/ConfigServiceIndexes.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/ConfigServiceIndexes.java new file mode 100644 index 000000000..06e13c86c --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/ConfigServiceIndexes.java @@ -0,0 +1,32 @@ +/* + * Copyright 2015-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.cloud.nacos.configdata; + +import java.util.Map; + +import com.alibaba.nacos.api.config.ConfigService; + +/** + * namespace -> {@link ConfigService} mapping + * + * @author freeman + */ +public interface ConfigServiceIndexes { + + Map getIndexes(); + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosBootstrapper.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosBootstrapper.java new file mode 100644 index 000000000..8738a41a4 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosBootstrapper.java @@ -0,0 +1,119 @@ +/* + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.cloud.nacos.configdata; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.springframework.boot.BootstrapContext; +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.BootstrapRegistry.InstanceSupplier; +import org.springframework.boot.BootstrapRegistryInitializer; +import org.springframework.boot.context.config.ConfigData; +import org.springframework.boot.context.config.ConfigDataLoaderContext; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.util.Assert; + +/** + * Spring boot >= 2.4.5 supports {@link BootstrapRegistryInitializer}. + * + * @author freeman + */ +public class NacosBootstrapper implements BootstrapRegistryInitializer { + + private Function configServiceFactory; + + private LoaderInterceptor loaderInterceptor; + + static NacosBootstrapper create() { + return new NacosBootstrapper(); + } + + public NacosBootstrapper withConfigServiceFactory( + Function indexesFactory) { + this.configServiceFactory = indexesFactory; + return this; + } + + public NacosBootstrapper withLoaderInterceptor(LoaderInterceptor loaderInterceptor) { + this.loaderInterceptor = loaderInterceptor; + return this; + } + + @Override + public void initialize(BootstrapRegistry registry) { + if (configServiceFactory != null) { + registry.register(ConfigServiceIndexes.class, configServiceFactory::apply); + } + if (loaderInterceptor != null) { + registry.register(LoaderInterceptor.class, + InstanceSupplier.of(loaderInterceptor)); + } + } + + public interface LoaderInterceptor extends Function { + + } + + @FunctionalInterface + public interface LoaderInvocation extends + BiFunction { + + } + + public static class LoadContext { + + private final ConfigDataLoaderContext loaderContext; + + private final NacosConfigDataResource resource; + + private final Binder binder; + + private final LoaderInvocation invocation; + + LoadContext(ConfigDataLoaderContext loaderContext, + NacosConfigDataResource resource, Binder binder, + LoaderInvocation invocation) { + Assert.notNull(loaderContext, "loaderContext may not be null"); + Assert.notNull(resource, "resource may not be null"); + Assert.notNull(binder, "binder may not be null"); + Assert.notNull(invocation, "invocation may not be null"); + this.loaderContext = loaderContext; + this.resource = resource; + this.binder = binder; + this.invocation = invocation; + } + + public ConfigDataLoaderContext getLoaderContext() { + return this.loaderContext; + } + + public NacosConfigDataResource getResource() { + return this.resource; + } + + public Binder getBinder() { + return this.binder; + } + + public LoaderInvocation getInvocation() { + return this.invocation; + } + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosConfigDataLoader.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosConfigDataLoader.java new file mode 100644 index 000000000..aa4b4408f --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosConfigDataLoader.java @@ -0,0 +1,144 @@ +/* + * Copyright 2015-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.cloud.nacos.configdata; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.EnumSet; +import java.util.List; + +import com.alibaba.cloud.nacos.NacosConfigProperties; +import com.alibaba.cloud.nacos.NacosPropertySourceRepository; +import com.alibaba.cloud.nacos.client.NacosPropertySource; +import com.alibaba.cloud.nacos.parser.NacosDataParserHandler; +import com.alibaba.nacos.api.exception.NacosException; +import org.apache.commons.logging.Log; + +import org.springframework.boot.context.config.ConfigData; +import org.springframework.boot.context.config.ConfigData.Option; +import org.springframework.boot.context.config.ConfigData.Options; +import org.springframework.boot.context.config.ConfigDataLoader; +import org.springframework.boot.context.config.ConfigDataLoaderContext; +import org.springframework.boot.context.config.ConfigDataResourceNotFoundException; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.core.env.PropertySource; +import org.springframework.util.StringUtils; + +import static com.alibaba.cloud.nacos.configdata.NacosBootstrapper.LoadContext; +import static com.alibaba.cloud.nacos.configdata.NacosBootstrapper.LoaderInterceptor; + +/** + * @author freeman + */ +public class NacosConfigDataLoader implements ConfigDataLoader { + + private static final EnumSet