type
status
date
slug
summary
tags
category
icon
password
catalog
sort

1. 目的与范围

1.1 目的

  • 统一团队对代码元素空状态(参数、返回值、字段等)的声明方式,结合编译期与运行期校验消除NPE
  • 规范主流框架注解(Jakarta Validation、MyBatis、Spring Web)的使用方式,提升代码一致性
  • 明确各层代码契约(如接口参数校验规则、DAO层返回值约定),减少协作沟通成本
  • 通过注解强化代码可读性与可维护性,降低后期迭代风险

1.2 适用范围

  • 所有基于Java开发的项目(JDK 11+),涵盖微服务、单体应用等架构
  • 涉及框架:Spring Boot 2.7+(含Spring Web)、MyBatis 3.5+、Jakarta Validation 3.0+
  • 所有开发人员、代码评审人员、测试人员及架构设计人员
  • 强制要求:新增代码100%遵守;过渡要求:存量代码6个月内完成改造(核心模块优先)

1.3 参考标准

  • 《Effective Java》第3版:第54条(返回空集合而非null)、第63条(慎用注解)

2. 基础注解与框架注解概述

2.1 核心注解分类

注解类型
作用域
核心框架/库
核心价值
编译期空安全注解
参数、返回值、字段
JetBrains Annotations
编译期识别NPE风险(如@NotNull/@Nullable
运行期校验注解
DTO字段、接口参数
Jakarta Validation
运行期校验参数合法性(如@NotBlank/@Email
数据访问注解
DAO方法、参数、结果映射
MyBatis
规范SQL映射与参数传递(如@Param/@Result
Web请求注解
控制器方法、参数
Spring Web
定义HTTP请求映射与参数绑定(如@GetMapping/@RequestBody

2.2 核心注解冲突说明

  • JetBrains @NotNull vs Jakarta @NotNull:前者是编译期提示(无运行时逻辑),后者是运行期校验(触发ConstraintViolationException),需按分层场景区分使用(见第5章分层规范)。
  • 框架注解优先级:同一元素若需多框架注解,按“功能分层”叠加(如@PathVariable+@NotBlank,前者绑定请求参数,后者校验非空)。

3. JetBrains Annotations 规范(编译期空安全)

3.1 基础空安全注解

3.1.1 @NotNull

  • 定义:标记元素(参数/返回值/字段)不允许为null,IDE编译期校验。
  • 适用场景
    • 业务核心参数(如用户ID、订单号):public void pay(@NotNull String orderId)
    • 必须初始化的字段(如领域模型主键):@NotNull private String id;
    • 必然有返回值的方法(如查询已确认存在的数据):@NotNull User getAdminUser()
  • 强制要求
    • 被标记的参数需在方法内补充运行期校验(推荐Objects.requireNonNull()),示例:
      • 禁止对@NotNull元素做冗余null判断(如if (param == null),IDE会提示警告)。

    3.1.2 @Nullable

    • 定义:标记元素允许为null,提示调用方必须处理null场景,IDE编译期校验。
    • 适用场景
      • 查询操作返回值(数据可能不存在):@Nullable User findByPhone(String phone)
      • 可选参数(如分页查询的“排序字段”):List<User> list(@Nullable String sortField)
      • 延迟加载的字段(如User.extraInfo可能未初始化):@Nullable String extraInfo;
    • 强制要求
      • 调用@Nullable方法后必须显式判空,支持两种方式:
        • 禁止直接调用@Nullable对象的方法(如nullableUser.getName()必须先判空)。

      3.1.3 包级空安全注解

      注解
      作用域
      语义
      适用场景
      @NotNullApi
      package-info.java
      包内未显式标注的元素默认不可为null
      服务层(Service)、领域模型(Domain)
      @NullableApi
      package-info.java
      包内未显式标注的元素默认可为null
      数据访问层(DAO)、外部接口适配层
      • 使用方式:在包根目录创建package-info.java,示例:

        3.2 方法契约与边界注解

        3.2.1 @Contract

        • 定义:描述方法“参数→返回值”的逻辑关系,辅助IDE精准分析代码流。
        • 适用场景:工具类方法、有明确输入输出映射的函数(如StringUtils)。
        • 语法规则
          • value:分号分隔的“条件→结果”表达式(如"null->false;!null->true"
          • pure = true:标记纯函数(无副作用,输入相同则输出相同)
        • 示例

          3.2.2 测试边界注解

          注解
          作用
          适用场景
          约束要求
          @TestOnly
          标记元素仅允许在测试代码中使用
          测试Mock方法(如mockOrder()
          生产代码调用必须在CR中驳回
          @VisibleForTesting
          标记本应私有但为测试改为非私有的方法
          需要单测覆盖的内部逻辑(如校验方法)
          访问修饰符需最小化(如private→protected
          • 示例:

            3.3 集合与类型注解

            3.3.1 集合元素空状态

            • 必须明确集合本身及元素的空状态,使用嵌套注解:

              3.3.2 不可修改集合

              • 返回不可修改的集合时,必须添加@Unmodifiable

                4. Jakarta Validation 注解规范(运行期校验)

                4.1 核心校验注解

                注解
                作用
                适用场景
                与JetBrains注解的区别
                @NotNull
                运行期校验元素非null(允许空字符串"")
                数字类型字段(如Integer age
                JetBrains @NotNull是编译期提示
                @NotBlank
                运行期校验字符串非null且非空白(去空格后长度>0)
                用户名、密码等字符串字段
                -
                @NotEmpty
                运行期校验集合非null且元素数量>0
                订单明细列表(List<OrderItem>
                -
                @Email
                运行期校验字符串符合邮箱格式
                用户邮箱字段
                可配合regexp自定义格式
                @Pattern(regexp)
                运行期校验字符串匹配正则表达式
                手机号(regexp = "^1[3-9]\\\\d{9}$"
                -
                @Min/@Max
                运行期校验数字在指定范围内
                年龄(@Min(18)
                -

                4.2 使用规范

                4.2.1 适用层级

                • 仅用于DTO层(如UserDTOOrderQuery),作为外部输入的第一道防线(如HTTP请求参数、消息队列消息体)。
                • 禁止在领域模型(User)、服务层方法参数直接使用(领域模型应通过业务逻辑校验,而非注解)。

                4.2.2 与Spring Web结合

                • 通过@Valid@Validated触发校验,示例:

                  4.2.3 自定义校验注解

                  • 复杂业务规则需定义自定义注解(如“密码强度校验”),示例:

                    5. MyBatis 注解规范(数据访问层)

                    5.1 核心注解分类

                    注解类型
                    核心注解
                    作用
                    SQL映射注解
                    @Select/@Insert
                    定义SQL语句(替代XML映射)
                    参数绑定注解
                    @Param
                    声明DAO方法参数名,便于SQL中引用
                    结果映射注解
                    @Result/@Results
                    定义查询结果与实体字段的映射关系
                    动态SQL注解
                    @If/@Where
                    构建动态SQL(需配合MyBatis-3.5+)

                    5.2 使用规范

                    5.2.1 参数绑定(@Param

                    • 方法参数≥2时必须用@Param指定名称,且名称需与SQL中的#{name}一致:
                      • 禁止使用@Param传递复合对象(如@Param("user") User user),建议直接用对象属性(如#{user.id})。

                      5.2.2 结果映射(@Result/@Results

                      • 字段名与列名不一致时,必须用@Result显式映射,且需指定property(实体字段)和column(表列名):
                        • 复用性高的映射关系需定义为@ResultMap,避免重复代码:

                          5.2.3 与JetBrains注解结合

                          • DAO方法返回值:查询操作(select)用@Nullable(数据可能不存在),新增/更新/删除用@NotNull(返回影响行数或主键):

                            6. Spring Web 注解规范

                            6.1 核心注解分类

                            注解类型
                            核心注解
                            作用
                            请求映射注解
                            @RequestMapping/@GetMapping
                            定义HTTP请求路径与方法
                            参数绑定注解
                            @PathVariable/@RequestParam
                            绑定URL路径参数/请求参数
                            请求体注解
                            @RequestBody
                            绑定HTTP请求体(JSON/XML)
                            响应注解
                            @ResponseBody/@ResponseStatus
                            声明响应体格式/HTTP状态码

                            6.2 使用规范

                            6.2.1 请求映射注解

                            • 优先使用HTTP方法级注解(@GetMapping/@PostMapping),避免通用@RequestMapping
                              • 路径命名规范:使用小写字母+连字符(),如/user-orders而非/userOrders

                              6.2.2 参数绑定与校验

                              • @PathVariable:路径参数必须用@NotNull(Jakarta)校验非空,且名称需与URL占位符一致:
                                • @RequestParam:查询参数需指定required属性(默认true),可选参数需显式设为required = false
                                  • @RequestBody:配合@Valid触发Jakarta Validation校验,且DTO需用@Nullable标记可能为null的字段:

                                    6.2.3 响应注解

                                    • 控制器方法返回值优先使用ResponseEntity<T>,明确HTTP状态码与响应体:
                                      • 禁止使用@ResponseBody标注类(如@RestController已包含该功能),避免冗余。

                                      7. 分层注解规范

                                      7.1 控制层(Controller)

                                      层职责
                                      核心注解组合
                                      示例代码
                                      请求接收与响应
                                      Spring Web注解 + Jakarta Validation注解
                                      java @GetMapping("/orders/{orderId}") public ResponseEntity<OrderVO> getOrder( @PathVariable("orderId") @NotNull String orderId, // 路径参数+非空校验 @RequestParam(required = false) @Nullable String viewType // 可选查询参数 ) { ... }
                                      请求体校验
                                      @RequestBody + @Valid + DTO(Jakarta注解)
                                      java @PostMapping("/orders") public ResponseEntity<OrderVO> createOrder( @Valid @RequestBody @NotNull OrderDTO dto // 请求体+校验 ) { ... }

                                      7.2 服务层(Service)

                                      层职责
                                      核心注解组合
                                      示例代码
                                      业务逻辑处理
                                      JetBrains注解(编译期空安全)
                                      java @Service public class OrderService { // 订单ID非空,返回值非空(业务约定) @NotNull public Order getOrder(@NotNull String orderId) { Objects.requireNonNull(orderId, "订单ID不能为空"); // 运行期兜底 return orderDao.selectById(orderId); } }
                                      方法契约声明
                                      @Contract(工具类方法)
                                      java public class OrderUtils { @Contract("null -> false") public static boolean isPaid(Order order) { return order != null && order.getStatus() == 2; } }

                                      7.3 数据访问层(DAO)

                                      层职责
                                      核心注解组合
                                      示例代码
                                      数据查询
                                      MyBatis注解 + JetBrains @Nullable
                                      java public interface OrderDao { @Select("SELECT * FROM t_order WHERE id = #{id}") @Nullable Order selectById(@NotNull String id); // 查询可能无结果 }
                                      数据写入
                                      MyBatis注解 + JetBrains @NotNull
                                      java public interface OrderDao { @Insert("INSERT INTO t_order(...) VALUES(...)") @NotNull String insert(@NotNull Order order); // 写入必返回主键 }

                                      7.4 DTO层(数据传输对象)

                                      层职责
                                      核心注解组合
                                      示例代码
                                      输入参数校验
                                      Jakarta Validation注解(运行期校验)
                                      java public class UserDTO { @NotBlank(message = "用户名不能为空") private String username; @Email(message = "邮箱格式错误") private String email; @Min(value = 18, message = "年龄不能小于18") private Integer age; }
                                      空状态声明
                                      JetBrains @Nullable(可选字段)
                                      java public class UserDTO { @NotBlank private String username; @Nullable // 可选字段 private String avatar; }

                                      8. 代码评审检查清单

                                      检查维度
                                      检查项
                                      权重
                                      空安全注解
                                      1. 所有public方法参数/返回值均有@NotNull/@Nullable(JetBrains)<br>2. @Nullable返回值调用处已判空
                                      Jakarta Validation
                                      1. DTO字段已用@NotBlank/@Email等标注校验规则<br>2. 控制器方法@RequestBody已加@Valid
                                      MyBatis注解
                                      1. 多参数方法已用@Param命名<br>2. DAO查询方法返回值已加@Nullable
                                      Spring Web注解
                                      1. 请求路径用小写+连字符,HTTP方法注解明确<br>2. @PathVariable与URL占位符名称一致
                                      注解冲突处理
                                      1. 未混淆JetBrains @NotNull与Jakarta @NotNull<br>2. 同一元素多注解功能不重叠
                                      集合与工具类
                                      1. 集合元素空状态已用嵌套注解标注(如List<@Nullable String>)<br>2. 工具类方法已加@Contract

                                      9. 自动化校验与工具支持

                                      9.1 IDE配置

                                      • JetBrains注解检查:IDEA开启Settings > Editor > Inspections > Java > Probable bugs > Nullability problems(全选)。
                                      • 框架注解提示:安装Spring BootMyBatis插件,启用注解自动补全(Ctrl+Space)。

                                      9.2 CI/CD集成

                                      • Jakarta Validation校验:通过spring-boot-starter-validation依赖,在测试阶段执行@Valid校验用例。
                                      • MyBatis注解检查:使用mybatis-spring-boot-starter-test,通过@MybatisTest验证DAO注解与SQL匹配性。
                                      • 静态扫描:SonarQube启用规则:
                                        • S2259:避免对@Nullable值进行非null比较
                                        • S3655:避免对@NotNull参数进行null检查
                                        • 自定义规则:检查@Param命名与SQL参数一致性

                                      9.3 冲突处理工具

                                      • 注解冲突检测:使用maven-enforcer-plugin禁止在领域模型同时使用Jakarta @NotNull与JetBrains @NotNull
                                      • 冗余注解清理:IDEA插件Annotation Cleaner自动移除无效注解(如@ResponseBody@RestController类中)。

                                      附录:注解速查表(按场景)

                                      场景
                                      推荐注解组合
                                      DTO非空字符串字段
                                      @NotBlank(Jakarta)
                                      控制器路径参数
                                      @PathVariable + @NotNull(Jakarta)
                                      DAO查询方法返回值
                                      MyBatis @Select + JetBrains @Nullable
                                      服务层核心参数
                                      JetBrains @NotNull + Objects.requireNonNull
                                      工具类纯函数
                                      JetBrains @Contract(pure = true)
                                      JetBrains Annotations:从入门到落地,彻底告别 NullPointerException从规范到架构:一篇读懂 Java 工程建模、分层、命名与演进之路
                                      Loading...
                                      Honesty
                                      Honesty
                                      花には咲く日があり、人には少年はいない
                                      统计
                                      文章数:
                                      83