
Java Development Manual
- 9 installs
- 33 repo stars
- Updated April 26, 2026
- bighardperson/computer-science-skills-collection
java-development-manual is a Claude skill encoding the Alibaba Java Development Manual as conventions for writing and reviewing Java code across seven dimensions.
About
This skill encodes the Alibaba Java Development Manual (Songshan edition) as a set of conventions across seven dimensions: coding style, exceptions and logging, unit testing, security, MySQL, project structure, and design. A developer uses it to write or review Java code against these standards. It provides quick-reference do/don't tables and links to detailed rule files. Documentation is in Chinese.
- Encodes the Alibaba Java Development Manual (Songshan edition) across 7 dimensions
- Covers coding conventions, exceptions/logging, unit tests, security, MySQL, project structure, and design
- Provides do/don't quick-reference tables for code review
Java Development Manual by the numbers
- 9 all-time installs (skills.sh)
- Ranked #826 of 1,354 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
java-development-manual capabilities & compatibility
Free reference; no external services.
- Capabilities
- code review · java review · convention check · security audit
- Use cases
- code review · testing · security audit · database
- Runs
- Runs locally
- Pricing
- Free
What java-development-manual says it does
涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。
禁止事项速查
npx skills add https://github.com/bighardperson/computer-science-skills-collection --skill java-development-manualAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 33 |
| Last updated | April 26, 2026 |
| Repository | bighardperson/computer-science-skills-collection ↗ |
What it does
Review Java code against the Alibaba Java Development Manual conventions for style, safety, and structure.
Who is it for?
Java teams enforcing the Alibaba coding, exception, security, and MySQL conventions in review.
When should I use this skill?
You are writing or reviewing Java code and need to check naming, exceptions, security, or MySQL conventions.
By the numbers
- 7 convention dimensions
- 3 enforcement levels: 强制/推荐/参考 (mandatory/recommended/reference)
Files
Java开发手册(嵩山版)
概述
本手册基于阿里巴巴Java开发手册(嵩山版),将规约分为7个维度。规约按约束力强弱分为:
| 级别 | 含义 | 说明 |
|---|---|---|
| 【强制】 | 必须遵守 | 违反可能导致严重问题 |
| 【推荐】 | 建议遵守 | 提升代码质量和可维护性 |
| 【参考】 | 可选择性采纳 | 根据实际情况判断 |
章节导航
根据需求选择对应章节的详细规约:
| 章节 | 适用场景 | 详细文档 |
|---|---|---|
| 编程规约 | 命名、格式、OOP、并发、集合处理 | coding-convention.md |
| 异常日志 | 错误码、异常处理、日志规范 | exception-log.md |
| 单元测试 | 测试用例、覆盖率、Mock | unit-test.md |
| 安全规约 | SQL注入、XSS、CSRF、脱敏 | security.md |
| MySQL数据库 | 建表、索引、SQL、ORM | mysql.md |
| 工程结构 | 分层架构、依赖管理、服务器 | project-structure.md |
| 设计规约 | UML、设计模式、设计原则 | design.md |
快速参考
命名规范速查
// 类名:UpperCamelCase
public class UserService { }
public class UserDO { } // DO/DTO/VO例外
// 方法名/变量:lowerCamelCase
private String userName;
public void getUserById() { }
// 常量:全大写+下划线
public static final int MAX_RETRY_COUNT = 3;
// 包名:全小写
package com.company.project.service;禁止事项速查
| 禁止 | 原因 |
|---|---|
| 拼音命名 | 可读性差 |
| 魔法值 | 难以维护 |
SELECT * | 性能和可维护性 |
| Executors创建线程池 | 可能OOM |
| 字符串拼接SQL | 注入风险 |
| finally中return | 丢失try返回值 |
| foreach中remove | ConcurrentModificationException |
必须事项速查
| 必须 | 原因 |
|---|---|
| 覆写方法加@Override | 避免签名错误 |
| 表必备三字段 | id, create_time, update_time |
| 敏感数据脱敏 | 隐私保护 |
| 参数校验 | 安全防护 |
| ThreadLocal回收 | 避免内存泄漏 |
| 日志用占位符 | 性能优化 |
异常处理速查
// 正确的异常处理
try {
// 业务逻辑
} catch (SpecificException e) {
logger.error("操作失败, 参数: {}", params, e);
throw new BusinessException("用户友好提示", e);
} finally {
// 资源关闭(JDK7+ try-with-resources)
}数据库速查
-- 建表必备
CREATE TABLE example (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 索引命名
-- 主键: pk_字段名
-- 唯一: uk_字段名
-- 普通: idx_字段名并发处理速查
// 线程池创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity),
new ThreadFactory() {
private AtomicInteger counter = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "worker-" + counter.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy()
);
// ThreadLocal使用
try {
threadLocal.set(value);
// 业务逻辑
} finally {
threadLocal.remove(); // 必须回收
}使用指南
代码审查场景
1. 命名检查 → 查看 coding-convention.md 的"命名风格"章节 2. 并发问题 → 查看 coding-convention.md 的"并发处理"章节 3. 异常处理 → 查看 exception-log.md 4. 安全问题 → 查看 security.md
新项目搭建场景
1. 架构设计 → 查看 design.md 2. 分层结构 → 查看 project-structure.md 3. 数据库设计 → 查看 mysql.md 4. 单元测试 → 查看 unit-test.md
问题排查场景
1. NPE问题 → 查看 exception-log.md 的"NPE防护" 2. 性能问题 → 查看 mysql.md 的"索引规约" 3. 并发问题 → 查看 coding-convention.md 的"并发处理"
{
"ownerId": "kn7f5zc4vn2b42kzz039kxd98d81f6dt",
"slug": "java-development-manual",
"version": "0.1.0",
"publishedAt": 1773365810610
}{
"slug": "java-development-manual",
"name": "Java Development Manual",
"version": "0.1.0",
"installedAt": 1776152399455,
"source": "skillhub"
}编程规约
目录
---
一、命名风格
【强制】规约
1. 禁止特殊字符开头/结尾:代码命名均不能以下划线或美元符号开始/结束
- 反例:
_name/__name/$name/name_/name$/name__
2. 禁止拼音混合命名:严禁使用拼音与英文混合,更不允许直接使用中文
- 正例:
ali/alibaba/taobao/hangzhou(国际通用名称可视同英文) - 反例:
DaZhePromotion/getPingfenByName()/String fw/int 某变量 = 3
3. 禁止歧视性词语:代码和注释中避免使用种族歧视性词语
- 正例:
blockList/allowList/secondary - 反例:
blackList/whiteList/slave
4. 类名使用UpperCamelCase:例外情况:DO/BO/DTO/VO/AO/PO/UID等
- 正例:
ForceCode/UserDO/HtmlDTO/XmlService - 反例:
forcecode/UserDo/HTMLDto/XMLService
5. 方法名、参数名、成员变量使用lowerCamelCase
- 正例:
localValue/getHttpMessage()/inputUserId
6. 常量全大写,下划线分隔
- 正例:
MAX_STOCK_COUNT/CACHE_EXPIRED_TIME - 反例:
MAX_COUNT/EXPIRED_TIME
7. 特殊类命名:
- 抽象类:
Abstract或Base开头 - 异常类:
Exception结尾 - 测试类:被测试类名开始,
Test结尾
8. 数组声明:类型与中括号紧挨
- 正例:
int[] arrayDemo - 反例:
String args[]
9. POJO布尔变量不加is前缀:避免框架解析序列化错误
- 说明:数据库字段用
is_xxx,需要在<resultMap>设置映射
10. 包名全小写,单数形式:点分隔符间仅一个自然语义单词
- 正例:
com.alibaba.ei.kunlun.aap.util、类名MessageUtils
11. 避免子父类成员变量同名
12. 杜绝不规范缩写
- 反例:
AbsClass/condi/Fu
13. 接口方法不加修饰符:保持简洁,加上Javadoc注释
14. Service/DAO实现类用Impl后缀
- 正例:
CacheServiceImpl实现CacheService
15. 枚举类带Enum后缀,成员全大写
- 正例:
ProcessStatusEnum.SUCCESS/ProcessStatusEnum.UNKNOWN_REASON
【推荐】规约
- 使用完整单词组合表达(自解释)
- 类型名词放词尾:
startTime/workQueue/nameList - 设计模式体现在命名中:
OrderFactory/LoginProxy/ResourceObserver
各层命名规约
| 层级 | 方法前缀 | 示例 |
|---|---|---|
| 获取单个对象 | get | getUserById |
| 获取多个对象 | list | listUsers |
| 获取统计值 | count | countUsers |
| 插入 | save/insert | saveUser |
| 删除 | remove/delete | deleteUser |
| 修改 | update | updateUser |
领域模型命名:
- 数据对象:
xxxDO(xxx为表名) - 数据传输对象:
xxxDTO - 展示对象:
xxxVO - 禁止命名成
xxxPOJO
---
二、常量定义
【强制】规约
1. 禁止魔法值:不允许未经预先定义的常量直接出现
// 反例
String key = "Id#taobao_" + tradeId;
cache.put(key, value);2. Long类型使用大写L
// 正例
Long a = 2L;
// 反例(容易混淆为数字21)
Long a = 2l;【推荐】规约
- 按功能归类常量:
CacheConsts/SystemConfigConsts - 常量复用层次:跨应用→应用内→子工程→包内→类内
- 固定范围变化用enum定义:
public enum SeasonEnum {
SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
private int seq;
SeasonEnum(int seq) { this.seq = seq; }
public int getSeq() { return seq; }
}---
三、代码格式
【强制】规约
1. 大括号规则:
- 空代码块:
{} - 非空:左大括号前不换行,后换行;右大括号前换行
2. 空格规则:
- 小括号内外不加空格
if/for/while/switch/do与括号间加空格- 运算符左右加空格
3. 缩进:4个空格,禁止Tab字符
4. 注释格式:双斜线与内容间仅一个空格
// 这是正确的注释格式5. 单行字符限制:不超过120字符
- 换行缩进4空格
- 运算符与下文一起换行
- 点符号与下文一起换行
- 逗号后换行
6. 参数逗号后加空格:
method(args1, args2, args3);7. IDE编码设置:UTF-8,换行符使用Unix格式
【推荐】规约
- 单个方法总行数不超过80行
- 不同逻辑/业务代码间插入空行
---
四、OOP规约
【强制】规约
1. 静态成员通过类名访问,不通过对象引用
2. 覆写方法必须加@Override注解
3. 可变参数放最后,同类型同含义才使用
4. 接口过时加@Deprecated注解
5. 禁止使用过时类/方法
6. equals避免NPE:用常量调用
// 正例
"test".equals(object);
// 推荐
Objects.equals(a, b);7. 整型包装类比较用equals:Integer缓存范围-128~127
8. 货币金额用整型存储:最小货币单位
9. 浮点数比较:
// 方式1:误差范围
float diff = 1e-6F;
if (Math.abs(a - b) < diff) { ... }
// 方式2:BigDecimal
BigDecimal a = new BigDecimal("1.0");
if (x.compareTo(y) == 0) { ... }10. BigDecimal比较用compareTo(),不用equals()
11. DO类属性类型匹配数据库字段类型
12. BigDecimal构造用String或valueOf:
// 正例
new BigDecimal("0.1");
BigDecimal.valueOf(0.1);
// 反例
new BigDecimal(0.1F); // 精度损失13. POJO属性用包装类型,局部变量用基本类型
14. POJO不设属性默认值
15. serialVersionUID不要随意修改
16. 构造方法禁止业务逻辑,放init方法
17. POJO必须写toString(),继承时加super.toString()
18. 禁止同时存在isXxx()和getXxx()
【推荐】规约
- String的split结果做边界检查
- 重载方法放一起
- 方法顺序:公有>私有>getter/setter
- setter/getter不加业务逻辑
- 循环内字符串连接用StringBuilder
- 慎用Object.clone()
- 访问控制从严
---
五、日期时间
【强制】规约
1. 年份用小写y:
// 正例
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
// 说明:YYYY是week in which year2. 区分大小写:
- 大写M:月份
- 小写m:分钟
- 大写H:24小时制
- 小写h:12小时制
3. 获取毫秒数:
// 正例
System.currentTimeMillis();
// 反例
new Date().getTime();4. 禁止使用:java.sql.Date / java.sql.Time / java.sql.Timestamp
5. 禁止写死一年365天:
// 正例
int daysOfThisYear = LocalDate.now().lengthOfYear();【推荐】规约
- 避免闰年2月问题
- 月份使用枚举:
Calendar.JANUARY
---
六、集合处理
【强制】规约
1. 覆写equals必须覆写hashCode
2. 判空用isEmpty(),不用size()==0
3. toMap()必须指定mergeFunction:
// 正例
Collectors.toMap(Pair::getKey, Pair::getValue, (v1, v2) -> v2)4. toMap()注意value为null抛NPE
5. subList结果不可强转ArrayList
6. keySet()/values()/entrySet()返回对象不可添加元素
7. Collections.emptyList()不可修改
8. subList场景注意父集合修改导致ConcurrentModificationException
9. 集合转数组用toArray(T[]):
String[] array = list.toArray(new String[0]);10. addAll()要做NPE判断
11. Arrays.asList()不可修改
12. foreach循环里禁止remove/add,用Iterator
13. Comparator必须满足三个条件
【推荐】规约
- 泛型定义使用diamond语法:
new HashMap<>(16) - 集合初始化指定大小:
(需要存储元素数 / 0.75) + 1 - 使用entrySet遍历Map,或JDK8的forEach
Map对null的支持:
| 集合类 | Key为null | Value为null |
|---|---|---|
| HashMap | 允许 | 允许 |
| ConcurrentHashMap | 不允许 | 不允许 |
| Hashtable | 不允许 | 不允许 |
| TreeMap | 不允许 | 允许 |
---
七、并发处理
【强制】规约
1. 单例对象保证线程安全
2. 线程池指定有意义名称
3. 线程资源必须通过线程池提供
4. 禁止Executors创建线程池,用ThreadPoolExecutor
- FixedThreadPool/SingleThreadPool:队列长度Integer.MAX_VALUE,可能OOM
- CachedThreadPool:线程数Integer.MAX_VALUE,可能OOM
5. SimpleDateFormat线程不安全:
// 正例:ThreadLocal
private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd")
);
// JDK8推荐:DateTimeFormatter6. ThreadLocal必须回收:
try {
threadLocal.set(value);
// ...
} finally {
threadLocal.remove();
}7. 同步调用考量锁性能:能无锁不锁,能块锁不全方法锁
8. 多资源加锁保持一致顺序
9. 锁的正确使用:
Lock lock = new XxxLock();
lock.lock();
try {
doSomething();
} finally {
lock.unlock();
}10. tryLock必须判断是否持有锁
11. 并发修改同一记录需加锁:乐观锁version或悲观锁
12. 多线程定时任务用ScheduledExecutorService,不用Timer
【推荐】规约
- 资金相关用悲观锁:一锁、二判、三更新、四释放
- CountDownLatch异步转同步,确保countDown执行
- Random多线程性能问题用ThreadLocalRandom
- 双重检查锁用volatile
- LongAdder比AtomicLong性能更好
---
八、控制语句
【强制】规约
1. switch每个case必须终止(break/return/continue),必须有default
2. switch String参数必须先null判断
3. if/else/for/while/do必须用大括号
4. 三目运算符注意自动拆箱NPE
5. 高并发避免用"等于"判断作中断条件
【推荐】规约
- 超过10行的方法,return/throw后加空行
- 异常分支用卫语句:
public void process(Man man) {
if (man.isUgly()) return;
if (man.isPoor()) return;
// 正常逻辑
}- if-else不超过3层,超过用策略/状态模式
- 复杂逻辑提取为布尔变量
- 不在条件表达式中插入赋值语句
- 循环体优化:对象定义、数据库连接等移到循环外
- 避免取反逻辑
---
九、注释规约
【强制】规约
1. 类、属性、方法用Javadoc格式/** */
2. 抽象方法必须Javadoc注释:说明功能、参数、返回值、异常
3. 类必须添加创建者和日期
4. *方法内单行用//,多行用/ /*
5. 枚举字段必须有注释
【推荐】规约
- 英文不好就用中文注释
- 代码修改同步更新注释
- 删除未使用的字段、方法、参数
【参考】规约
- 谨慎注释代码,无用则删除
- 特殊标记注明人和时间:TODO / FIXME
---
十、前后端规约
【强制】规约
1. API明确要素:协议(HTTPS)、域名、路径、请求方法、请求内容、状态码、响应体
2. 空列表返回空数组[]或空集合{}
3. 服务端错误返回:HTTP状态码 + errorCode + errorMessage + 用户提示
4. JSON的key用lowerCamelCase
5. 超大整数用String返回,禁止Long(JS精度问题)
6. URL参数不超过2048字节
7. body内容控制长度
8. 翻页边界处理:小于1返回第一页,大于总数返回最后一页
9. 内部重定向用forward
【推荐】规约
- 响应设置缓存:
Cache-Control: s-maxage=秒数 - 使用JSON格式而非XML
- 时间格式统一:
yyyy-MM-dd HH:mm:ss,GMT时区
设计规约
目录
---
一、UML图使用规范
【强制】规约
| 场景 | 使用UML图 |
|---|---|
| User超过1类 + UserCase超过5个 | 用例图 |
| 业务对象状态超过3个 | 状态图 |
| 调用链路涉及对象超过3个 | 时序图 |
| 模型类超过5个且有复杂依赖 | 类图 |
| 超过2个对象协作+复杂流程 | 活动图 |
状态图示例(订单状态)
┌──────────┐ 付款成功 ┌──────────┐ 发货 ┌──────────┐
│ 已下单 │ ───────────────> │ 已付款 │ ────────────> │ 已发货 │
└──────────┘ └──────────┘ └──────────┘
│ │ │
│ 取消 │ 取消 │ 确认收货
↓ ↓ ↓
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 已取消 │ │ 已取消 │ │ 已完成 │
└──────────┘ └──────────┘ └──────────┘注意:已下单与已完成之间不可能直接转换
---
二、存储与数据结构设计
【强制】规约
存储方案和底层数据结构设计需评审通过并沉淀为文档
评审内容包括: 1. 存储介质选型 2. 表结构设计是否满足技术方案 3. 存取性能和存储空间是否满足业务发展 4. 表/字段之间的辩证关系 5. 字段名称、类型、索引
数据结构变更同样需要评审
---
三、设计原则
【推荐】规约
1. 系统架构设计目标
| 目标 | 说明 |
|---|---|
| 确定系统边界 | 技术层面的做与不做 |
| 确定模块关系 | 依赖关系、宏观输入输出 |
| 确定演化原则 | 后续设计的框架和方向 |
| 确定非功能需求 | 安全性、可用性、可扩展性 |
2. 单一职责原则(SRP)
类在设计时只负责一项职责
3. 优先聚合/组合而非继承
// 正例:聚合/组合
public class Car {
private Engine engine; // 组合
}
// 继承需符合里氏代换原则4. 依赖倒置原则(DIP)
// 正例:依赖抽象
public class OrderService {
private PaymentProcessor processor; // 依赖接口
}
// 反例:依赖具体实现
public class OrderService {
private AlipayProcessor processor; // 依赖具体类
}5. 开闭原则(OCP)
对扩展开放,对修改闭合
// 正例:通过扩展支持新功能
public interface PaymentProcessor {
Result process(Order order);
}
public class AlipayProcessor implements PaymentProcessor { }
public class WechatProcessor implements PaymentProcessor { }
public class NewPaymentProcessor implements PaymentProcessor { } // 扩展6. DRY原则(Don't Repeat Yourself)
// 正例:抽取公共方法
public class UserService {
public void createUser(UserDTO dto) {
validateUser(dto); // 复用
}
public void updateUser(UserDTO dto) {
validateUser(dto); // 复用
}
private void validateUser(UserDTO dto) {
// 校验逻辑
}
}---
四、敏捷开发误区
【推荐】规约
误区:敏捷开发 = 讲故事 + 编码 + 发布
正解:
- 敏捷是快速交付迭代可用的系统
- 省略多余设计方案
- 摒弃传统审批流程
- 核心关键点的设计和文档沉淀仍然需要
反例:以敏捷为借口催进度,系统代码像面条,一年后大规模重构
---
五、可扩展性设计
【参考】规约
可扩展性本质:找到系统变化点,隔离变化点
极致扩展性标志:需求新增不会在原有代码上做任何修改
// 正例:策略模式隔离变化
public interface PricingStrategy {
BigDecimal calculate(Order order);
}
public class NormalPricing implements PricingStrategy { }
public class VipPricing implements PricingStrategy { }
public class PromotionPricing implements PricingStrategy { }
// 新增定价策略只需新增类,无需修改现有代码设计文档作用
1. 明确需求、理顺逻辑、后期维护 2. 避免为了设计而设计 3. 代码即文档是错误的观点:
- 清晰代码只是文档片断
- 深度调用、依赖关系需要文档呈现
异常日志规约
目录
---
一、错误码
【强制】规约
1. 错误码制定原则:快速溯源、沟通标准化
2. 错误码不体现版本号和错误等级
3. 全部正常返回00000
4. 错误码格式:5位字符串 = 错误来源(1位) + 数字编号(4位)
| 来源 | 含义 | 说明 |
|---|---|---|
| A | 用户端错误 | 参数错误、版本过低、支付超时等 |
| B | 当前系统错误 | 业务逻辑出错、程序健壮性差等 |
| C | 第三方服务错误 | CDN出错、消息投递超时等 |
5. 编号先到先得,不与业务/组织架构挂钩
6. 避免随意定义新错误码,优先使用已有错误码
7. 错误码不直接输出给用户
【推荐】规约
- 业务独特信息由errorMessage承载
- 第三方错误码可转义(C→B),带上原错误码
【参考】规约
- 一级宏观错误码:
A0001(用户端错误) /B0001(系统执行出错) /C0001(调用第三方出错)
常用错误码示例
| 错误码 | 描述 |
|---|---|
| 00000 | 一切OK |
| A0001 | 用户端错误 |
| A0100 | 用户注册错误 |
| A0111 | 用户名已存在 |
| A0121 | 密码长度不够 |
| B0001 | 系统执行出错 |
| C0001 | 调用第三方服务出错 |
---
二、异常处理
【强制】规约
1. 预检查规避RuntimeException:
// 正例
if (obj != null) { obj.method(); }
// 反例
try { obj.method(); } catch (NullPointerException e) { }2. 异常不用于流程控制:效率比条件判断低很多
3. catch区分异常类型:对非稳定代码分类处理
4. 捕获后必须处理:
- 不能空处理
- 不处理则向上抛出
- 最外层必须转化为用户可理解内容
5. 事务场景注意手动回滚
6. finally块关闭资源:
// JDK7+ 推荐
try (InputStream is = new FileInputStream(file)) {
// ...
}7. 禁止在finally中使用return:
// 反例:会丢弃try中的return
private int x = 0;
public int checkReturn() {
try { return ++x; }
finally { return ++x; } // 返回2而非1
}8. 捕获异常与抛出异常匹配(或是父类)
9. RPC/动态类用Throwable拦截
【推荐】规约
1. 返回null时必须注释说明
2. 防止NPE的场景:
- 返回基本类型但return包装对象
- 数据库查询结果
- 集合元素即使isNotEmpty也可能为null
- 远程调用返回对象
- Session获取的数据
- 级联调用
obj.getA().getB().getC()
3. 使用自定义异常:
- 推荐:
DAOException/ServiceException - 避免:直接抛
RuntimeException/Exception/Throwable
【参考】规约
RPC返回方式选择:
- 公司外HTTP/API接口:使用errorCode
- 应用内部:异常抛出
- 跨应用RPC:Result方式封装
// Result方式示例
public class Result<T> {
private boolean success;
private String errorCode;
private String errorMessage;
private T data;
}---
三、日志规约
【强制】规约
1. 使用日志框架SLF4J,不直接用Log4j/Logback:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);2. 日志保存时间:
- 至少15天(异常可能"周"为频次)
- 敏感操作日志不少于6个月(法规要求)
3. 日志文件命名:
- 当天:
应用名.log - 历史:
应用名.log.yyyy-MM-dd - 路径:
/home/admin/应用名/logs/
4. 扩展日志命名:appName_logType_logName.log
5. 字符串拼接用占位符:
// 正例
logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);6. trace/debug/info必须级别判断:
if (logger.isDebugEnabled()) {
logger.debug("Current ID is: {} and name is: {}", id, getName());
}7. 避免重复打印:设置additivity=false
8. 生产环境禁止:
System.out/System.erre.printStackTrace()
9. 异常日志包含:案发现场信息 + 异常堆栈
logger.error("inputParams:{} and errorMessage:{}",
params, e.getMessage(), e);10. 禁止直接JSON工具转换对象
【推荐】规约
1. 谨慎记录日志:
- 生产禁止debug日志
- 有选择输出info日志
- warn注意输出量
2. 参数错误用warn,不用error
3. 日志语言:英文优先,说不清用中文
MySQL数据库规约
目录
---
一、建表规约
【强制】规约
1. 布尔字段命名:is_xxx,类型unsigned tinyint(1表示是,0表示否)
2. 表名/字段名规范:
- 必须使用小写字母或数字
- 禁止数字开头
- 正例:
aliyun_admin/rdc_config/level3_name
3. 表名不使用复数名词
4. 禁用保留字:desc / range / match / delayed等
5. 索引命名规范:
- 主键索引:
pk_字段名 - 唯一索引:
uk_字段名 - 普通索引:
idx_字段名
6. 小数类型用decimal,禁止使用float和double
7. 字符串长度几乎相等时用char定长
8. varchar长度不超过5000,超过则用text独立建表
9. 表必备三字段:
CREATE TABLE example (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='示例表';【推荐】规约
1. 表命名:业务名称_表的作用 2. 库名与应用名称一致 3. 分库分表时机:单表超过500万行或2GB
存储长度参考
| 对象 | 范围 | 类型 | 字节 |
|---|---|---|---|
| 年龄 | 150岁内 | tinyint unsigned | 1 |
| 数百岁 | smallint unsigned | 2 | |
| 数千万年 | int unsigned | 4 | |
| 约50亿年 | bigint unsigned | 8 |
---
二、索引规约
【强制】规约
1. 唯一特性字段必须建唯一索引
2. 超过3个表禁止join
3. varchar字段索引必须指定长度:
CREATE INDEX idx_name ON user(name(20));4. 页面搜索严禁左模糊或全模糊
【推荐】规约
1. 利用索引有序性:
-- 索引:idx_a_b_c
SELECT * FROM table WHERE a=? AND b=? ORDER BY c;2. 利用覆盖索引避免回表
3. 延迟关联优化超多分页:
SELECT t1.*
FROM user t1,
(SELECT id FROM user WHERE condition LIMIT 100000, 20) t2
WHERE t1.id = t2.id;4. SQL性能目标:至少range级别,要求ref,最好是consts
5. 组合索引区分度最高的在最左边
---
三、SQL语句
【强制】规约
1. *统计行数用count()**:
-- 正例
SELECT count(*) FROM user;2. sum()注意NPE:
SELECT IFNULL(SUM(amount), 0) FROM orders;3. 判NULL用ISNULL()
4. 分页count为0直接返回
5. 禁止使用外键与级联
6. 禁止使用存储过程
7. 数据订正先select确认
8. 多表查询字段加别名限定:
SELECT t1.name FROM user AS t1, order AS t2 WHERE t1.id = t2.user_id;【推荐】规约
1. 表别名用as,按t1、t2、t3命名 2. in操作控制在1000个之内 3. 字符集用utf8mb4
---
四、ORM映射
【强制】规约
1. 禁止SELECT \*:
<select id="listUsers">
SELECT id, username, email FROM user
</select>2. 参数使用#{}:
<!-- 正例:防SQL注入 -->
<select id="getById">SELECT * FROM user WHERE id = #{id}</select>3. 必须定义resultMap
4. 更新时必须更新update_time
【推荐】规约
不写大而全的更新接口:
<update id="updateUserSelective">
UPDATE user
<set>
<if test="name != null">name = #{name},</if>
<if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}
</update>工程结构规约
目录
---
一、应用分层
推荐分层架构
┌─────────────────────────────────────────────────────┐
│ 开放API层 │
│ (RPC接口/HTTP接口/网关控制) │
├─────────────────────────────────────────────────────┤
│ 终端显示层 │
│ (velocity/JS/JSP/移动端) │
├─────────────────────────────────────────────────────┤
│ Web层 │
│ (Controller/参数校验/简单业务) │
├─────────────────────────────────────────────────────┤
│ Service层 │
│ (具体业务逻辑) │
├─────────────────────────────────────────────────────┤
│ Manager层 │
│ (通用业务/第三方封装/DAO组合) │
├─────────────────────────────────────────────────────┤
│ DAO层 │
│ (数据访问/MySQL/Oracle/HBase) │
└─────────────────────────────────────────────────────┘各层职责
| 层级 | 职责 | 说明 |
|---|---|---|
| 开放API层 | 对外暴露 | RPC接口、HTTP接口、网关 |
| 终端显示层 | 渲染展示 | velocity、JS、JSP、移动端 |
| Web层 | 访问控制转发 | Controller、参数校验 |
| Service层 | 业务逻辑 | 具体业务处理 |
| Manager层 | 通用处理 | 第三方封装、缓存、DAO组合 |
| DAO层 | 数据访问 | MySQL、Oracle、HBase |
分层异常处理
| 层级 | 异常处理方式 |
|---|---|
| DAO层 | catch(Exception) + throw DAOException,不打印日志 |
| Service层 | 必须记录日志到磁盘 |
| Web层 | 跳转友好错误页面 |
| 开放接口层 | 转化为错误码和错误信息 |
---
二、领域模型
| 模型 | 全称 | 说明 |
|---|---|---|
| DO | Data Object | 与数据库表一一对应 |
| DTO | Data Transfer Object | 数据传输对象 |
| BO | Business Object | 业务对象 |
| Query | - | 数据查询对象(超过2个参数封装) |
| VO | View Object | 显示层对象 |
注意:Query禁止使用Map类传输
---
三、二方库依赖
【强制】规约
1. GAV命名规范:
- GroupID:
com.{公司/BU}.业务线[.子业务线],最多4级 - ArtifactID:
产品线名-模块名
2. 版本号格式:主版本号.次版本号.修订号
- 主版本号:产品方向改变、大规模API不兼容
- 次版本号:相对兼容、增加主要功能
- 修订号:完全兼容、修复BUG
- 起始版本号必须为1.0.0
3. 线上不依赖SNAPSHOT版本
4. 依赖群定义统一版本变量:
<properties>
<spring.version>5.3.20</spring.version>
</properties>5. 禁止相同GAV不同Version
【推荐】规约
1. 依赖声明与版本仲裁分离:
<!-- 父POM:版本仲裁 -->
<dependencyManagement>
<dependencies>...</dependencies>
</dependencyManagement>
<!-- 子POM:依赖声明 -->
<dependencies>...</dependencies>2. 二方库不要有配置项
---
四、服务器配置
【推荐】规约
1. 调小TCP time_wait超时时间:
# /etc/sysctl.conf
net.ipv4.tcp_fin_timeout = 302. 调大最大文件句柄数
3. JVM配置OOM时输出dump:
-XX:+HeapDumpOnOutOfMemoryError4. Xms和Xmx设置相同大小:
-Xms4g -Xmx4g---
项目目录结构示例
project-name/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/company/project/
│ │ │ ├── controller/ # Web层
│ │ │ ├── service/ # Service层
│ │ │ │ └── impl/
│ │ │ ├── manager/ # Manager层
│ │ │ ├── dao/ # DAO层
│ │ │ ├── model/ # 领域模型
│ │ │ │ ├── dto/
│ │ │ │ ├── vo/
│ │ │ │ └── query/
│ │ │ └── config/
│ │ └── resources/
│ │ ├── mapper/
│ │ └── application.yml
│ └── test/
│ └── java/安全规约
目录
---
权限控制
【强制】隶属于用户个人的页面或功能必须进行权限控制校验
// 正例:水平权限校验
public void viewMessage(Long messageId, Long userId) {
Message message = messageDao.getById(messageId);
if (!message.getUserId().equals(userId)) {
throw new PermissionDeniedException("无权访问");
}
}风险场景:查看他人私信、修改他人订单、删除他人数据
---
数据脱敏
【强制】用户敏感数据禁止直接展示,必须脱敏
public class DataMaskUtil {
// 手机号脱敏:139****1219
public static String maskPhone(String phone) {
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
}
// 身份证脱敏:110***********1234
public static String maskIdCard(String idCard) {
return idCard.substring(0, 3) + "***********" + idCard.substring(idCard.length() - 4);
}
// 银行卡脱敏:6222 **** **** 1234
public static String maskBankCard(String cardNo) {
return cardNo.substring(0, 4) + " **** **** " + cardNo.substring(cardNo.length() - 4);
}
}---
SQL注入防护
【强制】用户输入的SQL参数严格使用参数绑定
// 正例:使用PreparedStatement
public User findByUsername(String username) {
String sql = "SELECT * FROM user WHERE username = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{username}, userRowMapper);
}
// 反例:字符串拼接SQL
public User findByUsername(String username) {
String sql = "SELECT * FROM user WHERE username = '" + username + "'";
// 危险!
}MyBatis正确用法:
#{param}安全${param}不安全,易注入
---
参数校验
【强制】用户请求传入的任何参数必须做有效性验证
忽略参数校验可能导致:
page size过大导致内存溢出- 恶意
order by导致数据库慢查询 - 缓存击穿、SSRF、SQL注入、ReDoS
// 正例:参数校验
public PageResult<User> listUsers(UserQuery query) {
// 分页参数校验
if (query.getPageNum() == null || query.getPageNum() < 1) {
query.setPageNum(1);
}
if (query.getPageSize() == null || query.getPageSize() > 100) {
query.setPageSize(20);
}
// 排序字段白名单
if (!ALLOWED_ORDER_FIELDS.contains(query.getOrderBy())) {
throw new IllegalArgumentException("非法排序字段");
}
return userDao.queryPage(query);
}---
XSS防护
【强制】禁止向HTML页面输出未经安全过滤的用户数据
import org.apache.commons.text.StringEscapeUtils;
public String safeOutput(String userInput) {
return StringEscapeUtils.escapeHtml4(userInput);
}---
CSRF防护
【强制】表单、AJAX提交必须执行CSRF安全验证
@Controller
public class CsrfController {
@GetMapping("/form")
public String showForm(HttpSession session, Model model) {
String csrfToken = UUID.randomUUID().toString();
session.setAttribute("csrfToken", csrfToken);
model.addAttribute("csrfToken", csrfToken);
return "form";
}
@PostMapping("/submit")
public String submitForm(HttpSession session, @RequestParam String csrfToken) {
String sessionToken = (String) session.getAttribute("csrfToken");
if (!csrfToken.equals(sessionToken)) {
throw new SecurityException("CSRF验证失败");
}
return "success";
}
}---
URL重定向安全
【强制】URL外部重定向必须执行白名单过滤
public class RedirectUtil {
private static final Set<String> ALLOWED_DOMAINS = Set.of(
"www.example.com", "m.example.com"
);
public static String safeRedirect(String targetUrl) {
try {
URL url = new URL(targetUrl);
if (!ALLOWED_DOMAINS.contains(url.getHost())) {
return "/";
}
return targetUrl;
} catch (MalformedURLException e) {
return "/";
}
}
}---
防重放机制
【强制】使用平台资源必须实现防重放机制
适用场景:短信、邮件、电话、下单、支付
@Service
public class SmsService {
// 发送频率限制:1分钟1次
private static final int INTERVAL_SECONDS = 60;
// 日发送次数限制:10次
private static final int DAILY_LIMIT = 10;
public void sendVerifyCode(String phone) {
String intervalKey = "sms:interval:" + phone;
// 频率限制
if (redisTemplate.hasKey(intervalKey)) {
throw new BusinessException("发送过于频繁");
}
// 日次数限制
Long count = redisTemplate.opsForValue().increment("sms:daily:" + phone);
if (count > DAILY_LIMIT) {
throw new BusinessException("今日发送次数已达上限");
}
// 发送验证码...
redisTemplate.opsForValue().set(intervalKey, "1",
Duration.ofSeconds(INTERVAL_SECONDS));
}
}单元测试规约
目录
---
AIR原则
好的单元测试必须遵守AIR原则:
| 原则 | 含义 | 说明 |
|---|---|---|
| Automatic | 自动化 | 测试执行完全自动化,非交互式 |
| Independent | 独立性 | 测试用例间不互相调用,不依赖执行顺序 |
| Repeatable | 可重复 | 不受外界环境影响,可重复执行 |
---
BCDE原则
编写单元测试遵守BCDE原则:
| 原则 | 含义 | 说明 |
|---|---|---|
| Border | 边界值测试 | 循环边界、特殊取值、特殊时间点、数据顺序 |
| Correct | 正确性测试 | 正确输入得到预期结果 |
| Design | 设计结合 | 与设计文档结合编写测试 |
| Error | 错误测试 | 非法数据、异常流程、业务允许外的情况 |
---
强制规约
1. 测试自动化
- 测试必须全自动执行,非交互式
- 禁止使用
System.out人肉验证 - 必须使用assert验证结果
2. 测试独立性
- 测试用例间不能互相调用
- 不能依赖执行顺序
3. 测试可重复性
- 不受外部环境影响(网络、服务、中间件)
- 通过DI注入本地/Mock实现
4. 测试粒度
- 至多是类级别,一般是方法级别
- 单测不负责跨类/跨系统的交互逻辑
5. 核心业务增量代码必须单测通过
6. 测试代码目录
- 必须写在
src/test/java - 禁止写在业务代码目录下
---
推荐规约
1. 测试覆盖率目标
| 类型 | 语句覆盖率 | 分支覆盖率 |
|---|---|---|
| 一般代码 | 70% | - |
| 核心模块 | 100% | 100% |
重点测试:DAO层、Manager层、可重用度高的Service
2. 数据库测试
- 不假设数据库数据存在
- 不直接操作数据库插入数据
- 使用程序插入或导入数据方式准备
3. 数据库测试数据隔离
- 设定自动回滚机制
- 或用明确前缀标识测试数据
4. 可测性设计
避免以下情况:
- 构造方法做太多事情
- 过多全局变量和静态方法
- 过多外部依赖
- 过多条件语句
5. 测试时机
- 项目提测前完成单元测试
- 不建议项目发布后补充
---
测试代码示例
/**
* UserService单元测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private ExternalApiService externalApiService;
private User testUser;
@Before
public void setUp() {
testUser = new User();
testUser.setUsername("test_user_" + System.currentTimeMillis());
}
@After
public void tearDown() {
if (testUser.getId() != null) {
userService.delete(testUser.getId());
}
}
// 正常流程测试 (Correct)
@Test
public void testCreateUser_success() {
UserDTO dto = new UserDTO();
dto.setUsername("newuser");
Long userId = userService.createUser(dto);
assertNotNull(userId);
}
// 边界值测试 (Border)
@Test(expected = IllegalArgumentException.class)
public void testCreateUser_usernameTooLong() {
UserDTO dto = new UserDTO();
dto.setUsername("a".repeat(256));
userService.createUser(dto);
}
// 异常流程测试 (Error)
@Test(expected = DuplicateUserException.class)
public void testCreateUser_duplicateUsername() {
UserDTO dto = new UserDTO();
dto.setUsername("duplicate");
userService.createUser(dto);
userService.createUser(dto); // 重复创建
}
}