在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(); } }