我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

Tomcat剖析(三):连接器2

这一节补充上一节未涉及的当我们要获取动态参数时Tomcat的处理。

这部分的处理放在了HttpRequest.java中。

因为只有我们发出请求且从未解析过时才会去读取参数,所以ex03.pyrmont.connector.http.HttpRequest中的getParameter,getParameterMap, getParameterNames 或者 getParameterValues 四个读取参数的方法开头都调用了 parseParameter 方法。如果已经解析过了(参数在请求内容里边被找到的话,),参数解析将会使得 SocketInputStream 到达字节流的尾部。类 HttpRequest 使用一个布尔变量 parsed 来指示是否已经解析过了。

下面看看parseParameter方法

 

 1     protected void parseParameters() {
 2         if (parsed)   //如果已经解析过了,直接返回
 3             return;
 4         ParameterMap results = parameters;
 5         if (results == null)//ParameterMap本文下面讲解
 6             results = new ParameterMap();
 7         results.setLocked(false);  //打开 parameterMap的锁以便写值。
 8         String encoding = getCharacterEncoding();
 9         if (encoding == null)//检查字符编码,并在字符编码为 null的时候赋予默认字符编码。
10             encoding = 'ISO-8859-1';
11 
12         //getQueryString方法在上一节解析了请求头时,如果URL如果有查询参数有设置。
13         //尝试解析查询字符串。解析参数是使用org.apache.Catalina.util.RequestUtil的 parseParameters方法来处理的。
14         //如果queryString为空(URL中没有参数),下面parseParameters方法的的解析直接返回
15         String queryString = getQueryString();
16         try {
17             RequestUtil.parseParameters(results, queryString, encoding);
18         } catch (UnsupportedEncodingException e) {
19             ;
20         }
21 
22         //获取内容类型
23         String contentType = getContentType();
24         if (contentType == null)
25             contentType = '';
26         int semicolon = contentType.indexOf(';');
27         if (semicolon >= 0) {
28             contentType = contentType.substring(0, semicolon).trim();
29         } else {
30             contentType = contentType.trim();
31         }
32         //请求方式是POST(内容长度大于零)且内容类型是 application/x-www-form-urlencoded
33         //同样用parseParameters解析POST中的内容
34         if ('POST'.equals(getMethod()) && (getContentLength() > 0)
35                 && 'application/x-www-form-urlencoded'.equals(contentType)) {
36             try {
37                 int max = getContentLength();
38                 int len = 0;
39                 byte buf[] = new byte[getContentLength()];
40                 ServletInputStream is = getInputStream();
41                 while (len < max) {
42                     int next = is.read(buf, len, max - len);
43                     if (next < 0) {
44                         break;
45                     }
46                     len += next;
47                 }
48                 is.close();
49                 if (len < max) {
50                     throw new RuntimeException('Content length mismatch');
51                 }
52                 RequestUtil.parseParameters(results, buf, encoding);
53             } catch (UnsupportedEncodingException ue) {
54                 ;
55             } catch (IOException e) {
56                 throw new RuntimeException('Content read fail');
57             }
58         }
59 
60         //锁定 ParameterMap表示不可修改参数
61         //设置 parsed为 true表示已经解析过了,
62         results.setLocked(true);
63         parsed = true;
64         parameters = results;
65     }

 

补充说明:

获取的参数可以在查询字符串或者请求内容里边找到。假如用户使用 GET 方法来请求 servlet 的话,所有的参数将在查询字符串里边出现。假如使用 POST 方法的话,你可以在请求内容中找到一些。这就是为什么会处理两次解析的原因

ParameterMap.java
参数所有的名/值对将会存储在一个HashMap 里边。 Servlet 程序员可以以 Map 的形式获得参数(通过调用 HttpServletRequest 的 getParameterMap 方法)和参数名/值,但不允许修改参数值。因此将使用一个特殊的HashMap---org.apache.catalina.util.ParameterMap。

ParameterMap 继承了 java.util.HashMap,所以许多方法都是用super关键字直接调用HashMap中的方法

那又是如何保证参数不被修改呢?Tomcat在ParameterMap中加入布尔变量 locked当 locked 是false 的时候,名/值对仅仅可以添加,更新或者移除。否则,lock为true时,异常 IllegalStateException 会抛出,结合parseParameters方法可以更加清晰了解。所以在put时做了些许的修改(对错误处理的类StringManager上一节说过了。)

 public Object put(Object key, Object value) {

        if (locked)
            throw new IllegalStateException
                (sm.getString('parameterMap.locked'));
        return (super.put(key, value));
  }

 

因为Tomcat4时还没有泛型,所以没有使用泛型,同时也没有继承效率更高的LinkedHashMap

Tomcat7中是这样的(小片段)

public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
   
     public V put(K key, V value) {

        if (locked)
            throw new IllegalStateException
                (sm.getString('parameterMap.locked'));
        return (super.put(key, value));

    }
}

 关于Tomcat4中连接器就简单说到这,下一节中将讲解Tomcat4默认的连接器,是难点。

 

 补充ResourceBundle国际化的使用(结合上一节的StringManager.java理解更好):

 

 1 package org.bundle;
 2 
 3 import java.util.Locale;
 4 import java.util.ResourceBundle;
 5 
 6 public class TestResourceBundle {
 7     
 8     public static void main(String[] args) {
 9         
10         Locale locale1 = new Locale('zh', 'CN');
11         ResourceBundle resb1 = ResourceBundle.getBundle('org.bundle.myres', locale1);
12         System.out.println(resb1.getString('login'));
13     
14         Locale locale3 = new Locale('en', 'US');
15         ResourceBundle resb3 = ResourceBundle.getBundle('org.bundle.myres', locale3);
16         System.out.println(resb3.getString('login'));
17         
18         ResourceBundle resb2 = ResourceBundle.getBundle('org.bundle.myres');//按
19         System.out.println(resb2.getString('login'));
20     }
21 }

myres_en_US.properties和myres.properties内容

login=login

myres_zh_CN.properties内容:后面表示“请登录'中文的UTF-8编码

login=请登录

读取的文件命名有规范: 自定义名_语言代码_国别代码.properties,

对于ResourceBundle而言,需要加上完整包名,getBundle个参数就是完整包名+自定义名

而语言代码和国别代码来自Locale中。

输出结果

请登录
login
请登录

可以看到,如果没有指定Locale,使用的是系统所在区域和语言。

 

如果我们以后在一个很大型的项目中有许多的类需要处理和管理大量的又必要的信息时(不仅仅国际化和错误处理)

你能联想到Tomcat是如何管理错误信息提示的?

我们可以通过包内单例模式,包之间实例保存在Map中。

这样既实现了有效的管理,又节省了内存消耗。

所以说学习Tomcat中最重要的是学习思想。

 

 如果觉得写得不错的话就推荐一下。

 

附:

相应代码可以在我的github上找到下载,拷贝到eclipse,然后打开对应包的代码即可。

https://github.com/zebinlin/Tomcat4-src

如发现编译错误,可能是由于jdk不同版本对编译的要求不同导致的,可以不管,供学习研究使用。

 

我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为1000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版百度优化、名注册、主机空间、手机网站建设公众号开发小程序制作、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线: 13820372851,我们会详细为你一一解答你心中的疑难。项目经理在线

我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

在线客服
联系方式

热线电话

13820372851

上班时间

周一到周五

公司电话

022-26262675

二维码
线
在线留言