本篇文章较长 重要的是思路!!!之后你看Tomcat的任何一个类的源码都可以根据这种思路去定位和trace
对于一个正在找工作的渣渣来说 习惯性的翻源码慢慢也变成了乐趣 -鲁迅
对于Tomcat的源码我觉得看懂架构非常容易 对于细节因人而异 贴一张Tomcat的架构图(百度找的)左边就是我们conf目录下很重要的server.xml文件内容,右边则是官方给的架构图 可以看到一一对应
从上图我们可以看到server.xml文件中的标签与右边的架构图的组件具有相同的相对位置 而在源码中他们更是具有相同的关系
上面是我引入了EmbddedTomcat的依赖 即嵌入式Tomcat,之后编写的启动Tomcat的代码 我们可以看到其中的元素与我们在架构图上看到的相对关系完全一致 具体细节可以参照下列图(自己翻一遍最好)
通过上面的示例我们可以得到这样一个结论 Tomcat的设计符合是jndi规范的 然后回归问题 如下图 一位同学在群里提出了这样一个问题
然后定位下问题所在 这个文件存在于Tomcat下载目录的logs文件夹下 而logs文件夹下为什么有这些日志文件那 我们可以观察server.xml文件 如下图 对的 就是你看到的那个位置 这里定义了accessLog的位置以及输出格式 可以发现pattern里面的正则与提出问题的输出语句格式相同
因此我们可以根据Tomcat设计的特性来从代码中找到AccessLogValve这个类 查看他的pattern的解释 但是从这个类中并没有pattern属性 于是我们可以去父类中找
定位到这条属性后 我们可以观察他的get set方法(思路) 然后根据server.xml中带的提示我们判断是进入common的代码块中 然后我们就可以去定位解析pattern的代码
protected AccessLogElement createAccessLogElement(char pattern) {
switch (pattern) {
case 'a':
return new RemoteAddrElement();
case 'A':
return new LocalAddrElement();
case 'b':
return new ByteSentElement(true);
case 'B':
return new ByteSentElement(false);
case 'D':
return new ElapsedTimeElement(true);
case 'F':
return new FirstByteTimeElement();
case 'h':
return new HostElement();
case 'H':
return new ProtocolElement();
case 'l':
return new LogicalUserNameElement();
case 'm':
return new MethodElement();
case 'p':
return new PortElement();
case 'q':
return new QueryElement();
case 'r':
return new RequestElement();
case 's':
return new HttpStatusCodeElement();
case 'S':
return new SessionIdElement();
case 't':
return new DateAndTimeElement();
case 'T':
return new ElapsedTimeElement(false);
case 'u':
return new UserElement();
case 'U':
return new RequestURIElement();
case 'v':
return new LocalServerNameElement();
case 'I':
return new ThreadNameElement();
case 'X':
return new ConnectionStatusElement();
default:
return new StringElement("???" + pattern + "???");
}
}