在Java Web编程中,设置Content-TypeContent-Length头部是再经常不过的操作了,但是HttpServletResponse提供了两个相关的方法来设置头部,一个是void setHeader(String name, String value),一个是void setContentType(String type)或者void setContentLength(int len)。这两者之间有什么区别吗?之前一直分不清楚,也在代码中看到两种方式都有被人使用,甚至是两个一起写的。

我们通过阅读Tomcat相关函数的代码,来看看这两者具体有什么区别。

javax.servlet.http.HttpServletResponse#setHeader的实现是org.apache.catalina.connector.ResponseFacade#setHeader,内部会调用org.apache.catalina.connector.Response#setHeader,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void setHeader(String name, String value) {

if (name == null || name.length() == 0 || value == null) {
return;
}

if (isCommitted()) {
return;
}

// Ignore any call from an included servlet
if (included) {
return;
}

//
char cc=name.charAt(0);
if (cc=='C' || cc=='c') {
if (checkSpecialHeader(name, value))
return;
}

getCoyoteResponse().setHeader(name, value);
}

setHeader函数中,如果发现设置的头部已c或者C开头,会调用checkSpecialHeader

1
2
3
4
5
6
7
8
// org.apache.catalina.connector.Response#checkSpecialHeader
private boolean checkSpecialHeader(String name, String value) {
if (name.equalsIgnoreCase("Content-Type")) {
setContentType(value);
return true;
}
return false;
}

setHeader函数中,为了性能,只校验第一个字母,在checkSpecialHeader方法中进行完整的判断,如果header的名字是不区分大小写的Content-Type,则调用setContentType函数来设置,然后直接返回。到这里我们可以知道,使用setHeader设置头部时,如果设置Content-Type,其实内部使用的是setContentType函数来实现。

Content-Length呢?不着急,继续往下看。在setHeader函数中,如果不是c或者C开头的情况,会执行getCoyoteResponse().setHeader(name, value)这句话,源码如下:

1
2
3
4
5
6
7
8
9
// org.apache.coyote.Response#setHeader
public void setHeader(String name, String value) {
char cc=name.charAt(0);
if( cc=='C' || cc=='c' ) {
if( checkSpecialHeader(name, value) )
return;
}
headers.setValue(name).setString( value);
}

是不是觉得眼熟,这里再一次地判断了header名称是不是以c或者C开头。但是这里的checkSpecialHeader实现是不一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// org.apache.coyote.Response#checkSpecialHeader
private boolean checkSpecialHeader( String name, String value) {
// XXX Eliminate redundant fields !!!
// ( both header and in special fields )
if( name.equalsIgnoreCase( "Content-Type" ) ) {
setContentType( value );
return true;
}
if( name.equalsIgnoreCase( "Content-Length" ) ) {
try {
long cL=Long.parseLong( value );
setContentLength( cL );
return true;
} catch( NumberFormatException ex ) {
// Do nothing - the spec doesn't have any "throws"
// and the user might know what he's doing
return false;
}
}
return false;
}

如果header的名字是不区分大小写的Content-Type,则调用setContentType函数来设置。如果header的名字是不区分大小写的Content-Length,则调用setContentLength函数来设置。

至此我们得到了结论:通过setHeader来设置Content-Type或者Content-Length头部,内部是调用setsetContentType/setContentLength来实现的。所以两者功能上没有区别。但是推荐使用setsetContentType/setContentLength因为少了多余的判断,性能更高,函数名也更明义。