Skip to content

Commit 6c2b14c

Browse files
author
Joey Yang
committed
add demo screenshot, improve documents and example codes
1 parent 85eeafd commit 6c2b14c

File tree

12 files changed

+152
-101
lines changed

12 files changed

+152
-101
lines changed

README-zh.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
<p align="center">
1+
## Spring Boot Dynamic Config
2+
3+
<p align="left">
4+
<br>
25
<a href="https://github.com/code2life/spring-boot-dynamic-config"><img src="https://github.com/code2life/spring-boot-dynamic-config/actions/workflows/gradle.yml/badge.svg" /></a>
3-
<a href="https://github.com/code2life/spring-boot-dynamic-config/actions/workflows/gradle.yml">coverage<img src=".github/badges/jacoco.svg" /></a>
6+
<a href="https://github.com/code2life/spring-boot-dynamic-config/actions/workflows/gradle.yml"><img src=".github/badges/jacoco.svg" /></a>
47
<a href="https://codebeat.co/projects/github-com-code2life-spring-boot-dynamic-config-main"><img alt="codebeat badge" src="https://codebeat.co/badges/ea7b2127-62f3-45f4-9f38-55f8203c0121" /></a>
8+
<br>
59
</p>
610

7-
## Spring Boot Dynamic Config
8-
911
一个注解实现SpringBoot应用的**动态配置**,配置热重载最简洁的方案。
1012

1113
[English](https://github.com/Code2Life/spring-boot-dynamic-config/blob/main/README.md) [简体中文](https://github.com/Code2Life/spring-boot-dynamic-config/blob/main/README-zh.md)
1214

1315
- :heart: **无侵入**,完全兼容SpringBoot原生的配置获取方式(@Value / @ConfigurationProperties
1416
- :zap: **超轻量,超快响应**, 不依赖SpringBoot核心库以外的任何三方库
1517
- :grinning: **极易使用**, 只提供一个简单的注解: @DynamicConfig;一个事件:ConfigurationChangedEvent
16-
-在K8S集群中运行SpringBoot/SpringCloud应用,与K8S ConfigMap完美结合的方案
18+
-在K8S集群中和K8S ConfigMap完美结合的SpringBoot/SpringCloud应用的动态配置方式
1719

1820
#### 相比于spring-cloud-starter-config:
1921

@@ -27,6 +29,8 @@
2729

2830
## 演示
2931

32+
<img src="example/demo.gif" alt="Demo" />
33+
3034
## 快速开始
3135

3236
### 步骤一:添加依赖spring-boot-dynamic-config

README.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,44 @@
1-
<p align="center">
1+
## Spring Boot Dynamic Config
2+
3+
<p align="left">
4+
<br>
25
<a href="https://github.com/code2life/spring-boot-dynamic-config"><img src="https://github.com/code2life/spring-boot-dynamic-config/actions/workflows/gradle.yml/badge.svg" /></a>
3-
<a href="https://github.com/code2life/spring-boot-dynamic-config/actions/workflows/gradle.yml">coverage<img src=".github/badges/jacoco.svg" /></a>
6+
<a href="https://github.com/code2life/spring-boot-dynamic-config/actions/workflows/gradle.yml"><img src=".github/badges/jacoco.svg" /></a>
47
<a href="https://codebeat.co/projects/github-com-code2life-spring-boot-dynamic-config-main"><img alt="codebeat badge" src="https://codebeat.co/badges/ea7b2127-62f3-45f4-9f38-55f8203c0121" /></a>
8+
<br>
59
</p>
610

7-
## Spring Boot Dynamic Config
8-
911
Hot-reload your SpringBoot configurations, with just a '@DynamicConfig' annotation, the simplest solution, ever.
1012

1113
[English](https://github.com/Code2Life/spring-boot-dynamic-config/blob/main/README.md) [简体中文](https://github.com/Code2Life/spring-boot-dynamic-config/blob/main/README-zh.md)
1214

1315
- :heart: **Non-intrusive**, compatible with SpringBoot native ways (@Value, @ConfigurationProperties)
1416
- :zap: **Lightweight & Blazing Fast**, depend on nothing but SpringBoot core libs
1517
- :grinning: **Extremely easy to use**, only provide an annotation: @DynamicConfig, an event: ConfigurationChangedEvent
16-
- ☸ Perfect solution for running SpringBoot on Kubernetes, combine with K8S ConfigMap
17-
18+
- ☸ Perfect solution for host-reloading configurations of SpringBoot application on Kubernetes, with K8S ConfigMap
1819

1920
#### Compare with spring-cloud-starter-config
2021

2122
- No need for config server
2223
- No SpringCloud dependency and @RefreshScope annotation, won't destroy and rebuild beans
2324

2425
#### Compare with Alibaba Nacos / Ctripcorp Apollo
26+
2527
- No need for Nacos/Apollo server
2628
- No need for learning Annotations, Client APIs, etc.
2729

2830
## Demo
2931

32+
<img src="example/demo.gif" alt="Demo" />
33+
3034
## Getting Started
3135

3236
### Step1. Add spring-boot-dynamic-config Dependency
3337

3438
Maven
3539

3640
```xml
41+
3742
<dependency>
3843
<groupId>top.code2life</groupId>
3944
<artifactId>spring-boot-dynamic-config</artifactId>
@@ -106,9 +111,9 @@ import java.util.Map;
106111
public class TestConfigurationProperties {
107112

108113
private String str;
109-
114+
110115
private Double doubleVal;
111-
116+
112117
private Map<String, Object> mapVal;
113118
}
114119

@@ -127,24 +132,27 @@ public class TestConfigurationProperties {
127132
java -jar your-spring-boot-app.jar --spring.config.location=/path/to/config
128133
```
129134

130-
Then, modifications on /path/to/config/application-<some-profile>.yml will take effect and reflect on @DynamicConfig beans **immediately**.
135+
Then, modifications on /path/to/config/application-<some-profile>.yml will take effect and reflect on @DynamicConfig
136+
beans **immediately**.
131137

132138
### Best Practices
139+
133140
- Configuration as Code, Everything as Code
134141
- Configurations should be maintained in Git, rather than any GUI system.
135142
- Configurations should be applied to dev/production environments by Continuous Integration system.
136-
- Git-Based DevOps workflow is the modern way of operating services, at scale.
143+
- Git-Based DevOps workflow is the modern way of operating services, at scale.
137144

138145
## Implementation
139146

140147
1. Bean 'DynamicConfigPropertiesWatcher' will be initialized if 'spring.config.location' is specified
141148
2. Bean 'DynamicConfigBeanPostProcessor' will be initialized if 'DynamicConfigPropertiesWatcher' exists
142-
3. DynamicConfigBeanPostProcessor collects beans' metadata after initializing
149+
3. DynamicConfigBeanPostProcessor collects beans' metadata after initializing
143150
4. DynamicConfigPropertiesWatcher watches configuration directory, then replace PropertySource in Environment on changes
144151
5. DynamicConfigPropertiesWatcher publishes 'ConfigurationChangedEvent'
145152
6. DynamicConfigBeanPostProcessor listens 'ConfigurationChangedEvent', calculate diff
146153
7. For each changed key, DynamicConfigBeanPostProcessor will use preserved bean metadata to check if it's related
147-
8. After filtering related beans, it will use reflect API or ConfigurationPropertiesBindingPostProcessor API to modify fields of existing bean
154+
8. After filtering related beans, it will use reflect API or ConfigurationPropertiesBindingPostProcessor API to modify
155+
fields of existing bean
148156

149157
## Compatibility
150158

@@ -158,10 +166,12 @@ Any SpringBoot/SpringCloud application within following SpringBoot version can u
158166
- √ SpringBoot 2.0.x
159167
- X SpringBoot 1.5.x and Lower
160168

161-
NOTES:
169+
NOTES:
170+
162171
- For SpringBoot 2.0.x, use Junit4 rather than Junit5.
163172
- This lib does not depend on ANY other libs except SpringBoot core libs.
164173

165174
## License
166175

167-
Spring Boot Dynamic Config is Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
176+
Spring Boot Dynamic Config is Open Source software released under
177+
the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].

example/build.gradle

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,29 @@ buildscript {
33
springBootVersion = '2.1.0.RELEASE'
44
}
55
}
6-
76
plugins {
87
id 'org.springframework.boot' version "${springBootVersion}"
98
id 'java-library'
109
}
1110

1211
group = 'com.example'
13-
version = '0.0.1-SNAPSHOT'
12+
version = '1.0.0'
1413
sourceCompatibility = JavaVersion.VERSION_1_8
1514

16-
configurations {
17-
compileOnly {
18-
extendsFrom annotationProcessor
19-
}
20-
}
21-
2215
bootJar {
2316
mainClassName "com.example.demo.DemoApplication"
2417
}
2518

2619
repositories {
2720
mavenCentral()
28-
maven {
29-
url "https://s01.oss.sonatype.org/content/repositories/snapshots"
30-
}
3121
}
3222

3323
dependencies {
3424
api platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
3525

3626
compileOnly 'org.projectlombok:lombok:1.18.20'
3727
annotationProcessor 'org.projectlombok:lombok:1.18.20'
38-
39-
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
4028
implementation 'org.springframework.boot:spring-boot-starter-web'
41-
implementation 'top.code2life:spring-boot-dynamic-config:1.0.3-SNAPSHOT'
42-
43-
testImplementation 'org.springframework.boot:spring-boot-starter-test'
44-
}
4529

46-
test {
47-
useJUnitPlatform()
48-
}
30+
implementation 'top.code2life:spring-boot-dynamic-config:1.0.4'
31+
}

example/demo.gif

337 KB
Loading

example/src/main/java/com/example/demo/DemoConfigProperties.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package com.example.demo;
22

3+
import com.fasterxml.jackson.annotation.JsonInclude;
34
import lombok.Data;
45
import org.springframework.boot.context.properties.ConfigurationProperties;
56
import org.springframework.context.annotation.Configuration;
6-
import org.springframework.core.Ordered;
7-
import org.springframework.core.annotation.Order;
87
import top.code2life.config.DynamicConfig;
98

109
import java.util.List;
@@ -17,6 +16,7 @@
1716
@DynamicConfig
1817
@ConfigurationProperties(prefix = "dynamic.prop")
1918
@Configuration
19+
@JsonInclude(JsonInclude.Include.NON_NULL)
2020
public class DemoConfigProperties {
2121

2222
private String str;
@@ -25,8 +25,6 @@ public class DemoConfigProperties {
2525

2626
private List<Integer> listVal;
2727

28-
private List<DemoConfigProperties> nestedList;
29-
3028
private Map<String, DemoConfigProperties> nestedMap;
3129

3230
private DemoConfigProperties nested;
Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package com.example.demo;
22

33
import lombok.RequiredArgsConstructor;
4-
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.beans.BeanUtils;
55
import org.springframework.web.bind.annotation.RequestMapping;
66
import org.springframework.web.bind.annotation.RequestMethod;
7-
import org.springframework.web.bind.annotation.RequestParam;
87
import org.springframework.web.bind.annotation.RestController;
9-
import top.code2life.config.DynamicConfig;
108
import top.code2life.config.FeatureGate;
119

12-
import java.util.Set;
10+
import java.util.HashMap;
11+
import java.util.Map;
1312

1413
/**
1514
* @author Code2Life
@@ -19,10 +18,6 @@
1918
public class DemoController {
2019

2120

22-
@DynamicConfig
23-
@Value("#{@featureGate.convert('${some.feature.beta-list}')}")
24-
private Set<String> betaUserList;
25-
2621
private final DemoConfigProperties demoConfigProps;
2722

2823
private final DemoDynamicValue demoDynamicValue;
@@ -31,23 +26,14 @@ public class DemoController {
3126

3227
@RequestMapping(value = "/demo", method = RequestMethod.GET)
3328
public Object getDemoConfigProps() {
34-
DemoConfigProperties resp = new DemoConfigProperties();
35-
resp.setStr(demoConfigProps.getStr());
36-
resp.setListVal(demoConfigProps.getListVal());
37-
resp.setMapVal(demoConfigProps.getMapVal());
38-
resp.setNestedList(demoConfigProps.getNestedList());
39-
resp.setNestedMap(demoConfigProps.getNestedMap());
40-
resp.setNested(demoConfigProps.getNested());
29+
Map<String, Object> resp = new HashMap<>(4);
30+
DemoConfigProperties configurationProps = new DemoConfigProperties();
31+
BeanUtils.copyProperties(demoConfigProps, configurationProps);
32+
resp.put("valuePlaceHolder", demoDynamicValue.getDynamicHello());
33+
resp.put("valueSpringEL", demoDynamicValue.getBetaUserList());
34+
resp.put("someUserInWhiteList", featureGate.isFeatureEnabled(demoDynamicValue.getBetaUserList(), "user4"));
35+
resp.put("betaFeatureEnabled", featureGate.isFeatureEnabled("beta.enabled"));
36+
resp.put("configurationPropertiesObj", configurationProps);
4137
return resp;
4238
}
43-
44-
@RequestMapping(value = "/demo2", method = RequestMethod.GET)
45-
public String getDemoConfig2() {
46-
return demoDynamicValue.getDynamicHello();
47-
}
48-
49-
@RequestMapping(value = "/demo3", method = RequestMethod.GET)
50-
public boolean isUserInBetaList(@RequestParam String id) {
51-
return featureGate.isFeatureEnabled(betaUserList, id);
52-
}
5339
}

example/src/main/java/com/example/demo/DemoDynamicValue.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22

33
import lombok.Getter;
44
import org.springframework.beans.factory.annotation.Value;
5-
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.stereotype.Component;
66
import top.code2life.config.DynamicConfig;
77

8+
import java.util.Set;
9+
810
/**
911
* @author Code2Life
1012
**/
1113
@Getter
1214
@DynamicConfig
13-
@Configuration
15+
@Component
1416
public class DemoDynamicValue {
1517

1618
@Value("${dynamic.hello-world}")
1719
private String dynamicHello;
20+
21+
@Value("#{@featureGate.convert('${some.feature.beta-list}')}")
22+
private Set<String> betaUserList;
1823
}

example/src/main/java/com/example/demo/DemoSubConfig.java

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
1+
# Run application with specified config location:
2+
# eg: java -jar app.jar --spring.config.location=src/main/resources/
3+
# then, it just works !
4+
dynamic.hello-world: Hello @DynamicConfig Awesome !!!
5+
beta.enabled: false
6+
some:
7+
feature:
8+
beta-list: user1, user2, user3,
19
dynamic:
210
prop:
3-
str: hello
11+
str: Hello @ConfigurationProperties !
412
map-val:
513
k1: v1
14+
615
k2: v2
7-
list-val:
16+
listVal:
817
- 1
918
- 2
1019
- 3
11-
nested-list:
12-
- str: hello-inner
13-
map-val:
14-
k2-inner: v1-inner
15-
nested_map:
16-
m1:
17-
str: hello-map
18-
list-val: [5,6,7]
19-
2020
nested:
21-
str: hello-recursive-1
22-
map-val:
23-
x1: y1
21+
str: Hello Recursive ! Awesome !!!

example/src/main/resources/application.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ spring:
33
include:
44
- another-profile
55

6-
dynamic:
7-
hello-world: Hello World !!!
8-
some:
9-
feature:
10-
beta-list: user1, user2, user3
11-
126
logging:
137
level:
148
top.code2life: debug

0 commit comments

Comments
 (0)