| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,6 @@ | |||
| 1 | 1 | .idea | |
| 2 | 2 | *.iml | |
| 3 | 3 | node_modules | |
| 4 | - target | ||
| 4 | + target | ||
| 5 | + upload | ||
| 6 | + .DS_Store | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,7 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | 19 | ## 模块一览 | |
| 20 | 20 | ||
| 21 | - 一共 16 个模块。`core` 是被其他模块共同依赖的基础包,其余每个模块各自演示一项技术。 | ||
| 21 | + 一共 21 个模块。`core` 是被其他模块共同依赖的基础包,其余每个模块各自演示一项技术。 | ||
| 22 | 22 | ||
| 23 | 23 | | 模块 | 演示内容 | 启动类 | 需要的外部服务 | | |
| 24 | 24 | | --- | --- | --- | --- | | |
@@ -38,9 +38,16 @@ | |||
| 38 | 38 | | `crawler` | jsoup 网络爬虫(阴阳师式神数据),抓取后落库 | `CrawlerMain` | MySQL | | |
| 39 | 39 | | `freemarker` | FreeMarker 模板引擎 | `FreemarkerMain` | — | | |
| 40 | 40 | | `thymeleaf` | Thymeleaf 模板引擎 | `ThymeleafMain` | — | | |
| 41 | + | `validation` | 参数校验(`@Valid` / `@Validated`)与 `@RestControllerAdvice` 全局异常处理 | `ValidationMain` | — | | ||
| 42 | + | `fileupload` | 文件上传下载,含路径穿越防护 | `FileUploadMain` | — | | ||
| 43 | + | `restclient` | 用 `RestClient` 调用外部 HTTP 服务 | `RestClientMain` | — | | ||
| 44 | + | `cache` | Spring Cache 抽象 + Caffeine 本地缓存 | `CacheMain` | — | | ||
| 45 | + | `actuator` | 健康检查、自定义 `HealthIndicator` 与业务指标 | `ActuatorMain` | — | | ||
| 41 | 46 | ||
| 42 | 47 | 除 `socket` 使用 8081 外,其余模块都监听 **8080**,所以一次只启动一个模块。 | |
| 43 | 48 | ||
| 49 | + 其中 `validation`、`fileupload`、`restclient`、`cache`、`actuator`、`async` 六个模块附带可直接运行的测试。它们都不依赖外部服务,`mvn test` 即可跑通,也可以当作各自技术点的可执行文档来读。 | ||
| 50 | + | ||
| 44 | 51 | ## 技术栈 | |
| 45 | 52 | ||
| 46 | 53 | | 组件 | 版本 | | |
@@ -52,6 +59,8 @@ | |||
| 52 | 59 | | Jackson | 3.x | | |
| 53 | 60 | | API 文档 | springdoc-openapi 3.x(OpenAPI 3.1) | | |
| 54 | 61 | | 数据库驱动 | MySQL Connector/J | | |
| 62 | + | 缓存 | Caffeine(`cache` 模块) | | ||
| 63 | + | 监控 | Micrometer + Spring Boot Actuator(`actuator` 模块) | | ||
| 55 | 64 | | 其他 | MyBatis、Lettuce(Redis)、jsoup、Apache POI、fastjson2、zxing | | |
| 56 | 65 | ||
| 57 | 66 | Spring、Jackson、Hibernate、JUnit 等版本统一由 `spring-boot-dependencies` BOM 管理,不在本项目中单独指定。 | |
@@ -84,6 +93,12 @@ mvn spring-boot:run -pl order | |||
| 84 | 93 | ||
| 85 | 94 | 在 IDE 中则找到对应模块的 `*Main` 类直接运行即可。 | |
| 86 | 95 | ||
| 96 | + 跑测试: | ||
| 97 | + | ||
| 98 | + ```bash | ||
| 99 | + mvn test | ||
| 100 | + ``` | ||
| 101 | + | ||
| 87 | 102 |  | |
| 88 | 103 | ||
| 89 | 104 | 部署到服务器时,Spring Boot 内置了 Tomcat,把打好的 jar 传上去执行就可以: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,51 @@ | |||
| 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>xiaomo</artifactId> | ||
| 7 | + <groupId>info.xiaomo</groupId> | ||
| 8 | + <version>2020.1</version> | ||
| 9 | + </parent> | ||
| 10 | + <modelVersion>4.0.0</modelVersion> | ||
| 11 | + | ||
| 12 | + <artifactId>actuator</artifactId> | ||
| 13 | + | ||
| 14 | + <dependencies> | ||
| 15 | + <dependency> | ||
| 16 | + <groupId>info.xiaomo</groupId> | ||
| 17 | + <artifactId>core</artifactId> | ||
| 18 | + <version>2020.1</version> | ||
| 19 | + </dependency> | ||
| 20 | + <dependency> | ||
| 21 | + <groupId>org.springframework.boot</groupId> | ||
| 22 | + <artifactId>spring-boot-starter-actuator</artifactId> | ||
| 23 | + </dependency> | ||
| 24 | + <dependency> | ||
| 25 | + <groupId>org.springframework.boot</groupId> | ||
| 26 | + <artifactId>spring-boot-starter-test</artifactId> | ||
| 27 | + <scope>test</scope> | ||
| 28 | + </dependency> | ||
| 29 | + <dependency> | ||
| 30 | + <groupId>org.springframework.boot</groupId> | ||
| 31 | + <artifactId>spring-boot-starter-webmvc-test</artifactId> | ||
| 32 | + <scope>test</scope> | ||
| 33 | + </dependency> | ||
| 34 | + </dependencies> | ||
| 35 | + | ||
| 36 | + <build> | ||
| 37 | + <plugins> | ||
| 38 | + <plugin> | ||
| 39 | + <groupId>org.springframework.boot</groupId> | ||
| 40 | + <artifactId>spring-boot-maven-plugin</artifactId> | ||
| 41 | + <executions> | ||
| 42 | + <execution> | ||
| 43 | + <goals> | ||
| 44 | + <goal>repackage</goal> | ||
| 45 | + </goals> | ||
| 46 | + </execution> | ||
| 47 | + </executions> | ||
| 48 | + </plugin> | ||
| 49 | + </plugins> | ||
| 50 | + </build> | ||
| 51 | + </project> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,23 @@ | |||
| 1 | + package info.xiaomo.actuator; | ||
| 2 | + | ||
| 3 | + import org.springframework.boot.SpringApplication; | ||
| 4 | + import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | ||
| 5 | + import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration; | ||
| 6 | + import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; | ||
| 7 | + import org.springframework.context.annotation.ComponentScan; | ||
| 8 | + import org.springframework.context.annotation.Configuration; | ||
| 9 | + | ||
| 10 | + /** | ||
| 11 | + * @author : xiaomo | ||
| 12 | + * Description: 健康检查与运行指标启动器 | ||
| 13 | + */ | ||
| 14 | + @Configuration | ||
| 15 | + @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) | ||
| 16 | + @ComponentScan("info.xiaomo.actuator") | ||
| 17 | + public class ActuatorMain { | ||
| 18 | + | ||
| 19 | + public static void main(String[] args) { | ||
| 20 | + SpringApplication.run(ActuatorMain.class, args); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,41 @@ | |||
| 1 | + package info.xiaomo.actuator.controller; | ||
| 2 | + | ||
| 3 | + import info.xiaomo.core.base.Result; | ||
| 4 | + import io.micrometer.core.instrument.Counter; | ||
| 5 | + import io.micrometer.core.instrument.MeterRegistry; | ||
| 6 | + import io.micrometer.core.instrument.Timer; | ||
| 7 | + import org.springframework.web.bind.annotation.GetMapping; | ||
| 8 | + import org.springframework.web.bind.annotation.PathVariable; | ||
| 9 | + import org.springframework.web.bind.annotation.RequestMapping; | ||
| 10 | + import org.springframework.web.bind.annotation.RestController; | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * 自定义业务指标。注册到 MeterRegistry 后可以在 /actuator/metrics/greeting.count 看到。 | ||
| 14 | + * | ||
| 15 | + * @author : xiaomo | ||
| 16 | + */ | ||
| 17 | + @RestController | ||
| 18 | + @RequestMapping("/greeting") | ||
| 19 | + public class GreetingController { | ||
| 20 | + | ||
| 21 | + private final Counter greetingCounter; | ||
| 22 | + private final Timer greetingTimer; | ||
| 23 | + | ||
| 24 | + public GreetingController(MeterRegistry registry) { | ||
| 25 | + this.greetingCounter = Counter.builder("greeting.count") | ||
| 26 | + .description("打招呼接口被调用的次数") | ||
| 27 | + .register(registry); | ||
| 28 | + this.greetingTimer = Timer.builder("greeting.latency") | ||
| 29 | + .description("打招呼接口的耗时") | ||
| 30 | + .register(registry); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + @GetMapping("/{name}") | ||
| 34 | + public Result<String> greet(@PathVariable("name") String name) { | ||
| 35 | + return greetingTimer.record(() -> { | ||
| 36 | + greetingCounter.increment(); | ||
| 37 | + return new Result<>("你好, " + name); | ||
| 38 | + }); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,42 @@ | |||
| 1 | + package info.xiaomo.actuator.health; | ||
| 2 | + | ||
| 3 | + import org.springframework.boot.health.contributor.Health; | ||
| 4 | + import org.springframework.boot.health.contributor.HealthIndicator; | ||
| 5 | + import org.springframework.stereotype.Component; | ||
| 6 | + | ||
| 7 | + import java.io.File; | ||
| 8 | + | ||
| 9 | + /** | ||
| 10 | + * 自定义健康检查。Bean 名字去掉 HealthIndicator 后缀就是它在 /actuator/health 里的键, | ||
| 11 | + * 所以这里会显示为 "diskSpaceRatio"。 | ||
| 12 | + * | ||
| 13 | + * @author : xiaomo | ||
| 14 | + */ | ||
| 15 | + @Component | ||
| 16 | + public class DiskSpaceRatioHealthIndicator implements HealthIndicator { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 可用空间低于该比例就判定为不健康。 | ||
| 20 | + */ | ||
| 21 | + private static final double THRESHOLD = 0.05; | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public Health health() { | ||
| 25 | + File root = new File("."); | ||
| 26 | + long total = root.getTotalSpace(); | ||
| 27 | + long free = root.getUsableSpace(); | ||
| 28 | + if (total <= 0) { | ||
| 29 | + return Health.unknown().withDetail("reason", "无法读取磁盘信息").build(); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + double freeRatio = (double) free / total; | ||
| 33 | + Health.Builder builder = freeRatio >= THRESHOLD ? Health.up() : Health.down(); | ||
| 34 | + return builder | ||
| 35 | + .withDetail("totalBytes", total) | ||
| 36 | + .withDetail("freeBytes", free) | ||
| 37 | + .withDetail("freeRatio", String.format("%.4f", freeRatio)) | ||
| 38 | + .withDetail("threshold", THRESHOLD) | ||
| 39 | + .build(); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,17 @@ | |||
| 1 | + logging.config=classpath:config/logback-dev.xml | ||
| 2 | + server.port=8080 | ||
| 3 | + | ||
| 4 | + server.max-http-header-size=20971520 | ||
| 5 | + | ||
| 6 | + spring.jackson.date-format=yyyy-MM-dd HH:mm:ss | ||
| 7 | + spring.jackson.time-zone=GMT+8 | ||
| 8 | + | ||
| 9 | + # 暴露哪些端点。生产环境不要用 * , 按需列出即可 | ||
| 10 | + management.endpoints.web.exposure.include=health,info,metrics,env,loggers | ||
| 11 | + # 展示健康检查的明细, 默认只返回一个总的 status | ||
| 12 | + management.endpoint.health.show-details=always | ||
| 13 | + | ||
| 14 | + # /actuator/info 的内容 | ||
| 15 | + management.info.env.enabled=true | ||
| 16 | + info.app.name=SpringBootUnity actuator \u793A\u4F8B | ||
| 17 | + info.app.description=\u5065\u5EB7\u68C0\u67E5\u4E0E\u8FD0\u884C\u6307\u6807 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,17 @@ | |||
| 1 | + <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | + | ||
| 3 | + <configuration scan="true"> | ||
| 4 | + | ||
| 5 | + <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> | ||
| 6 | + <encoder charset="UTF-8"> | ||
| 7 | + <pattern>[%d{yyyy-MM-dd HH:mm:ss} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %highlight(%msg) %n</pattern> | ||
| 8 | + </encoder> | ||
| 9 | + </appender> | ||
| 10 | + | ||
| 11 | + <root level="INFO"> | ||
| 12 | + <appender-ref ref="stdout"/> | ||
| 13 | + </root> | ||
| 14 | + | ||
| 15 | + <logger name="info.xiaomo" level="DEBUG"/> | ||
| 16 | + | ||
| 17 | + </configuration> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,58 @@ | |||
| 1 | + package info.xiaomo.actuator; | ||
| 2 | + | ||
| 3 | + import org.junit.jupiter.api.Test; | ||
| 4 | + import org.springframework.beans.factory.annotation.Autowired; | ||
| 5 | + import org.springframework.boot.test.context.SpringBootTest; | ||
| 6 | + import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; | ||
| 7 | + import org.springframework.test.web.servlet.MockMvc; | ||
| 8 | + | ||
| 9 | + import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||
| 10 | + import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; | ||
| 11 | + import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
| 12 | + | ||
| 13 | + @SpringBootTest(classes = ActuatorMain.class) | ||
| 14 | + @AutoConfigureMockMvc | ||
| 15 | + class ActuatorEndpointTest { | ||
| 16 | + | ||
| 17 | + @Autowired | ||
| 18 | + private MockMvc mockMvc; | ||
| 19 | + | ||
| 20 | + @Test | ||
| 21 | + void 健康检查应当返回UP并包含自定义的检查项() throws Exception { | ||
| 22 | + mockMvc.perform(get("/actuator/health")) | ||
| 23 | + .andExpect(status().isOk()) | ||
| 24 | + .andExpect(jsonPath("$.status").value("UP")) | ||
| 25 | + .andExpect(jsonPath("$.components.diskSpaceRatio").exists()) | ||
| 26 | + .andExpect(jsonPath("$.components.diskSpaceRatio.details.freeRatio").exists()); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Test | ||
| 30 | + void info端点应当返回配置的应用信息() throws Exception { | ||
| 31 | + mockMvc.perform(get("/actuator/info")) | ||
| 32 | + .andExpect(status().isOk()) | ||
| 33 | + .andExpect(jsonPath("$.app.name").value("SpringBootUnity actuator 示例")); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Test | ||
| 37 | + void 未在白名单中的端点不应当被暴露() throws Exception { | ||
| 38 | + // beans 没有列在 management.endpoints.web.exposure.include 里 | ||
| 39 | + mockMvc.perform(get("/actuator/beans")) | ||
| 40 | + .andExpect(status().isNotFound()); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Test | ||
| 44 | + void 调用业务接口后自定义指标应当可见且计数递增() throws Exception { | ||
| 45 | + mockMvc.perform(get("/greeting/{name}", "xiaomo")) | ||
| 46 | + .andExpect(status().isOk()) | ||
| 47 | + .andExpect(jsonPath("$.data").value("你好, xiaomo")); | ||
| 48 | + | ||
| 49 | + mockMvc.perform(get("/greeting/{name}", "houko")) | ||
| 50 | + .andExpect(status().isOk()); | ||
| 51 | + | ||
| 52 | + mockMvc.perform(get("/actuator/metrics/greeting.count")) | ||
| 53 | + .andExpect(status().isOk()) | ||
| 54 | + .andExpect(jsonPath("$.name").value("greeting.count")) | ||
| 55 | + .andExpect(jsonPath("$.measurements[0].value").value(2.0)); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,6 +16,11 @@ | |||
| 16 | 16 | <artifactId>core</artifactId> | |
| 17 | 17 | <version>2020.1</version> | |
| 18 | 18 | </dependency> | |
| 19 | + <dependency> | ||
| 20 | + <groupId>org.springframework.boot</groupId> | ||
| 21 | + <artifactId>spring-boot-starter-test</artifactId> | ||
| 22 | + <scope>test</scope> | ||
| 23 | + </dependency> | ||
| 19 | 24 | </dependencies> | |
| 20 | 25 | ||
| 21 | 26 | <build> | |
| Back | FazBrowse Home | New Git URL |
0 commit comments