Tomcat是如何处理请求参数的?

最近后台有朋友问问题,种类较多,后面我会选比较有针对性的深入源码分析下。

本次以GET请求为例,来分析下Tomcat对请求参数的处理方式。

说起请求参数,首先我们得了解下,请求参数的定义。我们一般的请求形式类似下面的样子:

http://hostName:port/contextName/query?parameName1=paramValue1&parameName2=paramValue2

参照上面的请求格式,url请求中带参数的形式(即我们常说的GET请求),是在请求目标后以问号开始,后面是参数名值对,多个名值对间以和号(&)分隔。

以下是HTTP规范RFC2616中对此的定义:


通过URL传递的参数,在Tomcat中是怎么解析出来的呢?

我们一般在Servlet中要获取某个参数,一般通过如下的方式

String value = request.getParameter("paramName");

我们在需要的时候通过参数名直接取,这个值又是什么设置的?名值对又是如何对应起来的?

我们顺着getParameter方法这个藤,来摸摸实现这个瓜。

我们在使用HttpServletRequest这个对象时,其实一直在使用的是其一个门面对象(RequestFacade),此对象使用了设计模式中的门面模式,封装了HttpServletRequest中的一些细节,只暴露出一些必要的API。

实际请求处理时,则调用其封装的request对象。
getParameter方法的代码是下面这个样子:

/ * Return the value of the specified request parameter, if any; otherwise,

* return <code>null</code>. If there is more than one value defined,

* return only the first one.

* @param name Name of the desired request parameter

*/

public String getParameter(String name) {

if (!parametersParsed) {

parseParameters();

}

return coyoteRequest.getParameters().getParameter(name);

}

每次请求时,会先判断参数是否已经解析过,如果已经解析过就直接返回。

protected void parseParameters() {

parametersParsed = true; //注意这里,解析之后就设为true了。

Parameters parameters = coyoteRequest.getParameters();

boolean success = false;

try {

// Set this every time in case limit has been changed via JMX

parameters.setLimit(getConnector().getMaxParameterCount());

}

...

parameters.handleQueryParameters();

}

所以,这个名值对的配置,初始化,是发生在第一次调用getParameter方法时。

再向下,这个handleQueryParameters是具体处理的方法。这里我们假设请求如下url:

http://localhost:8080/test?abc=1&def=2

在handleQueryParameters方法中,我们通过debug界面观察到如下内容:



此处parameters包含一个属性queryMB,其值刚好是我们传进来的字符串。所以后面的参数处理,是基于这个属性进行的。

再之后,在Parameter这个类的processParameter方法中,我们看到的是这样样子的处理方式:


我们看到,基本是遍历字符串中的各个char,遇到特定字符=和&之后,再从各个index获取等号前后的名和值

中间特别的一个地方是,遇到%和+时,是出现了像汉字一类的,其实是需要转义的,所以处理也是在此进行的

解析后,名值对是存放在ArrayList这样一个数据结构中。看下面的代码,

public void addParameter( String key, String value ) {

ArrayList<String> values = paramHashValues.get(key);

if (values == null) {

values = new ArrayList<>(1);

paramHashValues.put(key, values);

}

values.add(value);

}

是执行完上面的方法后,代码向下执行,看到的parameters这个对象,值已经变成了这样:

abc=1,\n def=2,\n

注意上面代码标红加粗的这两行,

你是否还记得上面提到,如果多个参数,对于重名的只返回第一个符合的项这件事?

具体request的参数请求中,如果不涉及初次处理,那执行的是下面的代码,很简单,就是直接从Map里取对应key的ArrayList,有值的话就从中取第一个值。

public String getParameter(String name ) {

handleQueryParameters();

ArrayList<String> values = paramHashValues.get(name);

if (values != null) {

if(values.size() == 0) {

return "";

}

return values.get(0); //注意这里,就是在兑现只返回第一个的承诺!!!

} else {

return null;

}

}

和我们直观理解上基本一致,我们深入代码,更多的是一起看一些细节上的东西,比如重名时返回第一项,比如实现过程中使用的数据结构等。

将本号推荐给你更多的朋友,大家一起交流。

无兄弟,不Coding嘛!

如果感觉有用,请分享到朋友圈,让更多人看到。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值