{"id":13694736,"url":"https://github.com/othorizon/spring-best-practices","last_synced_at":"2026-01-19T23:31:06.959Z","repository":{"id":36762013,"uuid":"230080058","full_name":"othorizon/spring-best-practices","owner":"othorizon","description":"spring 最佳实践 Demo案例","archived":false,"fork":false,"pushed_at":"2024-01-22T17:19:41.000Z","size":184,"stargazers_count":377,"open_issues_count":3,"forks_count":40,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-05-03T04:33:49.314Z","etag":null,"topics":["spring","spring-best-practices","spring-boot","spring-boot-demo"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/othorizon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-25T09:42:33.000Z","updated_at":"2025-03-16T03:32:43.000Z","dependencies_parsed_at":"2024-11-12T21:33:08.783Z","dependency_job_id":"51710334-2607-4f32-9011-de8a88d83aee","html_url":"https://github.com/othorizon/spring-best-practices","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/othorizon/spring-best-practices","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/othorizon%2Fspring-best-practices","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/othorizon%2Fspring-best-practices/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/othorizon%2Fspring-best-practices/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/othorizon%2Fspring-best-practices/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/othorizon","download_url":"https://codeload.github.com/othorizon/spring-best-practices/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/othorizon%2Fspring-best-practices/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28589752,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T20:45:59.482Z","status":"ssl_error","status_checked_at":"2026-01-19T20:45:41.500Z","response_time":67,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["spring","spring-best-practices","spring-boot","spring-boot-demo"],"created_at":"2024-08-02T17:01:39.354Z","updated_at":"2026-01-19T23:31:06.933Z","avatar_url":"https://github.com/othorizon.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"# spring 最佳实践\n\n总结了本人多年Java开发中的一些开发经验以及工具类和Spring框架的应用  \n采用了项目Demo的方式把零散的内容联系在一起去展示其用法，可以直接拿来作为种子项目，用于快速构建中小型的spring-boot项目  \n**项目持续更新中，将会逐步扩充完善**\n\nSpring Boot版本 ：2.1.7 , 兼容的Spring Cloud版本为 Greenwich ,版本对照参考[官网Overview](https://spring.io/projects/spring-cloud#overview)\n\n## 概要\n\n- 如何配置拦截器：interceptor、filter、@RestControllerAdvice\n- bean的初始化： InitializingBean接口、@conditionXXX 注解\n- 如何获取applicationContext上下文： ApplicationContextAware\n- 枚举的优雅使用： 1、如何把枚举作为接口的交互参数：@JsonCreator、@JsonValue ,要注意fastjson和spring采用的jackson 对注解的支持 2、valueOfByXX\n- 缓存的优雅使用： @Cacheable 、CaffeineCacheManager、请求级别的缓存RequestScopedCacheManager，注意防止副作用操作污染缓存数据\n- 配置文件配置时间属性：java.time.Duration\n- 正确的报错方式，message的国际化： [参考](https://www.jianshu.com/p/4d5f16f6ab82)\n- 接口参数校验：@Validated、javax.validation.constraints、自定义参数校验注解\n- 日志的优雅配置：log4j与logback的基础、使用MDC增加tractId跟踪日志\n- AOP的应用：自定义注解、方法拦截\n- java8 stream 的使用技巧\n\n第三方工具的使用\n\n- json的处理：gson、fastjson、jackson，json-path\n- RestTemplate的优雅使用：工具类的封装、header的注入\n- 借助Mybatis-Plus实现零SQL开发\n- 借助MapStruct实现po、bo、vo等对象之间的转换\n- 健康接口，项目部署版本检查： buildnumber-maven-plugin\n- 如何深度复制对象：json复制、mapStruct、BeanUtils\n- apache-common 系列、hutool等基本工具类\n- 自定义的时间格式化工具类、jackson的封装工具\n\n运维\n\n- 数据库版本维护 flyway\n- CI/CD相关: Jenkins的常用配置方式、docker、kubernetes\n\n### 在项目中的实践\n\n- 项目必备的工具包：apache-common系列、gson 等\n- 借助@RestControllerAdvice实现全局统一的response返回，方法直接通过抛异常来返回\n- 更好的实现项目的初始化相关操作\n\n### 代码之道\n\n- 如何干掉if-else\n\n## 导航\n\n#### 健康接口\n\n[web/src/main/java/top/rizon/springbestpractice/web/controller/ServerHealthController.java](web/src/main/java/top/rizon/springbestpractice/web/controller/ServerHealthController.java)  \n\n健康接口是项目必备接口  \n基本的ping-pong：接口作为最基本的服务存活检测  \n服务器信息接口：可以打印服务常用信息的接口，比如服务器时间等  \ngit版本信息的接口：接口打印了git版本号和build时间，在开发联调期间是非常重要的一个运维参考  \n_ps. 要从auth认证拦截中排除_\n\n#### 接口参数校验\n\n[web/src/main/java/top/rizon/springbestpractice/web/controller/UserController.java](web/src/main/java/top/rizon/springbestpractice/web/controller/UserController.java)  \n错误提示配置：[web/src/main/resources/ValidationMessages.properties](web/src/main/resources/ValidationMessages.properties)  \n\n示例方法：`top.rizon.springbestpractice.web.controller.UserController.list`\n\n通过注解实现参数校验，\n错误提示信息可以使用配置文件实现国际化处理\n\n#### 缓存的几种写法  \n\n[web/src/main/java/top/rizon/springbestpractice/web/controller/CacheExampleController.java](web/src/main/java/top/rizon/springbestpractice/web/controller/CacheExampleController.java)    \n\n#### 登陆认证的简单实现方式\n  \n[web/src/main/java/top/rizon/springbestpractice/web/config/auth/AuthWebConfig.java](web/src/main/java/top/rizon/springbestpractice/web/config/auth/AuthWebConfig.java)  \n\n#### 全局异常拦截处理\n  \n[common/src/main/java/top/rizon/springbestpractice/common/handler/ExceptionHandlers.java](common/src/main/java/top/rizon/springbestpractice/common/handler/ExceptionHandlers.java)  \n\n#### http请求工具RestTemplate的简单封装使用\n  \n[common/src/main/java/top/rizon/springbestpractice/common/utils/http/SimpleRestTemplateUtils.java](common/src/main/java/top/rizon/springbestpractice/common/utils/http/SimpleRestTemplateUtils.java)    \nRestTemplate拦截器：RestTemplateAuthConfig、BaseAuthHeaderHttpRequestInterceptor\n\n#### 封装的工具类\n\n[common/src/main/java/top/rizon/springbestpractice/common/utils/](common/src/main/java/top/rizon/springbestpractice/common/utils/)  \n\n##### sql注入的应用\n\n[dao/src/main/java/top/rizon/springbestpractice/dao/utils/dynamictblname/](dao/src/main/java/top/rizon/springbestpractice/dao/utils/dynamictblname/)  \n`top.rizon.springbestpractice.web.controller.SqlExampleController.queryDateTable`  \n动态表名,一般可用于按日期等方式做分表的业务场景  \n顺便演示了PageHelper的分页工具的使用  \n\n#### java8 stream 的技巧\n\n[common/src/test/java/top/rizon/springbestpractice/common/utils/StreamUtilTest.java](common/src/test/java/top/rizon/springbestpractice/common/utils/StreamUtilTest.java)  \n\nJDK8引入的Lambda表达式和Stream为Java平台提供了函数式编程的支持，java提供了consumer、function等一系列接口为函数式编程提供了基础。  \nLambda表达式是一个能够作为参数传递的匿名函数对象，它没有名字，有参数列表、函数体、返回类型，也可以抛出异常。它的类型是函数接口（Functional Interface）。  \n函数式编程以操作（函数）为中心，强调变量不变性，无副作用。  \n\n#### 第三方工具类相关\n\nMapStruct java对象映射工具  \n[web/src/main/java/top/rizon/springbestpractice/web/model/WebObjMapper.java](web/src/main/java/top/rizon/springbestpractice/web/model/WebObjMapper.java)  \nps. 某些场景下用于deepClone也是一个不错的选择    \n\n#### AOP的应用案例\n\n[common/src/main/java/top/rizon/springbestpractice/common/aspect/](common/src/main/java/top/rizon/springbestpractice/common/aspect/)  \n\n#####  分页页码自动修复\n\n[web/src/main/java/top/rizon/springbestpractice/web/controller/UserController.java](web/src/main/java/top/rizon/springbestpractice/web/controller/UserController.java)  \n`top.rizon.springbestpractice.web.controller.UserController.list`  \n当页码大于数据真实页码时会纠正为最后一页的数据，这可以解决前端分页展示删除最后一页的最后一条数据时刷新后为无数据的空白页的问题  \n\n##### 打印方法执行时间\n\n[web/src/main/java/top/rizon/springbestpractice/web/controller/DemoController.java](web/src/main/java/top/rizon/springbestpractice/web/controller/DemoController.java)\n`top.rizon.springbestpractice.web.service.AopExampleService.sleepMethod`  \n打印方法执行时间，这在优化代码，排查耗时过高的方法时有一定的帮助\n\n#### 代码之道\n\n##### 策略模式 干掉if-else\n\n[web/src/main/java/top/rizon/springbestpractice/web/controller/DemoController.java](web/src/main/java/top/rizon/springbestpractice/web/controller/DemoController.java)  \n`top.rizon.springbestpractice.web.controller.DemoController.formatDate`  \n如果你的if-else过于复杂那么应当考虑抽象业务了，简单的业务可以直接用枚举写抽象方法的实现，复杂的业务则可以写接口类去实现  \n\n#### 使用枚举作为请求参数\n\n[web/src/main/java/top/rizon/springbestpractice/web/controller/DemoController.java](web/src/main/java/top/rizon/springbestpractice/web/controller/DemoController.java)  \n`top.rizon.springbestpractice.web.controller.DemoController.formatDate`  \njackson的`@JsonCreator`可以指定json反序列化时的构造函数，`@JsonValue`则可以指定对象序列化时的取值属性\n\n## 使用\n\n[API Doc - Postman](https://documenter.getpostman.com/view/494976/SWLZgAn8)    \n\n### 构建部署\n\n#### 编译jar\n\n```bash\n# buildnumber-maven-plugin配置所致，在clean之后运行测试用例会由于尚未替换的配置导致配置文件读取失败\nmvn clean package -DskipTests=true\njava -jar web/target/web-0.0.1-SNAPSHOT.jar\n```\n\n#### 将外部配置文件和jar一起打包成tar  \n\n```bash\nmvn clean package  -DbuildType=tar -DpackageConf=true -DconfEnv=test -DskipTests=true\n# 编译会生成tar： web/target/spring-best-practice-web-0.0.1-SNAPSHOT.tar.gz\n# tar包中包含了启动脚本 解压到目标位置后执行启动脚本\nsh bin/app.sh start|stop|restart|status|pid\n```\n\n#### 构建docker镜像\n\n```bash\nmvn clean package -DbuildType=docker -DpackageConf=true -DconfEnv=test -DskipTests=true\ndocker run --rm -it -p8080:8080 rizon/spring-best-practice\n```\n\n### Online IDE\n\n[![在 Gitpod 中打开](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io#https://github.com/othorizon/spring-best-practices)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fothorizon%2Fspring-best-practices","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fothorizon%2Fspring-best-practices","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fothorizon%2Fspring-best-practices/lists"}