add: samples
diff --git a/samples/bar/pom.xml b/samples/bar/pom.xml
new file mode 100644
index 0000000..064db1f
--- /dev/null
+++ b/samples/bar/pom.xml
@@ -0,0 +1,69 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.buildcommons</groupId>
+    <artifactId>spring-boot-starter-parent</artifactId>
+    <version>2.0.1.RELEASE-1.0-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.leaveschool</groupId>
+  <artifactId>sample-bar</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <!-- Test things -->
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/samples/foo/pom.xml b/samples/foo/pom.xml
new file mode 100644
index 0000000..482e8d2
--- /dev/null
+++ b/samples/foo/pom.xml
@@ -0,0 +1,90 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.buildcommons</groupId>
+    <artifactId>spring-boot-starter-parent</artifactId>
+    <version>2.0.1.RELEASE-1.0-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.leaveschool</groupId>
+  <artifactId>sample-foo</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <properties>
+    <infras.version>0.0.1-SNAPSHOT</infras.version>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-thymeleaf</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+      <version>${infras.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-mvc</artifactId>
+      <version>${infras.version}</version>
+    </dependency>
+
+
+    <!-- Test things -->
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/Application.java b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/Application.java
new file mode 100644
index 0000000..cf36972
--- /dev/null
+++ b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/Application.java
@@ -0,0 +1,18 @@
+package com.supwisdom.leaveschool.foo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 启动后访问:
+ * 1. http://localhost:8080/foo/greeting/abc
+ * 2. http://localhost:8080/foo/model?name=abc&age=10
+ */
+@SpringBootApplication
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+
+}
diff --git a/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/controller/FooController.java b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/controller/FooController.java
new file mode 100644
index 0000000..120cb3c
--- /dev/null
+++ b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/controller/FooController.java
@@ -0,0 +1,45 @@
+package com.supwisdom.leaveschool.foo.controller;
+
+import com.supwisdom.leaveschool.foo.model.Foo;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.validation.Valid;
+
+@Controller
+@RequestMapping("/foo")
+public class FooController {
+
+  /**
+   * http://localhost:8080/foo/greeting/abc
+   *
+   * @param name
+   * @param model
+   * @return
+   */
+  @GetMapping(path = "/greeting/{name}")
+  public String greeting(@PathVariable String name, Model model) {
+    model.addAttribute("name", "Good " + name);
+    return "foo/greeting";
+  }
+
+  /**
+   * good请求:
+   * http://localhost:8080/foo/model?name=abc&age=10
+   *
+   * bad请求:
+   * http://localhost:8080/foo/model?name=abc&age=-1
+   *
+   * @param foo
+   * @return
+   */
+  @GetMapping(path = "/model")
+  public String model(@Valid @ModelAttribute Foo foo) {
+    return "foo/model";
+  }
+
+}
diff --git a/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/controller/FooRestController.java b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/controller/FooRestController.java
new file mode 100644
index 0000000..d8d1f98
--- /dev/null
+++ b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/controller/FooRestController.java
@@ -0,0 +1,60 @@
+package com.supwisdom.leaveschool.foo.controller;
+
+import com.supwisdom.leaveschool.foo.model.BarException;
+import com.supwisdom.leaveschool.foo.model.Foo;
+import com.supwisdom.leaveschool.foo.model.FooException;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * curl -H 'Accept:application/json' 'http://localhost:8080/foo-rest/greeting/abc'
+ */
+@RestController
+@RequestMapping("/foo-rest")
+public class FooRestController {
+
+  /**
+   * curl -H 'Accept:application/json' 'http://localhost:8080/foo-rest/greeting/abc'
+   *
+   * @param name
+   * @return
+   */
+  @GetMapping(path = "/greeting/{name}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> greeting(@PathVariable String name) {
+    Map<String, Object> result = new HashMap<>();
+    result.put("message", "Good " + name);
+    return result;
+  }
+
+  /**
+   * good请求:
+   * curl -H 'Accept=application/json' 'http://localhost:8080/foo-rest/greeting?name=abc&age=1
+   * bad请求:
+   * curl -H 'Accept=application/json' 'http://localhost:8080/foo-rest/greeting?name=abc&age=-1
+   *
+   * @param foo
+   * @return
+   */
+  @GetMapping(path = "/model", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Foo model(@Valid Foo foo) {
+    return foo;
+  }
+
+  @GetMapping(path = "/exception1", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> exception1() {
+    throw new FooException();
+  }
+
+  @GetMapping(path = "/exception2", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> exception2() {
+    throw new BarException();
+  }
+
+}
diff --git a/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/BarException.java b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/BarException.java
new file mode 100644
index 0000000..b0bb8b7
--- /dev/null
+++ b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/BarException.java
@@ -0,0 +1,22 @@
+package com.supwisdom.leaveschool.foo.model;
+
+public class BarException extends RuntimeException {
+  public BarException() {
+  }
+
+  public BarException(String message) {
+    super(message);
+  }
+
+  public BarException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public BarException(Throwable cause) {
+    super(cause);
+  }
+
+  public BarException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+    super(message, cause, enableSuppression, writableStackTrace);
+  }
+}
diff --git a/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/Foo.java b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/Foo.java
new file mode 100644
index 0000000..f86f7f2
--- /dev/null
+++ b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/Foo.java
@@ -0,0 +1,31 @@
+package com.supwisdom.leaveschool.foo.model;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+public class Foo {
+
+  @NotNull
+  @NotEmpty
+  private String name;
+
+  @Min(0)
+  private int age;
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public int getAge() {
+    return age;
+  }
+
+  public void setAge(int age) {
+    this.age = age;
+  }
+}
diff --git a/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/FooException.java b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/FooException.java
new file mode 100644
index 0000000..0542d65
--- /dev/null
+++ b/samples/foo/src/main/java/com/supwisdom/leaveschool/foo/model/FooException.java
@@ -0,0 +1,8 @@
+package com.supwisdom.leaveschool.foo.model;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Foo Reason")
+public class FooException extends RuntimeException {
+}
diff --git a/samples/foo/src/main/resources/META-INF/spring-configuration-metadata.json b/samples/foo/src/main/resources/META-INF/spring-configuration-metadata.json
new file mode 100644
index 0000000..1013fc1
--- /dev/null
+++ b/samples/foo/src/main/resources/META-INF/spring-configuration-metadata.json
@@ -0,0 +1,10 @@
+{
+  "groups": [
+    {
+      "sourceType": "com.supwisdom.leaveschool.foo.mvc.ExceptionErrorProperties",
+      "name": "exception.error-map",
+      "type": "java.util.Map",
+      "description": "Exception class -> error string的映射"
+    }
+  ]
+}
diff --git a/samples/foo/src/main/resources/application.yaml b/samples/foo/src/main/resources/application.yaml
new file mode 100644
index 0000000..e70d463
--- /dev/null
+++ b/samples/foo/src/main/resources/application.yaml
@@ -0,0 +1,9 @@
+spring:
+  application:
+    name: sample-foo
+
+infras:
+  mvc:
+    error:
+      error-map:
+        com.supwisdom.leaveschool.foo.model.BarException: Bar Reason
diff --git a/samples/foo/src/main/resources/templates/foo/greeting.html b/samples/foo/src/main/resources/templates/foo/greeting.html
new file mode 100644
index 0000000..f1c9c34
--- /dev/null
+++ b/samples/foo/src/main/resources/templates/foo/greeting.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html xmlns:th="http://www.thymeleaf.org">
+<head>
+  <title>Getting Started: Serving Web Content</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body>
+<p th:text="'Hello, ' + ${name} + '!'" />
+</body>
+</html>
diff --git a/samples/foo/src/main/resources/templates/foo/model.html b/samples/foo/src/main/resources/templates/foo/model.html
new file mode 100644
index 0000000..166c688
--- /dev/null
+++ b/samples/foo/src/main/resources/templates/foo/model.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html xmlns:th="http://www.thymeleaf.org">
+<head>
+  <title>Getting Started: Serving Web Content</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+<p th:text="'Hello, ' + ${foo.name} + '!'"/>
+<p th:text="'Your are ' + ${foo.age} + ' years old'"/>
+</body>
+</html>
diff --git a/samples/pom.xml b/samples/pom.xml
new file mode 100644
index 0000000..f5697b4
--- /dev/null
+++ b/samples/pom.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>com.supwisdom.leaveschool</groupId>
+  <artifactId>samples-aggregator</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <properties>
+    <maven.deploy.skip>true</maven.deploy.skip>
+  </properties>
+  
+  <modules>
+    <module>foo</module>
+    <module>bar</module>
+  </modules>
+
+</project>