spring框架中,response处理逻辑

林子曰 2018-10-18 02:12:54
最近在看设计模式,看到了装饰者模式,然后就想起自己的一个项目也用到了装饰者模式,回过头去看了下,发现一些东西没有搞懂,希望大佬帮着解答一下。
先说一下业务场景吧,是给APP提供接口的,app调用接口之后,所有返回的数据使用json格式包装一下(主要是加密)。自然就用到了filter的方式:response包装一层,加上状态码和数据加密。最后返回数据。现在对ResponseWrapper这个处理逻辑产生的一点疑问。

@Slf4j
@Order(Ordered.LOWEST_PRECEDENCE - 1)// 定义最后一个执行
@WebFilter(urlPatterns = "/*")
public class ResponseWrapperFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("..doFilterInternal..1");
String uri = request.getRequestURI();
boolean pass = "/".equals(uri);
if (!pass) {
System.out.println("..doFilterInternal..2");
ResponseWrapper wrapper = new ResponseWrapper(response);
try {
filterChain.doFilter(request, wrapper);
System.out.println("..doFilterInternal..3");
String responseStr = new String(wrapper.toByteArray(), response.getCharacterEncoding());
String new_response = DataUtils.parseResponse(request, JSON.parseObject(responseStr));
response.setContentLength(new_response.getBytes("utf-8").length);
response.setContentType("application/json;charset=utf-8");
response.getOutputStream().write(new_response.getBytes("utf-8"));
System.out.println("..doFilterInternal..4");
} catch (Exception e) {
e.printStackTrace();
}
} else {
// 直接通过。
ResponseWrapper wrapper = new ResponseWrapper(response);
filterChain.doFilter(request, wrapper);
}
}
}

其中用到这个,这个是从网上找的资料,先开始没有很明白为什么要这样使用,最近研究设计模式就开始仔细的看。越看越糊涂了。

ResponseWrapper wrapper = new ResponseWrapper(response);

代码为:

public class ResponseWrapper extends HttpServletResponseWrapper {

private ByteArrayOutputStream output;
private ServletOutputStream filterOutput;

public ResponseWrapper(HttpServletResponse response) {
super(response);
output = new ByteArrayOutputStream();
}

/**
* 参考:
* https://blog.csdn.net/qq_27886997/article/details/76560235
* https://blog.csdn.net/more_try/article/details/81175699
*
* @return
* @throws IOException
*/
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (filterOutput == null) {
filterOutput = new ServletOutputStream() {
@Override
public void write(int b) throws IOException {
output.write(b);
}

@Override
public boolean isReady() {
return false;
}

@Override
public void setWriteListener(WriteListener writeListener) {
}
};
}
return filterOutput;
}

public byte[] toByteArray() {
return output.toByteArray();
}
}

按照装饰者模式的设计,本质上ResponseWrapper都是继承了ServletResponseWrapper,在ServletResponseWrapper中有一个ServletResponse,应该是要指向一个response。
引用
servlet针对每一次http请求,都会创建一个request和response,从servlet处理请求到返回结果都会用到这2个类,嗯,其实是接口。

那么其实这个ServletResponse,应该就是指的一次http请求产生的response了。
ResponseWrapper wrapper = new ResponseWrapper(response);
这段代码创建了一个实例,看构造函数里面。

一个是调用父级的构造函数,最终是将response交给了ResponseWrapper?

但是疑问就产生了。filter是链式处理,处理完会一级一级的返回。那么按照mvc的模式解释,controller在处理完数据之后,将数据绑定到视图view中,然后通过ModelAndView返回给客户端。
我猜测view的底层也是用response.getOutPutStream().write()这样的方式写数据流到客户端。是否是这样?那么spring什么时候将数据写到outputstream的?怎么去获取呢?

第二个问题,controller产生的数据是何时,用什么手段保存在服务器端,并在最后通过什么返回?
第三个问题,因为加了filter,在数据返回时,需要拦截OutPutStream,因为要读取返回的数据,额外处理一下数据,最后再返回。那么OutPutStream的原则是读出之后,在缓存区就么有了,所以在ResponseWrapper就有一个ByteArrayOutputStream去临时存储,就可以反复读取了。但是我没有看到最开始的数据是怎么写到这个ByteArrayOutputStream的?或者是我没有完全理解这个处理方式。

希望大佬讲下这个原理。
...全文
391 1 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
林子曰 2018-10-19
  • 打赏
  • 举报
回复
人工置顶。大佬看看

67,549

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧