您的位置:首页 > 其它

servlet中Response输出源码解析

2015-11-25 15:35 330 查看
在Servlet编程中 经常会写

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;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: