Spring Boot RestController接口如何輸出到終端
今天在公司項目的代碼上實踐了一下,發現這種方法對業務代碼侵入性太大,大量的業務方法需要增加傳入參數,使得業務方法和
HttpServletResponse類耦合度太高,需要將業務和輸出解耦,具體實現請看下一篇文章 使用HttpServletResponse實現curl接口時控制臺輸出(續)
背景
公司項目的批處理微服務,一般是在晚上固定時段通過定時任務執行,但為了預防執行失敗,我們定義了對應的應急接口,必要時可以通過運維在終端中進行curl操作。然而,部分任務耗時較長,curl命令執行后長時間沒有輸出,如果不查看日志,無法知道系統當前的狀態,因此有必要研究一下如何在curl命令調用接口時,在終端輸出部分信息,已告知運維人員當前命令的執行狀態。
原理
使用 HttpServletResponse 類,可以自定義輸出,也就是模擬網頁輸出的效果,只不過我們輸出的內容是純文本。
代碼
- 新建一個Spring Boot項目,建立一個
TestController,作為我們的應急接口。
@RestController
@Slf4j
public class EmergencyController {
@Resource
private TestService testService;
@GetMapping("/test")
public void test(HttpServletResponse response) throws IOException {
response.setContentType("text/plain;charset=utf-8");
try {
boolean result = testService.emergencyOperation(response);
response.getWriter().println(CommonResult.success(null, "應急任務處理成功!").toString());
} catch (IOException e) {
log.error("應急任務處理失敗!", e);
response.getWriter().println(CommonResult.fail(null, "應急任務處理失敗!").toString());
}
}
}
這里的一個坑是:如果使用了這種方法輸出,那么接口方法不能再有任何返回值,不然會讓Spring Boot以為重復調用了 response.getWriter() 函數,于是報錯。需要將原本的輸出內容(如通用返回體CommonResult類,或字符串String)也放入 response.getWriter() 進行輸出。
其中 TestService 是我們的批處理業務接口,無論是應急接口還是定時任務,都需要使用該接口進行實際的業務操作。其實現類 TestServiceImpl 代碼如下:
/**
* 模擬應急操作方法
*/
@Override
public boolean emergencyOperation(HttpServletResponse response) throws IOException {
// 如果是定時任務,則該參數傳入null,不在終端輸出
boolean canOutput = response != null;
PrintWriter writer = createPrintWriter(canOutput, response);
log.info("開始執行應急操作任務");
output(canOutput, writer, "開始執行應急操作任務");
for (int i = 0; i < 20; i++) {
output(canOutput, writer, "完成第" + (i+1) + "批次");
log.info("完成第 {} 批次", i+1);
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
log.warn("應急操作任務失敗");
output(canOutput, writer, "應急操作任務失敗");
return false;
}
}
log.info("完成應急操作任務");
output(canOutput, writer, "應急操作任務完成");
return true;
}
其中 createPrintWriter() 方法設置 HttpServletResponse 對象的 ContentType 屬性,我們輸出的是純文本,因此需要設置為 text/plain;charset=utf-8,具體代碼如下:
private PrintWriter createPrintWriter(boolean output, HttpServletResponse response) throws IOException {
if (output) {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset=utf-8");
return response.getWriter();
}
return null;
}
在需要使用的地方調用 output() 方法,向控制臺打印輸出內容:
private void output(boolean output, PrintWriter writer, String message) throws IOException {
if (!output) {
return;
}
writer.println(message);
writer.flush();
}
測試
使用 Maven 構建項目,在項目生成目錄下運行 jar 包啟動程序,另外開一個控制臺窗口,執行 curl http://localhost:8080/test,可以看到控制臺輸出如下:

同時在運行 Spring Boot 應用的窗口,也可以看到日志成功輸出:

定時任務執行情況
我們定義定時任務 EmergencyTask 如下(不要忘了在應用啟動類上增加 @EnableScheduling 注解)
@Component
@Slf4j
public class EmergencyTask {
@Resource
private TestService testService;
@Scheduled(cron = "0 */5 22 * * MON-FRI")
public void emergencyTask() throws IOException {
testService.emergencyOperation(null);
}
}
這里我們設置的是從22點后每隔5分鐘執行一次,當然實際項目中需要根據需求來確定定時任務執行時間。
啟動應用,等5分鐘,可以看到定時任務成功執行。

再次運行 curl http://localhost:8080/test,可以看到控制臺和日志均正常輸出。
總結
使用 HttpServletResponse 類,可以在使用 curl 執行 Spring Boot REST接口的同時,在控制臺輸出一些信息,給運維人員知道當前命令執行的狀態。

浙公網安備 33010602011771號