上一篇文章的問題
在上一篇文章 Spring Boot RestController接口如何輸出到終端 中討論瞭如何使用 HttpSerlvetResponse 寫入輸出流,使應急接口通過 curl 調用時可以在控制檯輸出信息,使運維人員知道命令執行情況。
但是上一篇文章的問題是,HttpServletResponse 是 Controller 的參數,這就使得如果要在其調用的 Service 方法中也要實現控制檯輸出,就必須讓所有涉及到的 Service 方法都帶上 HttpServletResponse 參數,這對業務的侵入實在太大,對於實際業務可能包含多個 Service、Component 之間的相互引用,改造成本是不可容忍的。
本文的解決思路
那麼筆者就在想,如何構造一個全局的 HttpServletResponse 對象,使它能夠在各個 Controller、Service、Component 之前傳遞呢?經過查閱資料發現,HttpServletResponse 對象本身是可以被 @Autowired
或 @Resource
註解注入的!那麼我們就可以構造一個帶有自動注入的 HttpServletResponse 對象的 Component,然後在其他所有需要使用的地方去自動注入這個 Component 即可。
代碼
代碼實現如下:
@Component
public class WebUtil {
@Resource
private HttpServletResponse httpServletResponse;
public void output(String message) {
if (httpServletResponse == null) {
return;
}
try {
httpServletResponse.setContentType("text/plain;charset=utf-8");
httpServletResponse.setCharacterEncoding("UTF-8");
PrintWriter writer = httpServletResponse.getWriter();
writer.println(message);
writer.flush();
} catch (Exception e) {
return;
}
}
}
由於我們的目的只是爲了實現控制檯輸出,所以如果該方法拋出異常(主要是 IOException
和 IllegalStatesException
),那麼直接返回即可。這個問題主要出在定時任務中,因爲定時任務是不含 HttpServletResponse
對象的,如果在定時任務中調用該方法的時候會拋出 IllegalStatesException
。
在要使用該方法的類中使用自動注入,注入 WebUtil
類,然後使用它的對象(而不是這個類本身)的output方法即可。
@Service
@Slf4j
public class TestServiceImpl implements TestService {
@Resource
private WebUtil webUtil;
@Override
public boolean emergencyOperation() throws IOException {
log.info("開始執行應急操作任務");
webUtil.output( "開始執行應急操作任務");
for (int i = 0; i < 20; i++) {
webUtil.output( "完成第" + (i+1) + "批次");
log.info("完成第 {} 批次", i+1);
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
log.warn("應急操作任務失敗");
webUtil.output( "應急操作任務失敗");
return false;
}
}
log.info("完成應急操作任務");
webUtil.output( "應急操作任務完成");
return true;
}
}
經過測試發現,curl調用接口可以實現控制檯輸出,定時任務也執行正常,沒有預期外的異常產生。