都說了布爾類型的變量不要加 is 前綴,非要加,這不是坑我了嘛
開心一刻
今天心情不好,給哥們發語音
我:哥們,晚上出來喝酒聊天吧
哥們:咋啦,心情不好?
我:嗯,剛剛在公交車上看見前女友了
哥們:然后呢?
我:給她讓座時,發現她懷孕了...
哥們:所以難受了?
我:不是她懷孕讓我難受,是她懷孕還坐公交車讓我難受
哥們:不是,她跟著你就不用坐公交車了?不還是也要坐,有區別嗎?
我默默的掛斷了語音,心情更難受了

Java開發手冊
作為一個 javaer,我們肯定看過 Alibaba 的 Java開發手冊,作為國內Java開發領域的標桿性編碼規范,我們或多或少借鑒了其中的一些規范,其中有一點

我印象特別深,也一直在奉行,自己還從未試過用 is 作為布爾類型變量的前綴,不知道會有什么坑;正好前段時間同事這么用了,很不幸,他挖坑,我踩坑,阿西吧!

is前綴的布爾變量有坑
為了復現問題,我先簡單搞個 demo;調用很簡單,服務 workflow 通過 openfeign 調用 offline-sync,代碼結構如下

qsl-data-govern-common:整個項目的公共模塊
qsl-offline-sync:離線同步
- qsl-offline-sync-api:向外提供
openfeign接口- qsl-offline-sync-common:離線同步公共模塊
- qsl-offline-sync-server:離線同步服務
qsl-workflow:工作流
- qsl-workflow-api:向外提供
openfeign接口,暫時空實現- qsl-workflow-common:工作流公共模塊
- qsl-workflow-server:工作流服務
完整代碼:qsl-data-govern
qsl-offline-sync-server 提供刪除接口
/**
* @author 青石路
*/
@RestController
@RequestMapping("/task")
public class SyncTaskController {
private static final Logger LOG = LoggerFactory.getLogger(SyncTaskController.class);
@PostMapping("/delete")
public ResultEntity<String> delete(@RequestBody SyncTaskDTO syncTask) {
// TODO 刪除處理
LOG.info("刪除任務[taskId={}]", syncTask.getTaskId());
return ResultEntity.success("刪除成功");
}
}
qsl-offline-sync-api 對外提供 openfeign 接口
/**
* @author 青石路
*/
@FeignClient(name = "data-govern-offline-sync", contextId = "dataGovernOfflineSync", url = "${offline.sync.server.url}")
public interface OfflineSyncApi {
@PostMapping(value = "/task/delete")
ResultEntity<String> deleteTask(@RequestBody SyncTaskDTO syncTaskDTO);
}
qsl-workflow-server 調用 openfeign 接口
/**
* @author 青石路
*/
@RestController
@RequestMapping("/definition")
public class WorkflowController {
private static final Logger LOG = LoggerFactory.getLogger(WorkflowController.class);
@Resource
private OfflineSyncApi offlineSyncApi;
@PostMapping("/delete")
public ResultEntity<String> delete(@RequestBody WorkflowDTO workflow) {
LOG.info("刪除工作流[workflowId={}]", workflow.getWorkflowId());
// 1.查詢工作流節點,查到離線同步節點(taskId = 1)
// 2.刪除工作流節點,刪除離線同步節點
ResultEntity<String> syncDeleteResult = offlineSyncApi.deleteTask(new SyncTaskDTO(1L));
if (syncDeleteResult.getCode() != 200) {
LOG.error("刪除離線同步任務[taskId={}]失敗:{}", 1, syncDeleteResult.getMessage());
ResultEntity.fail(syncDeleteResult.getMessage());
}
return ResultEntity.success("刪除成功");
}
}
邏輯是不是很簡單?我們啟動兩個服務,然后發起 http 請求
POST http://localhost:8081/data-govern/workflow/definition/delete
Content-Type: application/json{
"workflowId": 99
}
此時 qsl-offline-sync-server 日志輸出如下
2025-06-30 14:53:06.165|INFO|http-nio-8080-exec-4|25|c.q.s.s.controller.SyncTaskController :刪除任務[taskId=1]
至此,一切都很正常,第一版也是這么對接的;后面 offline-sync 進行調整,刪除接口增加了一個參數:isClearData
public class SyncTaskDTO {
public SyncTaskDTO(){}
public SyncTaskDTO(Long taskId, Boolean isClearData) {
this.taskId = taskId;
this.isClearData = isClearData;
}
private Long taskId;
private Boolean isClearData = false;
public Long getTaskId() {
return taskId;
}
public void setTaskId(Long taskId) {
this.taskId = taskId;
}
public Boolean getClearData() {
return isClearData;
}
public void setClearData(Boolean clearData) {
isClearData = clearData;
}
}
然后實現對應的邏輯
/**
* @author 青石路
*/
@RestController
@RequestMapping("/task")
public class SyncTaskController {
private static final Logger LOG = LoggerFactory.getLogger(SyncTaskController.class);
@PostMapping("/delete")
public ResultEntity<String> delete(@RequestBody SyncTaskDTO syncTask) {
// TODO 刪除處理
LOG.info("刪除任務[taskId={}]", syncTask.getTaskId());
if (syncTask.getClearData()) {
LOG.info("清空任務[taskId={}]歷史數據", syncTask.getTaskId());
// TODO 清空歷史數據
}
return ResultEntity.success("刪除成功");
}
}
調整完之后,同事通知我,讓我做對 qsl-workflow 做對應的調整。調整很簡單,qsl-workflow 刪除時直接傳 true 即可
/**
* @author 青石路
*/
@RestController
@RequestMapping("/definition")
public class WorkflowController {
private static final Logger LOG = LoggerFactory.getLogger(WorkflowController.class);
@Resource
private OfflineSyncApi offlineSyncApi;
@PostMapping("/delete")
public ResultEntity<String> delete(@RequestBody WorkflowDTO workflow) {
LOG.info("刪除工作流[workflowId={}]", workflow.getWorkflowId());
// 1.查詢工作流節點,查到離線同步節點(taskId = 1)
// 2.刪除工作流節點,刪除離線同步節點
// 刪除離線同步任務,isClearData直接傳true
ResultEntity<String> syncDeleteResult = offlineSyncApi.deleteTask(new SyncTaskDTO(1L, true));
if (syncDeleteResult.getCode() != 200) {
LOG.error("刪除離線同步任務[taskId={}]失敗:{}", 1, syncDeleteResult.getMessage());
ResultEntity.fail(syncDeleteResult.getMessage());
}
return ResultEntity.success("刪除成功");
}
}
調整完成之后,發起 http 請求,發現歷史數據沒有被清除,看日志發現
LOG.info("清空任務[taskId={}]歷史數據", syncTask.getTaskId());
沒有打印,參數明明傳的是 true 吖!!!
offlineSyncApi.deleteTask(new SyncTaskDTO(1L, true));
這是哪里出了問題?

問題排查
因為 qsl-offline-sync-api 是直接引入的,并非我實現的,所以我第一時間找到了其實現者,反饋了問題后讓其自測下;一開始他還很自信,說這么簡單怎么會有問題

當他啟動 qsl-offline-sync-server 后,發起 http 請求
POST http://localhost:8080/data-govern/sync/task/delete
Content-Type: application/json{
"taskId": 123,
"isClearData": true
}
發現 isClearData 的值是 false

此刻,疑問從我的額頭轉移到了他的額頭上,他懵逼了,我輕松了。為了功能能夠正常交付,我還是決定看下這個問題,沒有了心理壓力,也許更容易發現問題所在。第一眼看到 isClearData,我就隱約覺得有問題,所以我決定仔細看下 SyncTaskDTO 這個類,發現 isClearData 的 setter 和 getter 方法有點不一樣
private Boolean isClearData = false;
public Boolean getClearData() {
return isClearData;
}
public void setClearData(Boolean clearData) {
isClearData = clearData;
}
方法名是不是少了 Is?帶著這個疑問我找到了同事,問他 setter 、getter 為什么要這么命名?他說是 idea 工具自動生成的(也就是我們平時用到的idea自動生成setter、getter方法的功能)

我讓他把 Is 補上試試
private Boolean isClearData = false;
public Boolean getIsClearData() {
return isClearData;
}
public void setIsClearData(Boolean isClearData) {
this.isClearData = isClearData;
}
發現傳值正常了,他回過頭看著我,我看著他,兩人同時提問
他:為什么加了
Is就可以了?我:布爾類型的變量,你為什么要加
is前綴?
問題延申
作為一個嚴謹的開發,不只是要知其然,更要知其所以然;關于
為什么加了
Is就可以了
這個問題,我們肯定是要會上一會的;會這個問題之前,我們先來捋一下參數的流轉,因為是基于 Spring MVC 實現的 Web 應用,所以我們可以這么問 deepseek
Spring MVC 是如何將前端參數轉換成POJO的
能夠查到如下重點信息

RequestResponseBodyMethodProcessor 的 resolveArgument
/**
* Throws MethodArgumentNotValidException if validation fails.
* @throws HttpMessageNotReadableException if {@link RequestBody#required()}
* is {@code true} and there is no body content or if there is no suitable
* converter to read the content with.
*/
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
正是解析參數的地方,我們打個斷點,再發起一次 http 請求

很明顯,readWithMessageConverters 是處理并轉換參數的地方,繼續跟進去會來到 MappingJackson2HttpMessageConverter 的 readJavaType 方法

此刻我們可以得到,是通過 jackson 完成數據綁定與數據轉換的。繼續跟進,會看到 isClearData 的賦值過程

通過前端傳過來的參數 isClearData 找對應的 setter方法是 setIsClearData,而非 setClearData,所以問題
為什么加了
Is就可以了
是不是就清楚了?
問題解決
-
按上述方式調整
isClearData的setter、getter方法帶上
ispublic Boolean getIsClearData() { return isClearData; } public void setIsClearData(Boolean isClearData) { this.isClearData = isClearData; } -
布爾類型的變量,不用
is前綴可以用
if前綴private Boolean ifClearData = false; public Boolean getIfClearData() { return ifClearData; } public void setIfClearData(Boolean ifClearData) { this.ifClearData = ifClearData; } -
可以結合
@JsonProperty來處理@JsonProperty("isClearData") private Boolean isClearData = false;
總結
-
Spring MVC對參數的綁定與轉換,內容不同,采用的處理器也不同-
form表單數據(application/x-www-form-urlencoded)
處理器:
ServletModelAttributeMethodProcessor -
JSON 數據 (application/json)
處理器:
RequestResponseBodyMethodProcessor
轉換器:MappingJackson2HttpMessageConverter -
多部分文件 (multipart/form-data)
處理器:
MultipartResolver
-
-
POJO的布爾類型變量,不要加is前綴命名不符合規范,集成第三方框架的時候就很容易出不好排查的問題
成不了規范的制定者,那就老老實實遵循規范!

浙公網安備 33010602011771號