This commit is contained in:
8ga committed 2026-09-26 16:09:11 +08:00
1 parent 4a1756edb4
commit 6798a567a2
12 files changed
+827

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
# 代码编写规范
## Java的新特性
目前主流的JDK选型有4个版本:8、17、21、25。通过`pom.xml`中的`<java.version>`标签可以定位当前项目的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 | 在非多线程场景使用。</br>方法返回空集合使用`Collections.emptyList()`。</br>如果是`Dubbo`接口的返回值,空集合要使用`new ArrayList<>()`。|
| HashSet | 在非多线程且**没有排序要求**的场景使用。</br>方法返回空集合使用`Collections.emptySet()`。</br>如果是`Dubbo`接口的返回值,空集合要使用`new HashSet<>()`。|
| HashMap | 在非多线程且**没有排序要求**的场景使用。</br>方法返回空集合使用`Collections.emptyMap()`。</br>如果是`Dubbo`接口的返回值,空集合要使用`new HashMap<>()`。|
| LinkedHashSet、LinkedHashMap | 在非多线程且**需要按照插入顺序遍历**的场景使用。|
| TreeSet、TreeMap | 在非多线程场景且**有排序要求**的场景使用。|
| ConcurrentSkipListSet | 在需要排序的场景且多线程安全的场景使用。|
| CopyOnWriteArraySet | 读多写少、无需排序、集合的规模较少,比如:监听器、回调注册表。 |
| ConcurrentHashMap | 在多线程场景且**无排序要求**的场景下使用。|
| ConcurrentSkipListMap | 在多线程场景且**有排序要求**的场景下使用。|
| ArrayBlockingQueue | 在多线程**需限制内存、容量固定、延迟可预测**的场景下使用 |
| LinkedBlockingQueue | 在多线程**需高吞吐、长度波动大、生产消费并发高**的场景下使用,**必须显式指定容量,防止OOM**。|
## Lock的使用规范
- 单机环境下,使用`ReentrantLock`或者`StampedLock`。
- 分布式环境下,如果`pom.xml`中引入了`Redisson`,则应该优先使用`RedissonClient`。
+224
View File
@@ -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<Strategy>`,根据标识方法初始化一个`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<LoginType, IUserLoginService> map;
public UserLoginFactory(List<IUserLoginService> 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());
}
}
```
+113
View File
@@ -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 # 启动类
```
@@ -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` | 按状态统计 |
@@ -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
+47
View File
@@ -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`存储精确数值,避免浮点精度丢失 |