{"id":19993397,"url":"https://github.com/xwjie/PLMCodeTemplate","last_synced_at":"2025-05-04T12:31:26.084Z","repository":{"id":19406690,"uuid":"86973904","full_name":"xwjie/PLMCodeTemplate","owner":"xwjie","description":"给部门制定的代码框架模板","archived":false,"fork":false,"pushed_at":"2024-01-08T03:15:39.000Z","size":4328,"stargazers_count":1922,"open_issues_count":20,"forks_count":539,"subscribers_count":158,"default_branch":"master","last_synced_at":"2025-04-15T05:32:04.078Z","etag":null,"topics":["aop","log4j-mdc","spring","threadlocal"],"latest_commit_sha":null,"homepage":"https://zhuanlan.zhihu.com/p/28705206","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/xwjie.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":"2017-04-02T08:43:47.000Z","updated_at":"2025-04-14T07:08:31.000Z","dependencies_parsed_at":"2025-01-04T02:13:33.328Z","dependency_job_id":"db928c15-df2f-4042-8a5d-c78cd0a1df46","html_url":"https://github.com/xwjie/PLMCodeTemplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xwjie%2FPLMCodeTemplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xwjie%2FPLMCodeTemplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xwjie%2FPLMCodeTemplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xwjie%2FPLMCodeTemplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xwjie","download_url":"https://codeload.github.com/xwjie/PLMCodeTemplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252334381,"owners_count":21731395,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["aop","log4j-mdc","spring","threadlocal"],"created_at":"2024-11-13T04:52:37.989Z","updated_at":"2025-05-04T12:31:21.076Z","avatar_url":"https://github.com/xwjie.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"# 晓风轻的Spring开发代码模板\n\n\u003e 本文的面向目标群为编写业务代码的初学者。\n\u003e\n\u003e 本文的目的是编写简单易读的代码。\n\n给部门制定的代码框架模板。追求工匠精神，编写简单代码。\n\n作者微信交流\n\n![晓风轻微信](docs/weixin.jpg) ![java技术交流群](docs/java技术交流群二维码.png)\n\npdf电子书请见ebook目录。\n\n[`SpringBoot版本在这里`](https://github.com/xwjie/ElementVueSpringbootCodeTemplate)**，持续更新中，后续加入vue+element的代码模板，欢迎加星加watch。**\n\n# 前言\n\n参考 [**程序员你为什么这么累**](https://zhuanlan.zhihu.com/p/28705206) 系列文章 ，里面有详细的讲解，评论里面有不同观点的讨论，建议也看看，相信对你有帮助。\n\n![](/pictures/main.png)\n\n# 工程使用说明\n\n工程使用jdk6+，使用了[lombok](https://projectlombok.org/)插件。在 Idea 里面选择 `source`目录导入 `Maven`  工程即可。然后在Tomcat里面运行工程即可。\n\n启动项目，访问地址 `http://localhost:8080/+[应用名（可为空）]` 即可。\n\n[工程使用详细说明](docs/install.md)\n\n# 目录\n\n**以下为简要说明，详细说明请看 [https://xwjie.github.io/](https://xwjie.github.io/)**\n\n\u003e * 优雅编码 - 接口定义规范\n\u003e * 优雅编码 - ResultBean的重要性和约束\n\u003e * 优雅编码 - 异常处理\n\u003e * 优雅编码 - 参数校验和国际化规范\n\u003e * 优雅编码 - 日志打印\n\u003e * 优雅编码 - 工具类编写\n\u003e * 技术点总结\n\u003e * 对开发组长的要求\n\u003e * 对开发人员的建议\n\n# 优雅编码 - 接口定义规范\n\n请阅读 [我的编码习惯 - 接口定义](https://zhuanlan.zhihu.com/p/28708259) ，不要犯里面的错误。\n\n1. 所有接口都必须返回ResultBean。\n2. 一开始就要考虑成功和失败二种场景。\n3. ResultBean只允许出现在controller，不允许到处传。\n4. 不要出现 map ，json 等复杂对象做为输入和输出。\n5. 不要出现 local ，messagesource 等和业务无关的参数。\n\n# 优雅编码 - ResultBean的重要性和约束\n\n请阅读 [我的编码习惯 - Controller规范](https://zhuanlan.zhihu.com/p/28717374)。\n\n这里都是对开发组长的要求。使用 `AOP` 统一处理异常，并根据不同异常返回不同返回码。尤其关注非用户自己抛出的异常。\n\n# 优雅编码 - 异常处理\n\n请阅读 [我的编码习惯 - 异常处理](https://zhuanlan.zhihu.com/p/29005176)。\n\n1. 开发人员不准捕获异常，直接抛出。\n2. 少加空判断。如果对象不应该为空，就不需要加空判断，加了空判断就要测试为空和不为空二种情况。\n\n其他对开发组长的要求请见上面的文章和代码。\n\n# 优雅编码 - 日志打印\n\n请阅读 [我的编程习惯 - 日志建议](https://zhuanlan.zhihu.com/p/28629319)。\n\n## 分支语句的变量需要打印变量\n\n分支语句变量会影响代码走向，不打日志无法知道究竟走那个分支。如下\n\n```Java\n// optype决定代码走向，需要打印日志\nlogger.info(\"edit user, opType:\" + opType);\n\nif (opType == CREATE) {\n  // 新增操作\n} else if (opType == UPDATE) {\n  // 修改操作\n} else {\n  // 错误的类型，抛出异常\n  throw new IllegalArgumentException(\"unknown optype:\" + opType);\n}\n```\n\n如果没有打印optype的值，出了问题你只能从前找到后，看optype是什么了，很浪费时间。\n\n\u003e 重要建议：养成增加else语句，把不合法参数抛出异常的好习惯。\n\u003e\n\u003e 抛异常的时候把对应的非法值抛出来。\n\n## 修改操作需要打印操作的对象\n\n这点是为了跟踪。防止出现，一个数据被删除了，找不到谁做的。如\n\n```Java\nprivate void deleteDoc(long id) {\n\n  logger.info(\"delete doc, id:\" + id);\n\n  // 删除代码\n}\n```\n\n## 大量数据操作的时候需要打印数据长度\n\n建议前后打印日志，而且要打印出数据长度，目的是为了知道 `处理了多少数据用了多少时间` 。如一个操作用了3秒钟，性能是好还是坏？ 如果处理了1条数据，那么可能就是性能差，如果处理了10000条数据，那么可能就是性能好。\n\n```Java\nlogger.info(\"query docment start, params:\" + params);\n\nList\u003cDocument\u003e docList = query(params);\n\nlogger.info(\"query docment done, size:\" + docList.size())\n```\n\n## 使用log4j的MDC打印用户名等额外信息\n\n有时候，业务量大的系统要找到某一个用户的操作日志定位问题非常痛苦，每一个日志上加用户名又低效也容易漏掉，所以我们要在更高层级上解决这些共性问题。\n\n我们使用log4j的 `MDC` 功能达成这个目的。在用户登录后，把用户标志放到 `MDC` 中。\n\n```Java\nprivate final static ThreadLocal\u003cString\u003e tlUser = new ThreadLocal\u003c\u003e();\n\npublic static final String KEY_USER = \"user\";\n\npublic static void setUser(String userid) {\n  tlUser.set(userid);\n\n  // 把用户信息放到log4j\n  MDC.put(KEY_USER, userid);\n}\n```\n\n然后修改log4j配置，pattern上增加 `%X{user}` ，位置随意。增加线程相关配置 `[%t]` 。更多参数[log4j变量](https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html)\n\n```XML\n\u003clayout class=\"org.apache.log4j.PatternLayout\"\u003e\n  \u003cparam name=\"ConversionPattern\" value=\"[%t]%-d{MM-dd HH:mm:ss,SSS} %-5p: %X{user} - %c - %m%n\" /\u003e/\u003e\n\u003c/layout\u003e\n```\n\n最终效果图：\n\n![日志](docs/pictures/log1.png)\n\n\u003e 没有用户信息的时候并不会报错，而是空串。\n\u003e\n\u003e 不要一开始就关注日志级别和日志性能，规则越多越难落地。\n\u003e\n\u003e 必须记得清空。\n\n## 集群环境下需要在静态服务器增加配置，返回处理机器的信息到响应头\n\n配置后可以直接找到代码在那个机器上运行，快速定位。举例，`Nginx` 集群最简配置（主要使用 `$upstream_addr`） ：\n\n```\nupstream code_server{\n  server 127.0.0.1:8080;\n  server 127.0.0.1:18080;\n  ip_hash;\n  keepalive 32;\n}\n\nserver{\n  listen 80;\n  server_name xwjie.com;\n\n  location /plm {\n    proxy_pass http://code_server/plm;\n    add_header x-slave $upstream_addr;\n  }\n\n}\n```\n\n效果图：\n\n![nginx](docs/pictures/nginx.png)\n\n# 优雅编码 - 参数校验和国际化规范\n\n请阅读 [我的编码习惯 - 参数校验和国际化规范](https://zhuanlan.zhihu.com/p/29129469) 。\n\n1. 调用自己的校验函数\n2. 业务代码任何地方参数上都不要出现local，messagesource\n3. 异常信息里面，不正确的参数值要提示出来，减少定位时间\n4. 国际化参数不要放到每一个url上\n\n# 优雅编码 - 工具类编写\n\n请阅读 [我的编码习惯 - 工具类规范](https://zhuanlan.zhihu.com/p/29199049) 。\n\n工具类的目标是要编写通用灵活的方法。重点注意参数的优化，多使用重载。\n\n1. 隐藏实现，编写自己的工具类方法，不要再业务代码直接调用第三方工具类\n2. 使用父类/接口\n3. 使用重载编写衍生函数组\n4. 使用静态引入，方便找到工具方法\n5. 物理上独立存放，独立维护\n\n# 技术点总结\n\n1. spring的aop的使用\n2. log4j的MDC使用\n3. JDK的ThreadLocal的使用\n\n# 对开发组长的要求\n\n定义好代码框架，不要做太多、太细的要求，否则无法落地。这篇文章中，规范中要求做到的少，不准做的多，落地相对容易。\n\n1. 定义好统一的接口格式、异常、常量等\n2. 多使用AOP和ThreadLocal简化代码\n3. 亲自编写校验函数和工具类\n4. 代码评审中，严格控制函数的参数，不允许出现复杂参数和业务无关的参数\n\n# 对开发人员的建议\n\n1. 不要养成面对debug编程，用日志代替debug\n2. 不要一上来就做整个功能测试，要一行一行代码一个一个函数测试\n3. 谨慎捕获异常和加空判断，加了空判断就要测试为空和不为空二种情况\n4. 日志也是代码的一部分，提交代码前先运行看一遍操作日志\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxwjie%2FPLMCodeTemplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxwjie%2FPLMCodeTemplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxwjie%2FPLMCodeTemplate/lists"}