概述
如果我们将request
传递到异步线程中使用,可能获取不到参数,并且会导致后续的请求,使用到这个线程也会出问题。
原因就是request
对象会被重复使用。
源码分析
1、获取参数
先看一个非常重要的方法,getParameter
方法调用第一次,会进行参数的解析,之后就不再解析。
package org.apache.catalina.connector;
public class Request implements HttpServletRequest {
/**
* 请求参数解析标志(默认为false)
*/
protected boolean parametersParsed = false;
@Override
public String getParameter(String name) {
if (!parametersParsed) { //判断是否已经解析参数,如果没解析,则进行解析。
parseParameters();
}
return coyoteRequest.getParameters().getParameter(name);
}
}
2、解析参数
真正解析参数的是 parameters.handleQueryParameters();
这个方法。
/**
* 解析请求参数
*/
protected void parseParameters() {
parametersParsed = true;
Parameters parameters = coyoteRequest.getParameters();
boolean success = false;
try {
......
parameters.handleQueryParameters(); //真正解析参数的方法
......
success = true;
} finally {
if (!success) {
parameters.setParseFailedReason(FailReason.UNKNOWN);
}
}
}
3、将查询字符串处理为参数
/**
* 将查询字符串处理为参数
*/
public void handleQueryParameters() {
if (didQueryParameters) { //判断是否已经处理过了
return;
}
didQueryParameters = true;
......
processParameters(decodedQuery, queryStringCharset);
}
4、添加参数到 paramHashValues
程序执行到这里会将参数加入到 paramHashValues
这个map
中,获取参数时,就是直接从这个map
里获取的。
private final Map<String,ArrayList<String>> paramHashValues =
new LinkedHashMap<>();
public void addParameter( String key, String value )
throws IllegalStateException {
if( key==null ) {
return;
}
parameterCount ++;
if (limit > -1 && parameterCount > limit) {
// Processing this parameter will push us over the limit. ISE is
// what Request.parseParts() uses for requests that are too big
setParseFailedReason(FailReason.TOO_MANY_PARAMETERS);
throw new IllegalStateException(sm.getString(
"parameters.maxCountFail", Integer.valueOf(limit)));
}
ArrayList<String> values = paramHashValues.get(key);
if (values == null) {
values = new ArrayList<>(1);
paramHashValues.put(key, values);
}
values.add(value);
}
5、释放request
最终请求的代码会走到这个地方,request的参数会在这里被清除,然后准备给下个请求进行复用。
方法叫做 recycle,表明是循环再利用,在这里面会把存放参数的 Map 清空,把 didQueryParameters 再次设置为了 false。
public void recycle() {
parameterCount = 0;
paramHashValues.clear();
didQueryParameters = false;
charset = DEFAULT_BODY_CHARSET;
decodedQuery.recycle();
parseFailedReason = null;
}
种种迹象表明 request 在 tomcat 里面是循环使用的。
参考:
https://blog.youkuaiyun.com/JavaMonsterr/article/details/126033884