소스 검색

init config loader

张泳健 5 년 전
커밋
7a6d49a09a

+ 91 - 0
.gitignore 파일 보기

@@ -0,0 +1,91 @@
1
+# Created by .ignore support plugin (hsz.mobi)
2
+### Maven template
3
+target/
4
+pom.xml.tag
5
+pom.xml.releaseBackup
6
+pom.xml.versionsBackup
7
+pom.xml.next
8
+release.properties
9
+dependency-reduced-pom.xml
10
+buildNumber.properties
11
+.mvn/timing.properties
12
+# https://github.com/takari/maven-wrapper#usage-without-binary-jar
13
+.mvn/wrapper/maven-wrapper.jar
14
+
15
+### JetBrains template
16
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
17
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
18
+
19
+# User-specific stuff
20
+.idea/*
21
+
22
+# Gradle and Maven with auto-import
23
+# When using Gradle or Maven with auto-import, you should exclude module files,
24
+# since they will be recreated, and may cause churn.  Uncomment if using
25
+# auto-import.
26
+# .idea/modules.xml
27
+# .idea/*.iml
28
+# .idea/modules
29
+*.iml
30
+*.log
31
+logs/
32
+# *.ipr
33
+
34
+# CMake
35
+cmake-build-*/
36
+
37
+# Mongo Explorer plugin
38
+.idea/**/mongoSettings.xml
39
+
40
+# File-based project format
41
+*.iws
42
+
43
+# IntelliJ
44
+out/
45
+
46
+# mpeltonen/sbt-idea plugin
47
+.idea_modules/
48
+
49
+# JIRA plugin
50
+atlassian-ide-plugin.xml
51
+
52
+# Cursive Clojure plugin
53
+.idea/replstate.xml
54
+
55
+# Crashlytics plugin (for Android Studio and IntelliJ)
56
+com_crashlytics_export_strings.xml
57
+crashlytics.properties
58
+crashlytics-build.properties
59
+fabric.properties
60
+
61
+# Editor-based Rest Client
62
+.idea/httpRequests
63
+
64
+# Android studio 3.1+ serialized cache file
65
+.idea/caches/build_file_checksums.ser
66
+
67
+### Java template
68
+# Compiled class file
69
+*.class
70
+
71
+# Log file
72
+*.log
73
+
74
+# BlueJ files
75
+*.ctxt
76
+
77
+# Mobile Tools for Java (J2ME)
78
+.mtj.tmp/
79
+
80
+# Package Files #
81
+*.jar
82
+*.war
83
+*.nar
84
+*.ear
85
+*.zip
86
+*.tar.gz
87
+*.rar
88
+
89
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
90
+hs_err_pid*
91
+

+ 3 - 0
README.md 파일 보기

@@ -0,0 +1,3 @@
1
+# 常用整理工具
2
+
3
+## [仿SpringBoot属性文件加载](./configuration-loader/README.md)

+ 91 - 0
configuration-loader/README.md 파일 보기

@@ -0,0 +1,91 @@
1
+# 仿SpringBoot属性文件加载
2
+
3
+> 一般Java应用程序中,用于加载属性文件
4
+
5
+## 依赖
6
+
7
+### 1.必须的依赖
8
+```xml
9
+<dependency>
10
+    <groupId>com.fasterxml.jackson.core</groupId>
11
+    <artifactId>jackson-databind</artifactId>
12
+    <version>${jackson.version}</version>
13
+</dependency>
14
+```
15
+### 2. 可先依赖(至少有一个,或者两个都加)
16
+```xml
17
+<!-- yml/yaml -->
18
+<dependency>
19
+    <groupId>com.fasterxml.jackson.dataformat</groupId>
20
+    <artifactId>jackson-dataformat-yaml</artifactId>
21
+    <version>${jackson.version}</version>
22
+</dependency>
23
+
24
+<!-- properties -->
25
+<dependency>
26
+    <groupId>com.fasterxml.jackson.dataformat</groupId>
27
+    <artifactId>jackson-dataformat-properties</artifactId>
28
+    <version>${jackson.version}</version>
29
+</dependency>
30
+```
31
+如果两个依赖都加的话,文件后缀顺序为:
32
+
33
+1. yml - jackson-dataformat-yaml
34
+2. yaml - jackson-dataformat-yaml
35
+3. properties - jackson-dataformat-properties
36
+
37
+## 用法
38
+ConnectorProperties.java
39
+```java
40
+@Data
41
+public class ConnectorProperties {
42
+
43
+    private Connector connector;
44
+    private Transfer transfer;
45
+    private Redis redis;
46
+
47
+    @Data
48
+    public static class Connector {
49
+        private Integer port;
50
+    }
51
+
52
+    @Data
53
+    public static class Transfer {
54
+        private List<String> urls;
55
+    }
56
+
57
+    @Data
58
+    public static class Redis {
59
+        private String host;
60
+        private Integer port;
61
+        private String password;
62
+        private int database;
63
+    }
64
+
65
+}
66
+```
67
+application.yml
68
+```yaml
69
+profile:
70
+  active: dev
71
+```
72
+
73
+application-dev.yml
74
+```yaml
75
+connector:
76
+  port: 9081
77
+transfer:
78
+  # all transfer services url
79
+  urls: [127.0.0.1:9082]
80
+redis:
81
+  host: ${connector.port}
82
+  port: 6379
83
+  password:
84
+  dababase: 0
85
+```
86
+
87
+默认的配置文件为`application.yml/application.yaml/application.properties`
88
+
89
+```java
90
+ConnectorProperties connectorProperties = ConfigLoader.parseConfig(ConnectorProperties.class);
91
+```

+ 57 - 0
configuration-loader/pom.xml 파일 보기

@@ -0,0 +1,57 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <parent>
6
+        <artifactId>lang-utils</artifactId>
7
+        <groupId>me.yuxiaoyao.lang</groupId>
8
+        <version>1.0.0</version>
9
+    </parent>
10
+    <modelVersion>4.0.0</modelVersion>
11
+
12
+    <artifactId>configuration-loader</artifactId>
13
+
14
+
15
+    <dependencies>
16
+
17
+        <!-- json -->
18
+        <dependency>
19
+            <groupId>com.fasterxml.jackson.core</groupId>
20
+            <artifactId>jackson-databind</artifactId>
21
+            <scope>provided</scope>
22
+        </dependency>
23
+
24
+        <!-- jackson data format -->
25
+        <dependency>
26
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
27
+            <artifactId>jackson-dataformat-yaml</artifactId>
28
+            <scope>provided</scope>
29
+        </dependency>
30
+        <dependency>
31
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
32
+            <artifactId>jackson-dataformat-properties</artifactId>
33
+            <scope>provided</scope>
34
+        </dependency>
35
+
36
+        <dependency>
37
+            <groupId>org.junit.jupiter</groupId>
38
+            <artifactId>junit-jupiter-api</artifactId>
39
+            <scope>test</scope>
40
+        </dependency>
41
+    </dependencies>
42
+
43
+    <build>
44
+        <plugins>
45
+            <plugin>
46
+                <groupId>org.apache.maven.plugins</groupId>
47
+                <artifactId>maven-compiler-plugin</artifactId>
48
+                <configuration>
49
+                    <source>8</source>
50
+                    <target>8</target>
51
+                </configuration>
52
+            </plugin>
53
+        </plugins>
54
+    </build>
55
+
56
+
57
+</project>

+ 370 - 0
configuration-loader/src/main/java/me/yuxiaoyao/config/loader/ConfigLoader.java 파일 보기

@@ -0,0 +1,370 @@
1
+package me.yuxiaoyao.config.loader;
2
+
3
+import com.fasterxml.jackson.annotation.JsonInclude;
4
+import com.fasterxml.jackson.core.JsonFactory;
5
+import com.fasterxml.jackson.core.type.TypeReference;
6
+import com.fasterxml.jackson.databind.DeserializationFeature;
7
+import com.fasterxml.jackson.databind.ObjectMapper;
8
+
9
+import java.io.*;
10
+import java.util.*;
11
+
12
+/**
13
+ * @author Kerry on 19/10/09
14
+ */
15
+
16
+public class ConfigLoader {
17
+
18
+    private static final String PROFILE = "profile";
19
+    private static final String ACTIVE = "active";
20
+
21
+    public static final String ACTIVE_PROFILE = PROFILE + "." + ACTIVE;
22
+
23
+    /**
24
+     * 如果想修改,可以通过反射修改
25
+     */
26
+    private static String DEFAULT_FILE_CONFIG_FOLDER = "config";
27
+    private static String DEFAULT_APPLICATION = "application";
28
+
29
+    private static String START_PLACEHOLDER = "${";
30
+    private static String END_PLACEHOLDER = "}";
31
+
32
+
33
+    private enum ConfigProtocol {
34
+        /**
35
+         * 文件
36
+         */
37
+        FILE(1),
38
+        /**
39
+         * 类路径
40
+         */
41
+        CLASSPATH(0);
42
+
43
+        /**
44
+         * 配置文件优先级
45
+         */
46
+        private int order;
47
+
48
+        ConfigProtocol(int order) {
49
+            this.order = order;
50
+        }
51
+
52
+        public static List<ConfigProtocol> sortList() {
53
+            List<ConfigProtocol> configProtocols = new ArrayList<>(Arrays.asList(values()));
54
+            configProtocols.sort((o1, o2) -> o2.order - o1.order);
55
+            return configProtocols;
56
+        }
57
+    }
58
+
59
+    public interface ConfigListener {
60
+        /**
61
+         * 回调Profile
62
+         *
63
+         * @param profile
64
+         */
65
+        void activeProfile(String profile);
66
+    }
67
+
68
+    /**
69
+     * 返回数据流
70
+     *
71
+     * @param configProtocol
72
+     * @param path
73
+     * @return 结果可能为 null
74
+     */
75
+    private static InputStream getConfigFileAsStream(ConfigProtocol configProtocol, String path) {
76
+
77
+        switch (configProtocol) {
78
+            case FILE:
79
+                try {
80
+                    return new FileInputStream(new File(DEFAULT_FILE_CONFIG_FOLDER + File.separator + path));
81
+                } catch (FileNotFoundException ignored) {
82
+                }
83
+                return null;
84
+            case CLASSPATH:
85
+                return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
86
+            default:
87
+                throw new Error("not support this path = " + path);
88
+        }
89
+    }
90
+
91
+
92
+    public static <T> T parseConfig(Class<T> clazz) {
93
+        return parseConfig(null, clazz, null);
94
+    }
95
+
96
+    public static <T> T parseConfig(String[] args, Class<T> clazz, ConfigListener configListener) {
97
+        LinkedHashMap map = loaderConfig(args, configListener);
98
+        return getObjectMapper().convertValue(map, clazz);
99
+    }
100
+
101
+
102
+    public static <T> T parseConfig(TypeReference<T> toValueTypeRef) {
103
+        return parseConfig(null, toValueTypeRef, null);
104
+    }
105
+
106
+    public static <T> T parseConfig(String[] args, TypeReference<T> toValueTypeRef, ConfigListener configListener) {
107
+        LinkedHashMap map = loaderConfig(args, configListener);
108
+        return getObjectMapper().convertValue(map, toValueTypeRef);
109
+    }
110
+
111
+    private static ObjectMapper getObjectMapper() {
112
+        ObjectMapper objectMapper = new ObjectMapper();
113
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
114
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
115
+        return objectMapper;
116
+    }
117
+
118
+    /**
119
+     * 加载默认配置文件
120
+     *
121
+     * @return
122
+     */
123
+    public static LinkedHashMap loaderConfig() {
124
+        return loaderConfig(null, null);
125
+    }
126
+
127
+    /**
128
+     * 加载指定 profile 的配置文件
129
+     *
130
+     * @param args
131
+     * @return
132
+     */
133
+    public static LinkedHashMap loaderConfig(String[] args, ConfigListener configListener) {
134
+
135
+        List<ConfigProtocol> configProtocols = ConfigProtocol.sortList();
136
+        LinkedHashMap defaultProperties = null;
137
+        ConfigProtocol matchProtocol = null;
138
+        for (ConfigProtocol protocol : configProtocols) {
139
+            LinkedHashMap temp = selectConfig(protocol, DEFAULT_APPLICATION);
140
+            if (temp != null) {
141
+                defaultProperties = temp;
142
+                matchProtocol = protocol;
143
+                break;
144
+            }
145
+        }
146
+        if (defaultProperties == null) {
147
+            return null;
148
+        }
149
+
150
+        String profile = getProfileByArgs(args);
151
+
152
+        if (profile == null || profile.length() == 0) {
153
+            profile = getActiveProfile(defaultProperties);
154
+            if (configListener != null) {
155
+                configListener.activeProfile(profile);
156
+            }
157
+        }
158
+        LinkedHashMap map = selectConfig(matchProtocol, DEFAULT_APPLICATION + "-" + profile);
159
+        if (map != null) {
160
+            removeActiveProfile(map);
161
+            //noinspection unchecked
162
+            defaultProperties.putAll(map);
163
+        }
164
+        return propertiesHandler(defaultProperties);
165
+    }
166
+
167
+
168
+    private static LinkedHashMap selectConfig(ConfigProtocol configProtocol, String filePrefix) {
169
+        InputStream configFileAsStream = getConfigFileAsStream(configProtocol, filePrefix + ".yml");
170
+        if (configFileAsStream == null) {
171
+            configFileAsStream = getConfigFileAsStream(configProtocol, filePrefix + ".yaml");
172
+        }
173
+        Object yamlFactory = null;
174
+        try {
175
+            Class<?> factory = Class.forName("com.fasterxml.jackson.dataformat.yaml.YAMLFactory");
176
+            yamlFactory = factory.newInstance();
177
+        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
178
+            // e.printStackTrace();
179
+        }
180
+        ObjectMapper objectMapper = null;
181
+        if (configFileAsStream != null && yamlFactory != null) {
182
+            objectMapper = new ObjectMapper((JsonFactory) yamlFactory);
183
+        } else {
184
+            configFileAsStream = getConfigFileAsStream(configProtocol, filePrefix + ".properties");
185
+            try {
186
+                Class<?> javaProps = Class.forName("com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper");
187
+                objectMapper = (ObjectMapper) javaProps.newInstance();
188
+            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
189
+                // e.printStackTrace();
190
+            }
191
+        }
192
+        if (objectMapper == null) {
193
+            throw new Error("jackson dependencies at least container [jackson-dataformat-yaml] or [jackson-dataformat-properties].");
194
+        }
195
+        if (configFileAsStream == null) {
196
+            return null;
197
+        }
198
+        try {
199
+            return objectMapper.readValue(configFileAsStream, LinkedHashMap.class);
200
+        } catch (IOException e) {
201
+            e.printStackTrace();
202
+        }
203
+        return null;
204
+    }
205
+
206
+
207
+    private static String getActiveProfile(Map fileMap) {
208
+        Object profile = fileMap.get(PROFILE);
209
+        if (profile instanceof Map) {
210
+            final Object o = ((Map) profile).get(ACTIVE);
211
+            if (o != null) {
212
+                return (String) o;
213
+            }
214
+        }
215
+        return null;
216
+    }
217
+
218
+    private static void removeActiveProfile(Map map) {
219
+        Object profile = map.get(PROFILE);
220
+        if (profile instanceof Map) {
221
+            ((Map) profile).remove(ACTIVE);
222
+        }
223
+    }
224
+
225
+    /**
226
+     * 属性处理,目前暂时不处理,替换符号等操作
227
+     *
228
+     * @param map
229
+     * @return
230
+     */
231
+    private static LinkedHashMap propertiesHandler(LinkedHashMap map) {
232
+        Map<Object, String> preHandler = new LinkedHashMap<>();
233
+        // find placeholder value
234
+        findPreResolveProperties(map, preHandler, null);
235
+        // replace value
236
+        Map<Object, Object> objectObjectMap = ConfigLoader.resolveProperties(map, preHandler);
237
+        // replace to map
238
+        replaceProperties(map, objectObjectMap);
239
+        return map;
240
+    }
241
+
242
+
243
+    private static Object getProperties(Map map, String key) {
244
+        final int i = key.indexOf(".");
245
+        if (i == -1) {
246
+            return map.get(key);
247
+        }
248
+        String currentKey = key.substring(0, i);
249
+        Object o = map.get(currentKey);
250
+
251
+        if (o instanceof Map) {
252
+            return getProperties((Map) o, (key.substring(i + 1)));
253
+        }
254
+        return o;
255
+    }
256
+
257
+    @SuppressWarnings("unchecked")
258
+    private static void setProperties(Map map, String key, Object value) {
259
+        if (map == null) {
260
+            return;
261
+        }
262
+        final int i = key.indexOf(".");
263
+        if (i == -1) {
264
+            map.put(key, value);
265
+            return;
266
+        }
267
+        String currentKey = key.substring(0, i);
268
+        Object o = map.get(currentKey);
269
+        if (o instanceof Map) {
270
+            setProperties((Map) o, (key.substring(i + 1)), value);
271
+        } else {
272
+            map.put(key, value);
273
+        }
274
+    }
275
+
276
+
277
+    public static void replaceProperties(Map map, Map<Object, ?> handlerMap) {
278
+
279
+        for (Object o : handlerMap.keySet()) {
280
+            Object value = handlerMap.get(o);
281
+            setProperties(map, o.toString(), value);
282
+
283
+        }
284
+
285
+    }
286
+
287
+
288
+    /**
289
+     * 把带的占位符的值替换为真实值
290
+     *
291
+     * @param map
292
+     * @param preHandlerMap
293
+     * @return
294
+     */
295
+    public static Map<Object, Object> resolveProperties(Map map, Map<Object, ?> preHandlerMap) {
296
+        Map<Object, Object> processedMap = new LinkedHashMap<>(preHandlerMap.size());
297
+
298
+        for (Object o : preHandlerMap.keySet()) {
299
+            Object value = preHandlerMap.get(o);
300
+
301
+            if (value instanceof String) {
302
+                // source value
303
+                String str = ((String) value).trim();
304
+                str = str.substring(START_PLACEHOLDER.length(), str.length() - END_PLACEHOLDER.length());
305
+                String[] defaultValue = str.split(":");
306
+                Object properties;
307
+                if (defaultValue.length > 1) {
308
+                    properties = getProperties(map, defaultValue[0]);
309
+                } else {
310
+                    properties = getProperties(map, defaultValue[0]);
311
+                }
312
+                // set default value
313
+                if (properties == null) {
314
+                    if (defaultValue.length > 1) {
315
+                        properties = defaultValue[1];
316
+                    } else {
317
+                        properties = value;
318
+                    }
319
+                }
320
+                /// System.out.println("key = [" + value + "] - value = [" + properties + "]");
321
+                processedMap.put(o, properties);
322
+            }
323
+        }
324
+        return processedMap;
325
+    }
326
+
327
+    /**
328
+     * 查找所有具有占位符号的值,目前不支持数组
329
+     * 起始符 {@link ConfigLoader#START_PLACEHOLDER}
330
+     * 结束符 {@link ConfigLoader#END_PLACEHOLDER}
331
+     *
332
+     * @param map
333
+     * @param result 查找结果接收
334
+     * @return
335
+     */
336
+    public static void findPreResolveProperties(Map map, Map<Object, String> result, String prefix) {
337
+
338
+        for (Object key : map.keySet()) {
339
+            Object value = map.get(key);
340
+            if (value instanceof Map) {
341
+                findPreResolveProperties((Map) value, result, prefix == null ? key.toString() : prefix + "." + key.toString());
342
+            } else if (value instanceof String) {
343
+                String str = ((String) value).trim();
344
+                if (str.startsWith(START_PLACEHOLDER) && str.endsWith(END_PLACEHOLDER)) {
345
+                    if (prefix == null) {
346
+                        result.put(key.toString(), str);
347
+                    } else {
348
+                        result.put(prefix + "." + key.toString(), str);
349
+                    }
350
+                }
351
+            }
352
+        }
353
+        //prefix = "";
354
+    }
355
+
356
+
357
+    private static String getProfileByArgs(String[] args) {
358
+        if (args != null && args.length > 0) {
359
+            for (String arg : args) {
360
+                arg = arg.trim();
361
+                if (arg.startsWith("--profile=")) {
362
+                    return arg.substring(10);
363
+                }
364
+            }
365
+        }
366
+        return null;
367
+    }
368
+
369
+
370
+}

+ 60 - 0
configuration-loader/src/test/java/me/yuxiaoyao/config/loader/ConfigLoaderTest.java 파일 보기

@@ -0,0 +1,60 @@
1
+package me.yuxiaoyao.config.loader;
2
+
3
+import org.junit.jupiter.api.Test;
4
+
5
+import java.util.HashMap;
6
+import java.util.LinkedHashMap;
7
+import java.util.Map;
8
+
9
+
10
+
11
+
12
+class ConfigLoaderTest {
13
+
14
+    @Test
15
+    public void resolveProperties() {
16
+        Map<String, Object> map = new LinkedHashMap<>();
17
+        map.put("port", 8080);
18
+        map.put("host", "yuxiaoyao.me");
19
+
20
+        Map<String, Object> redis = new LinkedHashMap<>();
21
+        redis.put("host", "127.0.0.1");
22
+        redis.put("redis-port", 6379);
23
+        redis.put("password", "----***____");
24
+        map.put("redis", redis);
25
+
26
+        Map<String, Object> app = new LinkedHashMap<>();
27
+        app.put("server", "--");
28
+        app.put("sp", "${redis.host}");
29
+        map.put("app", app);
30
+
31
+        Map<String, Object> testInfo = new LinkedHashMap<>();
32
+        testInfo.put("no", "test-server");
33
+        testInfo.put("protocol-no", "${app.sp}");
34
+        testInfo.put("protocol-server", "${app.server}");
35
+        testInfo.put("test-default", "${app.default:2222}");
36
+        testInfo.put("test-port", "${port}");
37
+        testInfo.put("notExit", "${portaaa}");
38
+
39
+        map.put("testInfo", testInfo);
40
+        map.put("first", "${redis.passworda:@@@@}");
41
+
42
+        System.out.println(map);
43
+
44
+
45
+        Map<Object, String> preHandler = new HashMap<>();
46
+        // find
47
+        ConfigLoader.findPreResolveProperties(map, preHandler, null);
48
+        System.out.println("find: " + preHandler);
49
+        // replace value
50
+        final Map<Object, Object> objectObjectMap = ConfigLoader.resolveProperties(map, preHandler);
51
+        System.out.println(objectObjectMap);
52
+
53
+        // replace to map
54
+
55
+        ConfigLoader.replaceProperties(map, objectObjectMap);
56
+        System.out.println("last:" + map);
57
+
58
+    }
59
+
60
+}

+ 96 - 0
pom.xml 파일 보기

@@ -0,0 +1,96 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <modelVersion>4.0.0</modelVersion>
6
+
7
+    <groupId>me.yuxiaoyao.lang</groupId>
8
+    <artifactId>lang-utils</artifactId>
9
+    <packaging>pom</packaging>
10
+    <version>1.0.0</version>
11
+    <modules>
12
+        <module>configuration-loader</module>
13
+    </modules>
14
+
15
+
16
+    <properties>
17
+        <sourceEncoding>UTF-8</sourceEncoding>
18
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
20
+        <compile.encoding>UTF-8</compile.encoding>
21
+        <compile.target>8</compile.target>
22
+        <compile.source>8</compile.source>
23
+
24
+        <slf4j.version>1.7.28</slf4j.version>
25
+        <logback.version>1.2.3</logback.version>
26
+        <lombok.version>1.18.10</lombok.version>
27
+        <jackson.version>2.10.0</jackson.version>
28
+    </properties>
29
+
30
+
31
+    <dependencyManagement>
32
+        <dependencies>
33
+            <!-- log -->
34
+            <dependency>
35
+                <groupId>org.slf4j</groupId>
36
+                <artifactId>slf4j-api</artifactId>
37
+                <version>${slf4j.version}</version>
38
+            </dependency>
39
+            <dependency>
40
+                <groupId>ch.qos.logback</groupId>
41
+                <artifactId>logback-classic</artifactId>
42
+                <version>${logback.version}</version>
43
+            </dependency>
44
+
45
+            <!-- json -->
46
+            <dependency>
47
+                <groupId>com.fasterxml.jackson.core</groupId>
48
+                <artifactId>jackson-databind</artifactId>
49
+                <version>${jackson.version}</version>
50
+            </dependency>
51
+
52
+            <!-- jackson data format -->
53
+            <dependency>
54
+                <groupId>com.fasterxml.jackson.dataformat</groupId>
55
+                <artifactId>jackson-dataformat-yaml</artifactId>
56
+                <version>${jackson.version}</version>
57
+            </dependency>
58
+            <dependency>
59
+                <groupId>com.fasterxml.jackson.dataformat</groupId>
60
+                <artifactId>jackson-dataformat-properties</artifactId>
61
+                <version>${jackson.version}</version>
62
+            </dependency>
63
+
64
+            <!-- provided -->
65
+            <dependency>
66
+                <groupId>org.projectlombok</groupId>
67
+                <artifactId>lombok</artifactId>
68
+                <version>${lombok.version}</version>
69
+                <scope>provided</scope>
70
+            </dependency>
71
+
72
+            <!-- test -->
73
+            <dependency>
74
+                <groupId>org.junit.jupiter</groupId>
75
+                <artifactId>junit-jupiter-api</artifactId>
76
+                <version>5.5.2</version>
77
+                <scope>test</scope>
78
+            </dependency>
79
+        </dependencies>
80
+    </dependencyManagement>
81
+
82
+    <build>
83
+        <plugins>
84
+            <plugin>
85
+                <groupId>org.apache.maven.plugins</groupId>
86
+                <artifactId>maven-compiler-plugin</artifactId>
87
+                <configuration>
88
+                    <source>8</source>
89
+                    <target>8</target>
90
+                </configuration>
91
+            </plugin>
92
+        </plugins>
93
+    </build>
94
+
95
+
96
+</project>