在基於 Spring + Spring MVC + Hibernate 的 Java Web 項目中,為了便於開發調試與線上排查,記錄 HTTP 請求的處理詳情(訪問時間、Controller 路徑、方法名、請求參數、處理耗時等)至關重要。利用 Spring MVC 的 HandlerInterceptor 機制,可無侵入式地實現全鏈路訪問日誌記錄。
package com.api.web.interceptor;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.yijia.api.util.SimpleDateFormatCache;
public class TimeCostInterceptor implements HandlerInterceptor {
private static final Logger logger = Logger.getLogger(TimeCostInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
StringBuilder sb = new StringBuilder(1000);
sb.append("-----------------------")
.append(SimpleDateFormatCache.getYmdhms().format(new Date()))
.append("-------------------------------------\n");
sb.append("Controller: ").append(hm.getBean().getClass().getName()).append("\n");
sb.append("Method : ").append(hm.getMethod().getName()).append("\n");
sb.append("Params : ").append(getParamString(request.getParameterMap())).append("\n");
sb.append("URI : ").append(request.getRequestURI()).append("\n");
logger.info(sb.toString()); // 建議使用日誌框架而非 System.out
}
return true; // 繼續執行後續攔截器與 Controller
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (handler instanceof HandlerMethod) {
long startTime = (Long) request.getAttribute("startTime");
long executeTime = System.currentTimeMillis() - startTime;
logger.info("CostTime : " + executeTime + "ms\n" + "-------------------------------------------------------------------------------");
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 可在此處理異常日誌、資源清理等
if (ex != null) {
logger.error("Request processing failed: " + request.getRequestURI(), ex);
}
}
private String getParamString(Map<String, String[]> paramMap) {
StringBuilder sb = new StringBuilder();
for (Entry<String, String[]> entry : paramMap.entrySet()) {
sb.append(entry.getKey()).append("=");
String[] values = entry.getValue();
if (values != null && values.length == 1) {
sb.append(values[0]).append("\t");
} else {
sb.append(Arrays.toString(values)).append("\t");
}
}
return sb.toString();
}
}
在 spring-mvc.xml 中註冊攔截器,支持路徑匹配與排除:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/"/>
<mvc:exclude-mapping path="/test"/>
<bean class="com.api.web.interceptor.TimeCostInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

preHandle 中檢查 Session/Token,未通過直接返回 401 或重定向登錄頁。Access-Control-Allow-* 響應頭。preHandle 返回 false 將中斷請求處理鏈,需自行寫入響應(如重定向、輸出 JSON 錯誤)。getParamString 中脫敏或過濾。