servlet中Response输出源码解析
2015-11-25 15:35
330 查看
在Servlet编程中 经常会写
获取字符输出流 这里 response对象是org.apache.catalina.connector.ResponseFacade
out是org.apache.catalina.connector.CoyoteWriter
println方法如下:
调用链如下
会将字符串写入OutputBuffer中的CharChunk buffer
println()代码如下:
写入换行符
执行完servlet的逻辑后
调用链如下
org.apache.catalina.connector.OutputBuffer #realWriteChars
上边的代码会将CharChunk 转换为ByteChunk
最好会调用到SocketOutputStream 的socketWrite()
SocketOutputStream #socketWrite 源代码
写回客户端后 客户端浏览器收到相应的数据包
这是tomcat容器下servlet向客户端输出的大体调用逻辑
只是一次debug获取到的小知识点
像SSI这种需求一般都是自己去扩展Response
response.setContentType("text/html"); PrintWriter out = response.getWriter();
获取字符输出流 这里 response对象是org.apache.catalina.connector.ResponseFacade
out是org.apache.catalina.connector.CoyoteWriter
println方法如下:
@Override public void println(String s) { print(s); println(); }
调用链如下
at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:543) at org.apache.catalina.connector.CoyoteWriter.write(CoyoteWriter.java:174) at org.apache.catalina.connector.CoyoteWriter.write(CoyoteWriter.java:184) at org.apache.catalina.connector.CoyoteWriter.print(CoyoteWriter.java:242) at org.apache.catalina.connector.CoyoteWriter.println(CoyoteWriter.java:309) at SessionExample.doGet(SessionExample.java:55)
会将字符串写入OutputBuffer中的CharChunk buffer
println()代码如下:
@Override public void println() { write(LINE_SEP); }
写入换行符
执行完servlet的逻辑后
调用链如下
realWriteChars():474, OutputBuffer {org.apache.catalina.connector} flushBuffer():464, CharChunk {org.apache.tomcat.util.buf} close():291, OutputBuffer {org.apache.catalina.connector} finishResponse():512, Response {org.apache.catalina.connector} service():435, CoyoteAdapter {org.apache.catalina.connector}
org.apache.catalina.connector.OutputBuffer #realWriteChars
public void realWriteChars(char buf[], int off, int len) throws IOException { outputCharChunk.setChars(buf, off, len); while (outputCharChunk.getLength() > 0) { conv.convert(outputCharChunk, bb); if (bb.getLength() == 0) { // Break out of the loop if more chars are needed to produce any output break; } if (outputCharChunk.getLength() > 0) { bb.flushBuffer(); } } }
上边的代码会将CharChunk 转换为ByteChunk
socketWrite():102, SocketOutputStream {java.net} write():159, SocketOutputStream {java.net} realWriteBytes():215, InternalOutputBuffer {org.apache.coyote.http11} flushBuffer():480, ByteChunk {org.apache.tomcat.util.buf} endRequest():159, InternalOutputBuffer {org.apache.coyote.http11} action():752, AbstractHttp11Processor {org.apache.coyote.http11} action():174, Response {org.apache.coyote} finish():291, Response {org.apache.coyote} close():313, OutputBuffer {org.apache.catalina.connector} finishResponse():512, Response {org.apache.catalina.connector} service():435, CoyoteAdapter {org.apache.catalina.connector}
最好会调用到SocketOutputStream 的socketWrite()
SocketOutputStream #socketWrite 源代码
private void socketWrite(byte b[], int off, int len) throws IOException { if (len <= 0 || off < 0 || off + len > b.length) { if (len == 0) { return; } throw new ArrayIndexOutOfBoundsException(); } Object traceContext = IoTrace.socketWriteBegin(); int bytesWritten = 0; FileDescriptor fd = impl.acquireFD(); try { socketWrite0(fd, b, off, len); bytesWritten = len; } catch (SocketException se) { if (se instanceof sun.net.ConnectionResetException) { impl.setConnectionResetPending(); se = new SocketException("Connection reset"); } if (impl.isClosedOrPending()) { throw new SocketException("Socket closed"); } else { throw se; } } finally { impl.releaseFD(); IoTrace.socketWriteEnd(traceContext, impl.address, impl.port, bytesWritten); } }
写回客户端后 客户端浏览器收到相应的数据包
这是tomcat容器下servlet向客户端输出的大体调用逻辑
只是一次debug获取到的小知识点
像SSI这种需求一般都是自己去扩展Response
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.catalina.ssi; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Locale; import java.util.TimeZone; import javax.servlet.ServletContext; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import org.apache.tomcat.util.ExceptionUtils; /** * A HttpServletResponseWrapper, used from * <code>SSIServletExternalResolver</code> * * @author Bip Thelin * @author David Becker * @version $Id: ResponseIncludeWrapper.java 1043105 2010-12-07 15:42:32Z markt $ */ public class ResponseIncludeWrapper extends HttpServletResponseWrapper { /** * The names of some headers we want to capture. */ private static final String CONTENT_TYPE = "content-type"; private static final String LAST_MODIFIED = "last-modified"; private static final DateFormat RFC1123_FORMAT; private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z"; protected long lastModified = -1; private String contentType = null; /** * Our ServletOutputStream */ protected ServletOutputStream captureServletOutputStream; protected ServletOutputStream servletOutputStream; protected PrintWriter printWriter; private ServletContext context; private HttpServletRequest request; static { RFC1123_FORMAT = new SimpleDateFormat(RFC1123_PATTERN, Locale.US); RFC1123_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); } /** * Initialize our wrapper with the current HttpServletResponse and * ServletOutputStream. * * @param context The servlet context * @param request The HttpServletResponse to use * @param response The response to use * @param captureServletOutputStream The ServletOutputStream to use */ public ResponseIncludeWrapper(ServletContext context, HttpServletRequest request, HttpServletResponse response, ServletOutputStream captureServletOutputStream) { super(response); this.context = context; this.request = request; this.captureServletOutputStream = captureServletOutputStream; } /** * Flush the servletOutputStream or printWriter ( only one will be non-null ) * This must be called after a requestDispatcher.include, since we can't * assume that the included servlet flushed its stream. */ public void flushOutputStreamOrWriter() throws IOException { if (servletOutputStream != null) { servletOutputStream.flush(); } if (printWriter != null) { printWriter.flush(); } } /** * Return a printwriter, throws and exception if a OutputStream already * been returned. * * @return a PrintWriter object * @exception java.io.IOException * if the outputstream already been called */ @Override public PrintWriter getWriter() throws java.io.IOException { if (servletOutputStream == null) { if (printWriter == null) { setCharacterEncoding(getCharacterEncoding()); printWriter = new PrintWriter( new OutputStreamWriter(captureServletOutputStream, getCharacterEncoding())); } return printWriter; } throw new IllegalStateException(); } /** * Return a OutputStream, throws and exception if a printwriter already * been returned. * * @return a OutputStream object * @exception java.io.IOException * if the printwriter already been called */ @Override public ServletOutputStream getOutputStream() throws java.io.IOException { if (printWriter == null) { if (servletOutputStream == null) { servletOutputStream = captureServletOutputStream; } return servletOutputStream; } throw new IllegalStateException(); } /** * Returns the value of the <code>last-modified</code> header field. The * result is the number of milliseconds since January 1, 1970 GMT. * * @return the date the resource referenced by this * <code>ResponseIncludeWrapper</code> was last modified, or -1 if not * known. */ public long getLastModified() { if (lastModified == -1) { // javadocs say to return -1 if date not known, if you want another // default, put it here return -1; } return lastModified; } /** * Sets the value of the <code>last-modified</code> header field. * * @param lastModified The number of milliseconds since January 1, 1970 GMT. */ public void setLastModified(long lastModified) { this.lastModified = lastModified; ((HttpServletResponse) getResponse()).setDateHeader(LAST_MODIFIED, lastModified); } /** * Returns the value of the <code>content-type</code> header field. * * @return the content type of the resource referenced by this * <code>ResponseIncludeWrapper</code>, or <code>null</code> if not known. */ @Override public String getContentType() { if (contentType == null) { String url = request.getRequestURI(); String mime = context.getMimeType(url); if (mime != null) { setContentType(mime); } else { // return a safe value setContentType("application/x-octet-stream"); } } return contentType; } /** * Sets the value of the <code>content-type</code> header field. * * @param mime a mime type */ @Override public void setContentType(String mime) { contentType = mime; if (contentType != null) { getResponse().setContentType(contentType); } } @Override public void addDateHeader(String name, long value) { super.addDateHeader(name, value); String lname = name.toLowerCase(Locale.ENGLISH); if (lname.equals(LAST_MODIFIED)) { lastModified = value; } } @Override public void addHeader(String name, String value) { super.addHeader(name, value); String lname = name.toLowerCase(Locale.ENGLISH); if (lname.equals(LAST_MODIFIED)) { try { synchronized(RFC1123_FORMAT) { lastModified = RFC1123_FORMAT.parse(value).getTime(); } } catch (Throwable ignore) { ExceptionUtils.handleThrowable(ignore); } } else if (lname.equals(CONTENT_TYPE)) { contentType = value; } } @Override public void setDateHeader(String name, long value) { super.setDateHeader(name, value); String lname = name.toLowerCase(Locale.ENGLISH); if (lname.equals(LAST_MODIFIED)) { lastModified = value; } } @Override public void setHeader(String name, String value) { super.setHeader(name, value); String lname = name.toLowerCase(Locale.ENGLISH); if (lname.equals(LAST_MODIFIED)) { try { synchronized(RFC1123_FORMAT) { lastModified = RFC1123_FORMAT.parse(value).getTime(); } } catch (Throwable ignore) { ExceptionUtils.handleThrowable(ignore); } } else if (lname.equals(CONTENT_TYPE)) { contentType = value; } } }
相关文章推荐
- OC---Math公式
- [AS3]移动设备上的触控事件和手势
- 22. 存储过程时间格式验证(13:30:00)
- Android布局中Padding与Margin的区别
- 虚拟机部署度量快速开发平台服务端出现ora-27101错误的一个解决方法
- ffmpeg中av_seek_frame使用样例(代码实现)
- Kb,KB,kbps
- 2015年11月24日 每天半小时学英语
- 如何在Swift 中使用AFNetworking
- 在web.config和IIS中设置Session过期时间
- 传智播客C++进驻广州羊城
- 2. Magento2 --- (2) theme --- structure
- 11.25总结及下月安排
- [转载]互联网Offer选择恐惧症
- CATransform3D 矩阵变换之立方体旋转实现细节
- c# 替换非法字符
- javascript数据结构之双链表插入排序实例详解
- 页面上点击事件页面重新刷新
- NSURLSession的网络请求类
- 【实例教程】你会用swift创建复杂的加载动画吗