在JavaWeb企業級應用中,接口是一個很常見的功能模塊。此處不討論以下問題(請自行搜索):git
通常的接口代碼中會有以下代碼片斷(此處省略接收參數、處理異常請求等功能代碼):程序員
PrintWriter writer = null; try { writer = response.getWriter(); String msg = "<xml><msg>hahah</msg><createAt>" +System.currentTimeMillis()+"</createAt></xml>"; writer.print(msg); writer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { writer.close(); } }
很簡單,如上代碼將msg的內容響應給請求者,請求者將獲取到msg的內容。apache
幾天前遇到一個問題,要在Struts2框架中使用攔截器,來截取請求報文與響應報文。(我的認爲該功能應該由核心處理器去統一完成。)不論可否實現,且試一下。tomcat
上面的代碼中很明顯將一個字符串放在response裏響應給請求者。那咱們能不能在這裏作文章呢。 Debug一下writer的值看能不能有什麼線索。框架
咱們能夠在writer.flush()以後添加點代碼:eclipse
PrintWriter writer = null; try { writer = response.getWriter(); String msg = "<xml><msg>hahah</msg><createAt>" +System.currentTimeMillis()+"</createAt></xml>"; writer.print(msg); writer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { writer.close(); } } try { PrintWriter writerToBeRead = response.getWriter(); System.out.print(writerToBeRead.getClass().getName()); } catch (Exception e) { e.printStackTrace(); }
咱們在ide
System.out.print(writerToBeRead.getClass().getName());
這一行添加斷點進行調試。 正好看到了要響應給請求者的信息。.net
能夠看到區域3中存放的信息就是msg,它的內容在區域5中清晰地顯示着。而區域3是區域2的成員屬性(其實區域4這個對象也和區域2同樣)。debug
最重要的是輸出的內容調試
org.apache.catalina.connector.CoyoteWriter
正好咱們在上圖的區域1中看到了
"writerToBeRead"=CoyoteWriter
說明writerToBeRead這個對象是org.apache.catalina.connector.CoyoteWriter
類型的。若是您瞭解tomcat源代碼,能夠知道該類在TOMCAT_BASE/lib/catalina.jar中,這個jar由tomcat主程序調用,不被程序員顯式使用,因此在添加dependency時添加provided類型的scope,因爲它還會引入coyote.jar文件,故也將其排除。
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>catalina</artifactId> <version>6.0.45</version> <scope>provided</scope> <exclusions> <exclusion> <artifactId>coyote</artifactId> <groupId>org.apache.tomcat</groupId> </exclusion> </exclusions> </dependency>
而後咱們就將writerToBeRead強制轉換成CoyoteWriter
PrintWriter writerToBeRead = response.getWriter(); CoyoteWriter cw = (CoyoteWriter)writerToBeRead;
而後在eclipse中按ctrl點擊CoyoteWriter,能夠看到它有一個受保護的成員屬性ob
下面要進行的就是——反射了。 先取出ob的內容,代碼以下 :
Class<?> clazz = cw.getClass(); Field declaredField = clazz.getDeclaredField("ob"); declaredField.setAccessible(true); OutputBuffer ob = (OutputBuffer)declaredField.get(cw);
一樣點擊OutputBuffer,能夠看到它有一個私有屬性outputChunk
無論三七二十一,拿出來再說。
Class<?> classOutputBuffer = ob.getClass(); Field fieldOutputChunk = classOutputBuffer.getDeclaredField("outputChunk"); fieldOutputChunk.setAccessible(true); Object object = fieldOutputChunk.get(ob); System.out.println("text from response: "+ object);
哈,咱們拿到想要的數據了。
多說一句,上面的object的類型是`org.apache.tomcat.util.buf.ByteChunk`,感興趣的能夠看下tomcat源碼,瞭解下它的toString()方法或其它的,tomcat真的是大師級做品啊。
博客也至此結束了。 歡迎參觀個人博客,拍磚~。
提供源文件內容:
public void doGetDataFromResponse(HttpServletResponse response) { PrintWriter writer = null; try { writer = response.getWriter(); String msg = "<xml><msg>hahah</msg><createAt>" +System.currentTimeMillis()+"</createAt></xml>"; writer.print(msg); writer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { writer.close(); } } try { PrintWriter writerToBeRead = response.getWriter(); System.out.print(writerToBeRead.getClass().getName()); CoyoteWriter cw = (CoyoteWriter)writerToBeRead; LOG.debug(cw.getClass().getName()); Class<?> clazz = cw.getClass(); Field declaredField = clazz.getDeclaredField("ob"); declaredField.setAccessible(true); OutputBuffer ob = (OutputBuffer)declaredField.get(cw); LOG.debug(ob); Class<?> classOutputBuffer = ob.getClass(); Field fieldOutputChunk = classOutputBuffer.getDeclaredField("outputChunk"); fieldOutputChunk.setAccessible(true); Object object = fieldOutputChunk.get(ob); LOG.info(object.getClass().getName()); LOG.info("text from response: "+ object); } catch (Exception e) { e.printStackTrace(); } }