From 6798a567a2b4cda90aec9071c5ca76ab610ffe70 Mon Sep 17 00:00:00 2001 From: 8ga <8ga@email.com> Date: Sat, 26 Sep 2026 16:09:11 +0800 Subject: [PATCH] add --- java-spec/SKILL.md | 18 ++ java-spec/examples/ExampleDTO.java | 51 ++++ java-spec/examples/ExampleEntity.java | 93 ++++++++ java-spec/examples/StateEnum.java | 27 +++ java-spec/examples/api-doc.md | 60 +++++ java-spec/examples/example_table.sql | 12 + java-spec/references/coding.md | 41 ++++ java-spec/references/design-patten.md | 224 ++++++++++++++++++ java-spec/references/directory.md | 113 +++++++++ java-spec/references/nameing-convention.md | 97 ++++++++ .../references/open-source-project-doc.md | 44 ++++ java-spec/references/orm.md | 47 ++++ 12 files changed, 827 insertions(+) create mode 100644 java-spec/SKILL.md create mode 100644 java-spec/examples/ExampleDTO.java create mode 100644 java-spec/examples/ExampleEntity.java create mode 100644 java-spec/examples/StateEnum.java create mode 100644 java-spec/examples/api-doc.md create mode 100644 java-spec/examples/example_table.sql create mode 100644 java-spec/references/coding.md create mode 100644 java-spec/references/design-patten.md create mode 100644 java-spec/references/directory.md create mode 100644 java-spec/references/nameing-convention.md create mode 100644 java-spec/references/open-source-project-doc.md create mode 100644 java-spec/references/orm.md diff --git a/java-spec/SKILL.md b/java-spec/SKILL.md new file mode 100644 index 0000000..fac0d3d --- /dev/null +++ b/java-spec/SKILL.md @@ -0,0 +1,18 @@ +--- +name: java-spec +description: 在编写Java代码时必备技能。 +--- + +# java-spec + +作为一名资深Java开发者,**代码的质量是衡定工作产出价值中最重要的判定依据**,你将恪守下面的要求,站在资深开发者的立场,去生成干净、高质量的代码。 + +1. 请严格遵守[命名规范](./references/nameing-convention.md)、[代码编写规范](./references/coding.md)、[通用的目录结构](./references/directory.md)生成。 + +2. 在修改、新建数据库表、实体类时,阅读[实体类与数据库表字段的映射规则](./references/orm.md),并参考[MySQL建表语句示例](./examples/example_table.sql)、[Java实体类示例](./examples/ExampleEntity.java)生成。 + +3. 在新建、修改枚举或DTO、VO的时候,参考[Java枚举类示例](./examples/StateEnum.java)、[DTO或者VO示例](./examples/ExampleDTO.java)生成。 + +4. 在排查问题或基于开源的三方类库编写、修改代码时,或许能在[开源项目参考文档](./references/open-source-project-doc.md)中找到有用的答案和关键线索。 + +5. 当被要求编写HTTP接口文档时,参考[api-doc.md](./examples//api-doc.md)生成。 \ No newline at end of file diff --git a/java-spec/examples/ExampleDTO.java b/java-spec/examples/ExampleDTO.java new file mode 100644 index 0000000..2e230b7 --- /dev/null +++ b/java-spec/examples/ExampleDTO.java @@ -0,0 +1,51 @@ +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.Getter; +import org.springframework.format.annotation.DateTimeFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.math.BigDecimal; + +@Data +@Schema(title = "DTO示例") +public class ExampleDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @NotBlank + @Schema(description = "字符串") + private String strField; + + @Schema(description = "长整数") + @JsonSerialize(using = ToStringSerializer.class) + private Long longField; + + @NotNull + @Schema(description = "日期类型", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate dateField; + + @NotNull + @Schema(description = "时间类型", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime timeField; + + @NotNull + @Schema( + title = "状态", + description = "参考示例值", + example = StateEnum.DESC + ) + private StateEnum state; + +} \ No newline at end of file diff --git a/java-spec/examples/ExampleEntity.java b/java-spec/examples/ExampleEntity.java new file mode 100644 index 0000000..d3b586c --- /dev/null +++ b/java-spec/examples/ExampleEntity.java @@ -0,0 +1,93 @@ +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.Getter; +import org.springframework.format.annotation.DateTimeFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.math.BigDecimal; + +@Data +@TableName(value = "example_table", autoResultMap = true) +@Schema(title = "示例表") +public class ExampleEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "ID") + @TableId(value = "id", type = IdType.ASSIGN_ID) + @TableField(value = "id") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + @NotBlank + @Schema(description = "字符串") + @TableField(value = "str_field") + private String strField; + + @Schema(description = "整数", defaultValue = "0") + @TableField(value = "int_field") + private Integer intField; + + @Schema(description = "长整数") + @TableField(value = "long_field") + @JsonSerialize(using = ToStringSerializer.class) + private Long longField; + + @Schema(description = "高精度浮点数") + @TableField(value = "decimal_field") + private BigDecimal decimalField; + + @NotNull + @Schema(description = "日期类型", pattern = "yyyy-MM-dd") + @TableField(value = "date_field") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate dateField; + + @NotNull + @Schema(description = "时间类型", pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(value = "time_field") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime timeField; + + @NotNull + @Schema( + title = "状态", + description = "参考示例值", + example = StateEnum.DESC + ) + @TableField(value = "state") + private StateEnum state; + + @Schema(description = "JSON类型") + @TableField(value = "json_field", typeHandler = JacksonTypeHandler.class) + private JsonField jsonField; + + @Data + public static class JsonField implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "示例字段1") + private String field1; + + @Schema(description = "示例字段2") + private Integer field2; + } + +} \ No newline at end of file diff --git a/java-spec/examples/StateEnum.java b/java-spec/examples/StateEnum.java new file mode 100644 index 0000000..7a12834 --- /dev/null +++ b/java-spec/examples/StateEnum.java @@ -0,0 +1,27 @@ +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +@Schema(description = "某某状态") +public enum StateEnum { + + ONLY_READ("only_read"), + + READ_WRITE("read_write"); + + public static final String DESC = "only_read=只读、read_write=读写"; + + @JsonValue + @EnumValue + private final String value; + + @JsonCreator + public static StateEnum deserialize(String input) { + return Arrays.stream(values()).filter(e -> e.getValue().equals(input)).findFirst().orElse(null); + } +} \ No newline at end of file diff --git a/java-spec/examples/api-doc.md b/java-spec/examples/api-doc.md new file mode 100644 index 0000000..956e74a --- /dev/null +++ b/java-spec/examples/api-doc.md @@ -0,0 +1,60 @@ +# 智能体使用数据 + +以下是关于智能体使用数据的API文档。 + +## 累计使用数据卡片 + +- 请求地址:/agent/metrics/total +- 请求方式:GET + +### 请求参数 + +| 参数名称 | 是否必传 | 类型 | 描述 | +|:-----|:-----|:-----|:-----| +| agentId | 是 | String | 智能体ID | + +### 请求示例 + +```bash +curl -X GET {gateway}/agent/metrics/total?agentId={agentId} +``` + +### 响应参数 + +| 参数名称 | 类型 | 描述 | +|:-----|:-----|:-----| +| code | Number | 返回码(200=成功) | +| msg | String | 提示信息 | +| data | Object | 返回数据 | +| data.agentId | String | 智能体 ID | +| data.agentName | String | 智能体名称 | +| data.inputToken | Number | 累计输入Token | +| data.outputToken | Number | 累计输出Token | +| data.userNum | Number | 累计用户数 | +| data.chatNum | Number | 累计对话数 | + +### 成功示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "agentId": "123456", + "agentName": "智能体名称", + "inputToken": "123243654645", + "outputToken": "345645765765", + "userNum": 5678, + "chatNum": 45678999 + } +} +``` + +### 失败示例 + +```json +{ + "code": 500, + "msg": "错误信息" +} +``` \ No newline at end of file diff --git a/java-spec/examples/example_table.sql b/java-spec/examples/example_table.sql new file mode 100644 index 0000000..b242f12 --- /dev/null +++ b/java-spec/examples/example_table.sql @@ -0,0 +1,12 @@ +CREATE TABLE `example_table` ( + `id` BIGINT NOT NULL COMMENT 'ID', + `str_field` VARCHAR(32) NOT NULL COMMENT '字符串`, + `int_field` INT DEFAULT NULL COMMENT '整数`, + `long_field` BIGINT DEFAULT NULL COMMENT '长整数`, + `decimal_field` DECIMAL(14, 2) DEFAULT NULL COMMENT '高精度浮点数`, + `date_field` DATE NOT NULL COMMENT '日期类型`, + `time_field` DATETIME NOT NULL COMMENT '日期类型`, + `state` VARCHAR(32) NOT NULL COMMENT '状态:only_read、read_write', + `json_field` JSON DATETIME NULL COMMENT 'JSON类型' + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='示例表'; \ No newline at end of file diff --git a/java-spec/references/coding.md b/java-spec/references/coding.md new file mode 100644 index 0000000..8ee4866 --- /dev/null +++ b/java-spec/references/coding.md @@ -0,0 +1,41 @@ +# 代码编写规范 + +## Java的新特性 + +目前主流的JDK选型有4个版本:8、17、21、25。通过`pom.xml`中的``标签可以定位当前项目的JDK版本是多少。积极的使用新特性,例如:局部变量类型推断、Switch表达式、模式匹配、文本块、记录类、虚拟线程。**虚拟线程应当用于IO密集型的任务,而不是CPU密集型任务。** + +## 必须恪守的基本原则 + +- 一个Java源文件的行数**不得超过300行**,否则拆成多个文件。 +- 一个方法的行数**不得超过20行**,否则拆成多个方法。 +- 积极使用`Optional`处理NULL值,避免大量的`if (obj != null)`。 +- 优先使用`Stream`,若`map`或`flatMap`代码块**超过5行**就单独写一个方法。如果`map`代码块需要中间对象且JDK版本高于17,则优先使用记录类,否则退化到普通java类,记录类和DTO存放在一个目录。 +- 构建JavaBean对象,优先使用构造函数而不是单独写方法,**只有3个以内的属性,应提供一个全部参数的构造函数**。 +- 只在关键地方插入简洁的注释,不啰嗦、不废话、不长篇大论。尽量用清晰明确的命名,避免添加注释。 + +## 三方库的使用原则 + +- 如果项目引入了`Lombok`,请积极使用,无需担心编译速度被拖慢。 +- 如果项目引入了`hutool`,请尽量少写工具类,最大程度复用`hutool-core`、`hutool-json`、`hutool-system`模块中的工具,其他模块的包慎用!当JDK版本高于11,不得引入`hutool-http`工具包,使用JDK内置的`HttpClient`替代,可以新建一个`HttpUtils`减少代码冗余。 +- 如果项目引入了`jackson`,`ObjectMapper`或`JsonMapper`应该单例使用,且基于`jackson`新建1个`JsonUtils`去替代`hutool-json`是更好的选择。但不能创建多个`JsonUtils`,能复用则复用。 + +## 集合与队列 + +| 集合工具 | 何时使用 | +| --- | --- | +| ArrayList | 在非多线程场景使用。
方法返回空集合使用`Collections.emptyList()`。
如果是`Dubbo`接口的返回值,空集合要使用`new ArrayList<>()`。| +| HashSet | 在非多线程且**没有排序要求**的场景使用。
方法返回空集合使用`Collections.emptySet()`。
如果是`Dubbo`接口的返回值,空集合要使用`new HashSet<>()`。| +| HashMap | 在非多线程且**没有排序要求**的场景使用。
方法返回空集合使用`Collections.emptyMap()`。
如果是`Dubbo`接口的返回值,空集合要使用`new HashMap<>()`。| +| LinkedHashSet、LinkedHashMap | 在非多线程且**需要按照插入顺序遍历**的场景使用。| +| TreeSet、TreeMap | 在非多线程场景且**有排序要求**的场景使用。| +| ConcurrentSkipListSet | 在需要排序的场景且多线程安全的场景使用。| +| CopyOnWriteArraySet | 读多写少、无需排序、集合的规模较少,比如:监听器、回调注册表。 | +| ConcurrentHashMap | 在多线程场景且**无排序要求**的场景下使用。| +| ConcurrentSkipListMap | 在多线程场景且**有排序要求**的场景下使用。| +| ArrayBlockingQueue | 在多线程**需限制内存、容量固定、延迟可预测**的场景下使用 | +| LinkedBlockingQueue | 在多线程**需高吞吐、长度波动大、生产消费并发高**的场景下使用,**必须显式指定容量,防止OOM**。| + +## Lock的使用规范 + +- 单机环境下,使用`ReentrantLock`或者`StampedLock`。 +- 分布式环境下,如果`pom.xml`中引入了`Redisson`,则应该优先使用`RedissonClient`。 \ No newline at end of file diff --git a/java-spec/references/design-patten.md b/java-spec/references/design-patten.md new file mode 100644 index 0000000..b32caf4 --- /dev/null +++ b/java-spec/references/design-patten.md @@ -0,0 +1,224 @@ +# 设计模式应用实践 + +适当的应用设计模式,能显著提高代码的可读性、维护性。我们应该学习spring和mybatis框架中的实践,让业务代码变得更加健壮和易读。最常用的设计模式比如:工厂、策略、发布订阅、状态机等。 + +## 1、何时使用策略模式 + +- 当`if`或`switch`分支语句>=3个 +- 算法逻辑复杂 +- 运行时动态切换 +- 多种实现完成同一目标,只是规则不同 + +### 1.1、典型的业务场景 + +- 支付方式:微信、支付宝、银行卡、余额、积分抵扣 +- 优惠/促销计算:满减、折扣、秒杀价、会员价、优惠券 +- 多种登录方式:账号密码、短信验证码、微信授权、人脸识别 +- 数据导出:导出 Excel、CSV、PDF、JSON,格式不同但流程一致 +- 消息通知渠道:短信、邮件、站内信、钉钉、企业微信、Push +- 运费计算:按重量、按件数、包邮、偏远地区、顺丰/圆通不同计费 +- 风控/校验规则:不同用户等级走不同风控策略 +- 排序/压缩/加密算法:多种算法实现同一接口,运行时选择 +- 任务调度策略:立即执行、延迟执行、定时执行、重试策略 +- 反序列化/序列化:按类型选择不同解析器 + +### 1.2、不适合使用策略模式的场景 + +| 场景 | 原因 | +|------|------| +| 分支只有 2~3 个且稳定 | 引入类膨胀,得不偿失 | +| 分支逻辑只有一两行 | if-else 更直观 | +| 分支之间高度耦合、共享大量状态 | 拆出去反而更复杂 | +| 一次性、不会扩展的判断 | 过度设计 | + +### 1.3、Spring下的实践建议 + +- 策略接口加标识方法,如`getUserType()`,结合枚举更佳,便于工厂用`Map`索引。 +- 每个策略类用`@Component`修饰,由`Spring`注入到工厂。 +- 工厂用构造器注入`List`,根据标识方法初始化一个`Map`,通过标识方法的返回值去获取策略。 + +### 1.4、示例代码 + +```java +@RequiredArgsConstructor +public enum LoginType { + + PASSWORD("账号密码登录"), + + SMS("短信验证码登录"), + + EMAIL("邮箱验证码登录"); + + private final String description; + +} +``` + +```java +@Data +public class LoginRequest { + + private LoginType loginType; + + private Object param; + +} +``` + +```java +public interface IUserLoginService { + + LoginType getLoginType(); + + void doLogin(Object param); + +} +``` + +```java +@Component +public class PasswordLoginService implements IUserLoginService { + + @Override + public LoginType getLoginType() { + return LoginType.PASSWORD; + } + + @Override + public void doLogin(Object param) { + // 执行账号密码登录 + } + +} +``` + +```java +@Component +public class SmsLoginService implements IUserLoginService { + + @Override + public LoginType getLoginType() { + return LoginType.SMS; + } + + @Override + public void doLogin(Object param) { + // 执行短信验证码登录 + } + +} +``` + +```java +@Component +public class EmailLoginService implements IUserLoginService { + + @Override + public LoginType getLoginType() { + return LoginType.EMAIL; + } + + @Override + public void doLogin(Object param) { + // 执行邮箱验证码登录 + } + +} +``` + +```java +@Component +public class UserLoginFactory { + + private final EnumMap map; + + public UserLoginFactory(List userLoginServiceList) { + map = new EnumMap<>(LoginType.class); + userLoginServiceList.forEach(x -> map.put(x.getLoginType(), x)); + } + + public void doLogin(LoginRequest request) { + map.get(request.getLoginType()).doLogin(request.getParam()); + } + +} +``` + +## 2、何时使用发布定阅模式 + +- 一个动作触发多个后续操作,如用户注册后,需要发邮件、送积分、写日志,但这些逻辑不应阻塞主流程。 +- 发布方不关心谁处理、如何处理,核心业务只负责“通知”,具体响应逻辑由各订阅方自行定义。 +- 需要并行处理不同工作流,同一消息需要触发多个独立的处理链路。 +- 可容忍最终一致性,发布与处理之间存在延迟,不要求强实时响应。 +- 需要与异构系统/服务通信,不同语言、协议、平台的服务之间通过事件解耦。 +- 新增功能不应修改原有代码,每加一个后续操作,只需新增监听器,核心发布逻辑零改动 。 + +### 2.1、典型业务场景 + +- 用户注册 → 发邮件、发优惠券、初始化积分、写审计日志 +- 订单支付成功 → 扣库存、通知物流、更新用户等级、发送推送 +- 数据变更 → 同步到缓存、搜索引擎、数据仓库 +- 系统监控 → 关键业务事件触发告警 + +### 2.2、Spring下的实践建议 + +Spring 提供了内置的事件机制(`ApplicationEvent` + `ApplicationEventPublisher` + `@EventListener`),本质就是发布订阅模式,无需引入外部消息中间件即可实现轻量级解耦。如果在集群环境下需要跨服务通信,应结合消息队列(如RocketMQ、Kafka)使用。 + +### 2.3、示例代码 + +```java +@Data +@RequiredArgsConstructor +public class UserRegisteredEvent { + private final Long userId; + private final String username; + private final String email; +} +``` + +```java +@Service +@RequiredArgsConstructor +public class UserService { + + private final ApplicationEventPublisher eventPublisher; + + @Transactional(rollbackFor = Exception.class) + public void register(String username, String email) { + // 核心业务:保存用户 + Long userId = saveUser(username, email); + // 发布事件,不关心谁处理 + eventPublisher.publishEvent(new UserRegisteredEvent(userId, username, email)); + } + +} +``` + +```java +@Slf4j +@Component +public class UserEventListener { + + // 场景1:同步监听,强一致逻辑 + @EventListener + public void handleSync(UserRegisteredEvent event) { + log.info("记录注册日志: {}", event.getUsername()); + } + + // 场景2:异步监听,耗时操作不阻塞主线程 + // 需在启动类加 @EnableAsync + @Async + @EventListener + public void handleAsync(UserRegisteredEvent event) { + // 发邮件、调第三方,耗时 1s+ + sendWelcomeEmail(event.getEmail()); + } + + // 场景3:事务提交后监听,确保能查到刚写入的数据 + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handleAfterCommit(UserRegisteredEvent event) { + // 此时事务已提交,可以安全查询数据库 + initUserPoints(event.getUserId()); + } +} +``` \ No newline at end of file diff --git a/java-spec/references/directory.md b/java-spec/references/directory.md new file mode 100644 index 0000000..b3ce0ff --- /dev/null +++ b/java-spec/references/directory.md @@ -0,0 +1,113 @@ +# 通用的目录结构 + +```text +com/example/project/ +│ +├── controller/ # 控制器层(接收请求) +│ ├── UserController.java +│ └── ... +│ +├── service/ # 业务逻辑层 +│ ├── UserService.java # 接口 +│ ├── impl/ # 实现类 +│ │ └── UserServiceImpl.java +│ └── ... +│ +├── mapper/ # 数据访问层(MyBatis Mapper) +│ ├── ProductMapper.java +│ └── ... +│ +├── domain/ # 领域层(数据对象 + 业务词汇统一入口) +│ ├── entity/ # 实体类(与数据库表对应) +│ │ ├── Product.java +│ │ └── ... +│ │ +│ ├── dto/ # 数据传输对象(输入参数) +│ │ ├── ProductQueryDTO.java +│ │ └── ... +│ │ +│ ├── vo/ # 视图对象(输出结果) +│ │ ├── UserVO.java +│ │ ├── ProductVO.java +│ │ ├── PageVO.java # 分页结果封装 +│ │ └── ... +│ │ +│ ├── common/ # 通用对象 +│ │ ├── Result.java # 统一返回结果封装 +│ │ ├── PageResult.java # 分页返回封装 +│ │ ├── BaseEntity.java # 实体基类(id、创建时间等) +│ │ └── ... +│ │ +│ ├── enums/ # 枚举类 +│ │ ├── ResultCodeEnum.java # 统一返回码 +│ │ ├── OrderStatusEnum.java # 业务状态枚举 +│ │ └── ... +│ │ +│ └── constants/ # 常量类 +│ ├── StatusCode.java +│ ├── RedisKeyConstant.java +│ └── ... +│ +├── config/ # 配置类 +│ ├── DataSourceConfig.java +│ ├── WebMvcConfig.java # SpringMVC 配置(拦截器注册、跨域、消息转换器等) +│ ├── MyBatisConfig.java # MyBatis 配置 +│ ├── SwaggerConfig.java # 接口文档配置 +│ └── ... +│ +├── interceptor/ # 拦截器(HandlerInterceptor) +│ ├── LoginInterceptor.java # 登录校验拦截器 +│ ├── AuthInterceptor.java # 权限校验拦截器 +│ └── ... +│ +├── aop/ # 切面编程(Aspect) +│ ├── LogAspect.java # 操作日志切面 +│ ├── TransactionAspect.java # 事务切面(可选,通常用 @Transactional) +│ └── ... +│ +├── handler/ # 处理器(全局处理 / 自定义处理器) +│ ├── GlobalExceptionHandler.java# 全局异常处理(@RestControllerAdvice) +│ ├── MyMetaObjectHandler.java # MyBatis-Plus 字段自动填充 +│ └── ... +│ +├── event/ # 事件驱动(Spring Event) +│ ├── UserRegisterEvent.java # 事件定义 +│ ├── UserRegisterListener.java # 事件监听器 +│ └── ... +│ +├── filter/ # 过滤器(Servlet Filter) +│ ├── CorsFilter.java # 跨域过滤器 +│ ├── RequestLogFilter.java # 请求日志过滤器 +│ └── ... +│ +├── validator/ # 校验相关 +│ ├── group/ # 校验分组(如 Insert、Update) +│ │ ├── InsertGroup.java +│ │ └── UpdateGroup.java +│ ├── custom/ # 自定义校验注解 + 校验器 +│ │ ├── Phone.java # 自定义注解 +│ │ ├── PhoneValidator.java # 对应 ConstraintValidator 实现 +│ │ └── ... +│ └── ... +│ +├── annotation/ # 自定义注解 +│ ├── Log.java # 日志注解 +│ ├── RateLimit.java # 限流注解 +│ └── ... +│ +├── converter/ # 对象转换器 +│ ├── UserConverter.java +│ └── ... +│ +├── exception/ # 自定义异常 +│ ├── BusinessException.java # 业务异常 +│ ├── ErrorCode.java # 错误码接口 +│ └── ... +│ +├── utils/ # 工具类 +│ ├── DateUtil.java +│ ├── ResultUtil.java # 统一返回结果工具 +│ └── ... +│ +└── Application.java # 启动类 +``` \ No newline at end of file diff --git a/java-spec/references/nameing-convention.md b/java-spec/references/nameing-convention.md new file mode 100644 index 0000000..4f267e8 --- /dev/null +++ b/java-spec/references/nameing-convention.md @@ -0,0 +1,97 @@ +# 命名规范 + +良好的命名规范,能够大幅提升可读性、维护性,避开生僻单词!**言简意赅始终是最佳选择。** + +## Java源码的基础命名规范 + +| 类型 | 命名规范 | 示例 | +| --- | --- | --- | +| 局部变量 | 小驼峰 | `name`、`userName` | +| 静态常量 | 英文大写、单词与单词之间下划线连缀 | `ELEMENT_TYPE` | +| 方法名 | 使用动词、小驼峰 | `doLogin`、`execute` | +| 类名 | 使用名词或形容词、大驼峰 | `UserInfo` | +| 数据库 ORM 实体类 | 在类名的基础上增加 `Entity` 后缀 | `UserInfoEntity` | +| 数据传输对象(Data Transfer Object,简称 DTO) | 在类名的基础上增加 `DTO` 后缀 | `UserInfoDTO` | +| 视图对象(View Object,简称 VO) | 在类名的基础上增加 `VO` 后缀 | `UserInfoVO` | + +## 缩写命名示例 + +|命名|含义| +|---|---| +|etc|配置文件(etcetera)| +|usr|用户系统资源(user system resources)| +|opt|可选(optional)| +|tmp|临时(temporary)| +|boot|启动相关| +|sys|系统( system)| +|ctl|控制(control)| +|sync|同步(synchronize)| +|cfg|配置(config)| +|conf|配置文件(configuration)| +|do|执行| + +这些命名短小、可读,几十年里保持了一致性、可预测性、简洁性,让人看到名字就能大致猜到用途。 + +## Spring的命名 + +### 常见类名后缀 + +| 后缀 | 含义 | 示例 | +|---|---|---| +| `Controller` | 控制器 | `UserController` | +| `Service` | 服务 | `UserService` | +| `ServiceImpl` | 服务实现 | `UserServiceImpl` | +| `Repository` | 仓储/DAO | `UserRepository` | +| `Mapper` | MyBatis 映射器 | `UserMapper` | +| `DAO` | 数据访问对象 | `UserDAO` | +| `Config` | 配置类 | `WebConfig` | +| `Properties` | 配置属性 | `JwtProperties` | +| `Filter` | 过滤器 | `AuthFilter` | +| `Interceptor` | 拦截器 | `LoginInterceptor` | +| `Advice` | 增强/通知 | `GlobalExceptionAdvice` | +| `Aspect` | 切面 | `LogAspect` | +| `Listener` | 监听器 | `OrderListener` | +| `Handler` | 处理器 | `GlobalExceptionHandler` | +| `Factory` | 工厂 | `BeanFactory` | +| `Builder` | 构建器 | `RequestBuilder` | +| `Converter` | 转换器 | `UserConverter` | +| `Validator` | 校验器 | `UserValidator` | +| `Template` | 模板 | `JdbcTemplate` | +| `Client` | 客户端 | `FeignClient` | +| `DTO` | 数据传输对象 | `UserDTO` | +| `VO` | 值对象/视图对象 | `UserVO` | +| `Entity` | 实体 | `UserEntity` | +| `Request` | 请求对象 | `LoginRequest` | +| `Response` | 响应对象 | `LoginResponse` | +| `Exception` | 异常 | `BizException` | +| `Enum` | 枚举 | `OrderStatusEnum` | +| `Util` | 工具类 | `DateUtil` | +| `Constant` | 常量类 | `RedisConstant` | +| `Test` | 测试类 | `UserServiceTest` | + +### 常见方法命名 + +| 命名 | 含义 | +|---|---| +| `findById` | 按 ID 查询 | +| `findAll` | 查询全部 | +| `getUserById` | 获取用户 | +| `save` | 保存 | +| `saveAll` | 批量保存 | +| `update` | 更新 | +| `deleteById` | 按 ID 删除 | +| `existsById` | 判断是否存在 | +| `count` | 统计数量 | +| `createOrder` | 创建订单 | +| `cancelOrder` | 取消订单 | +| `handleException` | 处理异常 | +| `validateToken` | 校验令牌 | +| `loadUserByUsername` | 按用户名加载用户 | +| `findByUsername` | 按用户名查询 | +| `findByUsernameAndStatus` | 按用户名和状态查询 | +| `findByAgeGreaterThan` | 年龄大于 | +| `findByCreateTimeBetween` | 创建时间在区间内 | +| `findByUsernameContaining` | 用户名包含 | +| `findByStatusOrderByCreateTimeDesc` | 按状态查并倒序 | +| `deleteByUsername` | 按用户名删除 | +| `countByStatus` | 按状态统计 | \ No newline at end of file diff --git a/java-spec/references/open-source-project-doc.md b/java-spec/references/open-source-project-doc.md new file mode 100644 index 0000000..8e1a632 --- /dev/null +++ b/java-spec/references/open-source-project-doc.md @@ -0,0 +1,44 @@ +# 开源项目参考文档 + +## spring-framework + +- [6.x版本](https://docs.spring.io/spring-framework/reference/6.2/index.html) +- [7.x版本](https://docs.spring.io/spring-framework/reference/overview.html) +- [更早版本(例如5.3.9)](https://docs.spring.io/spring-framework/docs/5.3.9/reference/html/) + +## spring-boot + +- [版本>=3.4](https://docs.spring.io/spring-boot/index.html) +- [版本<=3.3](https://docs.spring.io/spring-boot/docs/{版本号}/reference/htmlsingle/) +- [更早版本(例如2.7.9)](https://docs.spring.io/spring-boot/docs/2.7.9/reference/htmlsingle/) + +## mybatis-plus + +- [使用配置](https://raw.githubusercontent.com/baomidou/mybatis-plus-doc/refs/heads/master/src/content/docs/reference/index.mdx) +- [注解的使用方法](https://raw.githubusercontent.com/baomidou/mybatis-plus-doc/refs/heads/master/src/content/docs/reference/annotation.mdx) +- [常见问题](https://raw.githubusercontent.com/baomidou/mybatis-plus-doc/refs/heads/master/src/content/docs/reference/question.mdx) + +## hutool + +https://github.com/chinabugotech/hutool-site/blob/master/docs/SUMMARY.md + +## spring-ai + +- [Index](https://docs.spring.io/spring-ai/reference/index.html) +- [Chat Model API](https://docs.spring.io/spring-ai/reference/api/chatmodel.html) +- [Embeddings Model API](https://docs.spring.io/spring-ai/reference/api/embeddings.html) +- [OpenAI Chat](https://docs.spring.io/spring-ai/reference/api/chat/openai-chat.html) +- [Image Model API](https://docs.spring.io/spring-ai/reference/api/imageclient.html) +- [Audio Models](https://docs.spring.io/spring-ai/reference/api/audio.html) +- [Chat Memory](https://docs.spring.io/spring-ai/reference/api/chat-memory.html) +- [Tool Calling](https://docs.spring.io/spring-ai/reference/api/tools.html) +- [Model Context Protocol](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html) +- [Retrieval Augmented Generation](https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html) +- [Vector Databases](https://docs.spring.io/spring-ai/reference/api/vectordbs.html) +- [Observability](https://docs.spring.io/spring-ai/reference/observability/index.html) +- [Model Evaluation Testing](https://docs.spring.io/spring-ai/reference/api/testing.html) +- [Upgrade Notes](https://docs.spring.io/spring-ai/reference/upgrade-notes.html) + +## spring-ai-agent-utils + +https://github.com/spring-ai-community/spring-ai-agent-utils/blob/main/README.md \ No newline at end of file diff --git a/java-spec/references/orm.md b/java-spec/references/orm.md new file mode 100644 index 0000000..96f5d1b --- /dev/null +++ b/java-spec/references/orm.md @@ -0,0 +1,47 @@ +# 实体类与数据库表字段的映射规则 + +仅适用于Mybatis-Plus框架。 + +- 数据库表结构字段名称、实体类字段名称,采用驼峰与下划线映射的方式。 +- `@TableField`注解中`typeHandler`等于`JacksonTypeHandler.class`时,表结构字段默认JSON类型,在spring-boot版本>=4.0应换成`Jackson3TypeHandler`。 +- 实体类字段包含`@NotNull`注解,如果字段被`@Schema`注解修饰,需要结合`defaultValue`属性判断是否需要设置默认值。 +- 主键列是否采用MySQL自增序列,取决于实体类被`@TableId`注解修饰的字段,要看其`type`属性,如果是`IdType.AUTO`则使用自增,否则不需要。 +- 如果字段被`@Schema`注解修饰,且指定了`maxLength`属性,按如下原则设置字段类型: + - maxLength<=2000,使用VARCHAR + - maxLength>2000且<10000,使用TEXT + - maxLength>10000,使用LONGTEXT + +## MySQL + +|实体类字段类型|MySQL字段类型| +| --- | --- | +| String | VARCHAT(255) | +| Long | BIGINT | +| Integer | INT | +| LocalDate | DATE | +| LocalDateTime | DATETIME | +| BigDecimal | DECIMAL(14, 2) | + +## SQLite + +| 实体类属性类型 | SQLite字段类型 | 说明 | +|---|---|---| +| `String` | `TEXT` |-| +| `Long` | `INTEGER` |-| +| `Integer` | `INTEGER` |-| +| `LocalDate` | `TEXT` | 以`yyyy-MM-dd`格式字符串存储 | +| `LocalDateTime` | `TEXT` | 以`yyyy-MM-dd HH:mm:ss`格式字符串存储 | +| `@TableField(typeHandler = JacksonTypeHandler.class)` | `TEXT` | 配合`JacksonTypeHandler`自动序列化/反序列化 | +| `BigDecimal` | `TEXT` | 用`TEXT`存储精确数值,避免浮点精度丢失 | + +## PostgreSQL + +| 实体类属性类型 | PostgreSQL字段类型 | 说明 | +|---|---|---| +| `String` | `TEXT` | - | +| `Long` | `BIGINT` | - | +| `Integer` | `INTEGER` | - | +| `LocalDate` | `DATE` | 直接使用原生日期类型存储 | +| `LocalDateTime` | `TIMESTAMP` | 直接使用原生时间戳类型存储 | +| `@TableField(typeHandler = JacksonTypeHandler.class)` | `TEXT` / `JSONB` | 配合`JacksonTypeHandler`自动序列化/反序列化;`JSONB`性能更优且支持索引 | +| `BigDecimal` | `NUMERIC` | 用`NUMERIC`存储精确数值,避免浮点精度丢失 | \ No newline at end of file