在JavaWeb企业级应用中,接口是一个很常见的功能模块。此处不讨论如下问题(请自行搜索):
- 接口是什么
- 为什么要使用接口
- .....
一般的接口代码中会有如下代码片段(此处省略接收参数、处理异常请求等功能代码):
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的内容。
几天前遇到一个问题,要在Struts2框架中使用拦截器,来截取请求报文与响应报文。(个人认为该功能应该由核心处理器去统一完成。)不论能否实现,且试一下。
上面的代码中很明显将一个字符串放在response里响应给请求者。那我们能不能在这里做文章呢。 Debug一下writer的值看能不能有什么线索。
我们可以在writer.flush()之后添加点代码:
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();
}
我们在
System.out.print(writerToBeRead.getClass().getName());
这一行添加断点进行调试。 正好看到了要响应给请求者的信息。
可以看到区域3中存放的信息就是msg,它的内容在区域5中清晰地显示着。而区域3是区域2的成员属性(其实区域4这个对象也和区域2一样)。
最重要的是输出的内容
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(); } }