1.需求:給某些請求接口記錄日志,記錄請求的數據和響應的數據和請求所花費的時間。這里采用非侵入式編程,也業務代碼進行解耦。按照spring AOP 的編程思想。
2.編程設計:在spring 攔截器中植入日志代碼。因為其剛好滿足非侵入,且能捕獲到請求和響應的數據。
3.了解spring 攔截器和過濾器的運行原理
先執行過濾器,然后執行攔截器。
4. 分析:當在攔截器中獲取請求的輸入流和響應的輸出流的時候發現,只能讀取一次,攔截器在具體的業務代碼之前執行,導致請求的輸入流被攔截器使用,到controller 的時候將獲取不到數據。當響應的數據流被攔截器的postHandler 使用之后,輸出到客戶端沒有響應的數據。
流程:先創建過濾器,過濾器是鏈式執行,配置的web.xml 的中。
WrapperFilter 在web.xml 中的配置
<filter> <filter-name>sosWrapperFilter</filter-name> <filter-class>com.xiao.handler.filter.WrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>sosWrapperFilter</filter-name>
<!-- 過濾的url地址 -->
<url-pattern>/myTest/*</url-pattern> </filter-mapping>
增加一個攔截器配置,配置需要過濾器的請求地址,走 spring mvc的攔截器,攔截的是請求地址(不包含上下文 eg:http://localost:8080/xiao)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/test/*"/> <bean class="com.xiao.interceptor.SosOrderInterceptor"/> </mvc:interceptor> </mvc:interceptors> </beans>
開始編寫過濾器,攔截器
過濾器
/** * * @describ request 請求過濾器增強 * @date 2019-03-06 * @author coder_xiao */ public class WrapperFilter implements Filter { private static Log log = LogFactory.getLog(WrapperFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { CacheContentRequestWrapper requestWrapper=null; CacheContentResponseWrapper responseWrapper=null; HttpServletRequest request=(HttpServletRequest)servletRequest; HttpServletResponse response=(HttpServletResponse) servletResponse; try { requestWrapper = new CacheContentRequestWrapper(request); responseWrapper = new CacheContentResponseWrapper(response); }catch (Exception e){ log.error("獲取requestWrapper失敗!",e); }finally { if((requestWrapper!=null)&&(responseWrapper!=null)) { filterChain.doFilter(requestWrapper, responseWrapper); responseWrapper.reWriteResponse(responseWrapper,(HttpServletResponse) servletResponse); }else{ filterChain.doFilter(servletRequest, servletResponse); } } } @Override public void destroy() { } }
攔截器
public class SosOrderInterceptor implements HandlerInterceptor { private final static Log logger = LogFactory.getLog(SosOrderInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { BufferedReader bufferedInput=null; String data=null; try{ String urlPath=request.getRequestURI(); String contentType=request.getContentType(); Boolean typeIndex=contentType==null?false:contentType.indexOf("application/json")>-1; Boolean createIndex=urlPath.indexOf("createOrder")>-1; Boolean buildIndex=urlPath.indexOf("buildDraftOrder")>-1; Boolean urlIndex=(urlPath.indexOf("createSosOrder")+urlPath.indexOf("buildDraftSosOrder"))>-2; //POST 請求 contentType=application/json if(typeIndex&&urlIndex) { //支持mark and reset 為使流復用 CacheContentRequestWrapper contentRequestWrapper= new CacheContentRequestWrapper(request); data = contentRequestWrapper.getBodyString(); String requestId=UUIDUtil.uuid(); request.setAttribute("requestId",requestId); BuildAudit audit=BeanTool.getBean(BuildAudit.class); //創建訂單 if (createIndex) { logger.info("開始調用創建訂單"); audit.buildData(data,"1","1","創建訂單",requestId); } //創建草稿 if (buildIndex) { logger.info("創建草稿單!"); audit.buildData(data,"2","1","創建草稿單",requestId); } } }catch (Exception e){ e.printStackTrace(); logger.error(e); }finally { if(bufferedInput!=null) { bufferedInput.close(); } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception { logger.info("postHandle"); try{ String urlPath=request.getRequestURI(); String contentType=request.getContentType(); Boolean typeIndex=contentType==null?false:contentType.indexOf("application/json")>-1; Boolean createIndex=urlPath.indexOf("createOrder")>-1; Boolean buildIndex=urlPath.indexOf("buildDraftOrder")>-1; Boolean urlIndex=(urlPath.indexOf("createSosOrder")+urlPath.indexOf("buildDraftOrder"))>-2; String requestId= request.getAttribute("requestId")==null?null:request.getAttribute("requestId").toString(); if(typeIndex&&urlIndex) { CacheContentResponseWrapper cacheResponse =(CacheContentResponseWrapper)((WebStatFilter.StatHttpServletResponseWrapper) response).getResponse(); String data=new String(cacheResponse.getResponseData(),"UTF-8"); BuildAudit audit=BeanTool.getBean(BuildAudit.class); //創建訂單 if (createIndex) { audit.buildData(data,"1","2",null,requestId); } //創建草稿 if (buildIndex) { audit.buildData(data,"2","2",null,requestId); } //草稿單轉正 if (urlPath.indexOf("buildDraftSosOrder") > -1) { } } }catch (Exception e){ logger.error(e); } } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { logger.info("afterCompletion"); try{ }catch (Exception ex){ logger.error(ex); } }
兩個緩存器
package com.lacesar.handler.wrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @description 包裝spring 的 HttpServletRequestWrapper */ public class CacheContentRequestWrapper extends HttpServletRequestWrapper { private static Log log = LogFactory.getLog(CacheContentRequestWrapper.class); /** * 存儲body數據的容器 */ private final byte[] body; private String requestId; public CacheContentRequestWrapper(HttpServletRequest request) throws IOException { super(request); // 將body數據存儲起來 String bodyStr = getBodyString(request); body = bodyStr.getBytes("UTF-8"); } /** * 獲取請求Body * * @param request request * @return String */ public String getBodyString(final ServletRequest request) { try { return inputStream2String(request.getInputStream()); } catch (IOException e) { log.error("", e); throw new RuntimeException(e); } } /** * 獲取請求Body * * @return String */ public String getBodyString() { if (body != null) { final InputStream inputStream = new ByteArrayInputStream(body); return inputStream2String(inputStream); } else { return null; } } /** * 將inputStream里的數據讀取出來並轉換成字符串 * * @param inputStream inputStream * @return String */ private String inputStream2String(InputStream inputStream) { StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { log.error("", e); throw new RuntimeException(e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { log.error("", e); } } } return sb.toString(); } public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream inputStream = new ByteArrayInputStream(body); return new ServletInputStream() { public int read() throws IOException { return inputStream.read(); } public boolean isFinished() { return false; } public boolean isReady() { return false; } public void setReadListener(ReadListener readListener) { } }; } public String getRequestId() { return requestId; } public void setRequestId(String requestId) { this.requestId = requestId; } }
package com.lacesar.handler.wrapper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class CacheContentResponseWrapper extends HttpServletResponseWrapper { private ByteArrayOutputStream buffer = null; private ServletOutputStream out = null; private PrintWriter writer = null; public CacheContentResponseWrapper(HttpServletResponse resp) throws IOException { super(resp); buffer = new ByteArrayOutputStream(); out = new WapperedOutputStream(buffer); writer = new PrintWriter(new OutputStreamWriter(buffer, "UTF-8")); } /** * 重新寫入response 數據 * * @param wrapperResponse * @param response */ public void reWriteResponse(CacheContentResponseWrapper wrapperResponse, HttpServletResponse response) { String result = null; try { result = new String(wrapperResponse.getResponseData(), "UTF-8"); //解決可能在運行的過程中頁面只輸出一部分 response.setContentLength(-1); response.setCharacterEncoding("UTF-8"); PrintWriter out = null; out = response.getWriter(); if (out != null) { out.write(result); out.flush(); out.close(); } } catch (Exception e) { e.printStackTrace(); } } public ServletOutputStream getOutputStream() throws IOException { return out; } public PrintWriter getWriter() throws UnsupportedEncodingException { return writer; } public void flushBuffer() throws IOException { if (out != null) { out.flush(); } if (writer != null) { writer.flush(); } } public void reset() { buffer.reset(); } public byte[] getResponseData() throws IOException { flushBuffer(); return buffer.toByteArray(); } private class WapperedOutputStream extends ServletOutputStream { private ByteArrayOutputStream bos = null; public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException { bos = stream; } @Override public void write(int b) throws IOException { bos.write(b); } @Override public void write(byte[] b) throws IOException { bos.write(b, 0, b.length); } public boolean isReady() { return false; } public void setWriteListener(WriteListener writeListener) { } } }