外观
使用与配置
完成快速开始后,先确定应用的响应和错误协议,再决定是否覆盖 Base 的默认 Bean。Base 没有独立的 own.base.* 配置属性类,主要通过自动配置和 Bean 替换提供扩展。
返回统一模型
Controller 主动返回 ResultModel<T>,通常使用 R.success(data)。Base 没有将任意 Controller 返回值自动包装成该模型的全局响应增强;直接返回字符串、实体或 ResponseEntity 时仍由对应 MVC 转换器处理。
| 字段 | 含义 |
|---|---|
code | 字符串业务码,普通成功为 00000 |
message | 展示消息,普通成功默认为“请求成功” |
t | 创建响应或重新设置响应码时的本地时间,格式为 yyyy-MM-dd HH:mm:ss |
success | 可被设置的布尔字段,不保证从 code 推导 |
traceId | 构造响应时读取 MDC 的 traceId;没有上下文时可为空 |
data | ResultModel<T> 的业务数据;无数据成功响应不赋值 |
常用构造方式如下,R、ResultModel、UpdateModel 均位于 com.own.component.base.model:
| 用法 | 当前行为 |
|---|---|
| R.success(data) | 新建成功响应,携带业务数据 |
| R.success() | 新建成功响应,data 未赋值;不等于返回 true |
| R.success(false) | 操作响应成功,业务数据是布尔 false |
| R.success(data, message) | 成功响应并自定义展示消息 |
| R.success(responseMessage) / R.success(responseMessage, data) | 使用 Constant 的 ResponseMessage 指定码和消息,但不会据此自动将 success 改为 false |
| R.fail(message) | 直接抛出 BusinessSimpleException,不是返回失败模型;默认业务码为 E0001 |
| R.success(updateModel) | 由 UpdateModel.flag 选择成功或失败码,仍需留意下述布尔语义 |
当前成功标识的限制
BaseResponseModel.responseCode(...) 和 content(...) 都会把 success 设为 true,与传入的码无关。之后单独 setCode(...) 也不会联动修改成功标识。若自行构造失败 ResultModel,应在设置完码和消息后显式 setSuccess(false),并按应用确定的业务码协议处理。
ExceptionMessageModel 在构造时显式设置 success=false,另含 ex,不含 data。普通失败响应和校验失败响应的形状因此不同。
ResultModel(Boolean flag, T data) 当前是 flag=true 使用失败码 S0001,flag=false 使用成功码并赋值数据;不能把它当作常规“true 表示成功”的构造器。ResultModel(UpdateModel<T>) 则按 flag=true 成功、false 失败处理,失败码为 00002 或带自定义消息时的 00001,但 success 仍可能为 true,flag=null 会触发拆箱异常。
R.TRUE、R.FALSE 是共享的可变响应实例,时间和 TraceId 在类初始化时确定。每次请求使用 R.success(true) / R.success(false) 新建响应,可避免复用旧时间、旧 TraceId 或被其他调用修改的字段。
解析已有响应
R.resolveForJson(json, DataType.class) 使用 Fastjson2 提取 data,也可传 Function<JSONObject,T> 自定义提取方式。它再读取外层字段,并要求 t 能按 yyyy-MM-dd HH:mm:ss 解析;缺失时间、时间格式不同或 JSON 非法时会抛出异常,不是容错式远端响应适配器。
异常转换
默认处理器均为全局 @RestControllerAdvice,不限定业务包;校验处理器顺序为 1,业务处理器顺序为 99。
| 异常 | 默认响应 |
|---|---|
| BusinessSimpleException | 保留业务码和消息,返回 ExceptionMessageModel,该处理方法不记录错误堆栈 |
| BusinessException | 保留业务码和消息,返回异常模型,并记录错误堆栈 |
NullPointerException | code=null_pointer,消息“数据准备中,请重新进入页面” |
SQLException | code=sql_error,消息“数据库处理异常” |
HttpMessageNotReadableException | code=parameter_not_match,消息“参数不匹配” |
其他 RuntimeException、Exception、Throwable | code=00002,消息“操作失败,请刷新页面” |
MethodArgumentNotValidException、BindException | ResultModel<List<String>>,code=00003,data 为所有默认校验消息,message 用逗号拼接;当前 success=true |
业务和兜底异常响应设置 success=false。响应消息可能被应用的 Constant 响应消息缓存覆盖,表中列的是源码默认值。Controller 外部 Filter 的异常不保证进入这些 MVC Advice。
处理器没有设置 @ResponseStatus 或 ResponseEntity 状态,也不主动修改 HttpServletResponse;普通已处理异常通常仍返回 HTTP 200。需要 HTTP 4xx/5xx 协议时,由应用实现相应异常映射,不能把模型中的业务码当作 HTTP 状态码。
校验错误使用的 00003 与 ResponseMessageEnum.Request.OPERATION_SUCCESS 的码相同,并且保留 success=true。因此不能在全站把 00003 一律解释为成功;应用应覆盖校验处理器、分配明确的校验错误码并同步客户端协议。这也影响依赖成功码识别的系统日志响应提取。
标记维护中的方法
在由 Spring AOP 代理调用的方法上添加 com.own.constant.aop.DevelopAop:
java
@DevelopAop
@GetMapping("/maintenance")
public ResultModel<String> maintenance() {
return R.success("ready");
}上例作为 Controller 方法使用,DevelopAop、GetMapping、ResultModel、R 需按对应包导入。切面在执行前抛出 S0004(“api接口维护中”),方法体不会执行。它没有 profile 判断,所有环境都会阻断,不是仅开发环境启用的标记;同类内部调用等未经过代理的调用不保证拦截。
MVC JSON 约定
Servlet 应用默认注册 CustomLongMvcConfig,通过 Spring MVC 的 JSON 转换器使用 Jackson 3(tools.jackson.*)的独立 JsonMapper。
| 类型或行为 | 默认规则 |
|---|---|
包装类型 Long、BigInteger | 输出十进制字符串 |
基本类型 long | 没有显式注册同样的字符串序列化器,不应将包装类型规则直接推广到它 |
LocalDateTime | 读写 yyyy-MM-dd HH:mm:ss |
LocalDate | 读写 yyyy-MM-dd |
LocalTime | 读写 HH:mm:ss |
ZonedDateTime | 输出 yyyy-MM-dd HH:mm:ss.SSS,不带时区或偏移;未配置对应自定义反序列化器 |
| 请求中的未知属性 | 忽略,不因未知字段报错 |
| 日期时间戳输出 | 关闭日期时间按时间戳写出 |
格式不等于时区转换:LocalDateTime 没有时区,带时区时间输出也未保留偏移,组件不统一把所有时间转换到 UTC 或 GMT+8。跨系统交换绝对时刻时应由应用选择保留偏移的协议。
MVC 与工具 JSON 的区别
| 入口 | 实现与生效位置 |
|---|---|
| 默认 MVC JSON 转换器 | Jackson 3,应用 HTTP 请求与响应;由 CustomLongMvcConfig 单独创建 Builder |
| new JacksonObjectMapper() | Jackson 2(com.fasterxml.jackson.*),带 JDK8/Java Time 模块及 Long/BigInteger 字符串规则,不自动注册为 Spring Bean |
| JacksonJsonMapperUtil.toJsonString(object) | 复用私有 Jackson 2 单例,只提供对象转字符串;失败包装为 RuntimeException("转换json字符串失败", cause) |
| R.resolveForJson(...) | Fastjson2 解析统一响应,与上述两个 Mapper 无关 |
工具 Mapper 对日期使用相同的主要格式,但不能推断它与 MVC 的全部行为相同。MVC Mapper 不通过注入应用的 ObjectMapper 构造,不能假定新增 Jackson 2 Bean、修改 spring.jackson.* 或某个全局定制器就一定改变这个转换器。
需要改变 HTTP JSON 规则时,可提供 CustomLongMvcConfig 类型的自定义 Bean,使默认 Bean 退让,并在其 configureMessageConverters(...) 中配置应用需要的转换器。替换时一并验证 Long、日期、空值、未知字段及现有模型注解,不要只检查单个字段。
默认装配与扩展位置
| Bean | 注册条件与扩展方式 |
|---|---|
| SessionUserProviderHolder | 缺少同类型 Bean 时注册;通过 ObjectProvider<SessionUserProvider> 取得身份来源,Base 不提供默认 Provider |
| DevelopAspect | 缺少同类型 Bean 时注册 |
| BusinessExceptionHandler、ValidatedExceptionHandler | 分别在缺少同类型 Bean 时注册;可用同类型子类 Bean 覆盖,也可按 Advice 顺序实现应用的独立处理器 |
| CustomLongMvcConfig | Servlet Web 应用且缺少同类型 Bean |
| TraceIdInterceptorConfig | Servlet Web 应用且缺少同类型 Bean;具体拦截器在此配置内创建 |
customAsyncExecutor | 按 Bean 名检查缺失,默认类型为 ThreadPoolTaskExecutor |
| ExecutorUtil | 缺少同类型 Bean 时注册,按 customAsyncExecutor 名称注入 ThreadPoolTaskExecutor |
Base 自动配置本身不限定 Web 应用,只有两个 MVC 配置 Bean 有 Servlet 条件。只使用工具类不代表 Spring 应用只会装配工具;默认异常处理和执行器也会注册。
完整排除 Base 自动配置可使用 Spring Boot 的排除设置,随后由应用承担全部所需装配:
yaml
spring:
autoconfigure:
exclude:
- com.own.component.base.config.BaseAutoConfiguration这不会移除 Base JAR 的静态工具和模型。通常只需按上表覆盖单项 Bean;请求上下文与线程池覆盖示例见上下文与异步任务。