为什么要学习Tomcat源码?学习源码的目的是什么?
学习Tomcat源码属于理论学习,实践使用属于技能练习,长时间使用和处理使用问题属于经验积累。
1、掌握Tomcat原理理论,
可以进行调优:在各种场景下,合理的配置使用Tomcat,最大限度的发挥Tomcat的性能。
可以解决问题:根据原理来解决使用过程中遇到的疑难杂症。
2、代码学习
学习大神们如何写简洁、高效、优雅的代码;
学习大神们如何设计代码,比如设计模式;
一、环境信息
TOMCAT版本:apache-tomcat-8.5.54-src.zip
JDK版本:jdk1.8.0_171
ANT版本:apache-ant-1.10.7-bin.zip
IDE:eclipse Oxygen.3a Release (4.7.3a)
操作系统:win7_X64
二、搭建步骤
1、下载tomcat、jdk、ant、eclipse,官网下载即可(略).
2、解压安装配置JDK(略).
3、解压eclipse(略).
4、安装ant,tomcat默认使用ant构建,有些博客说是tomcat和ant是同一个人开发的有关。
(1)解压安装
我的安装目录是:D:apache-ant-1.10.7
(2)配置环境变量
ANT_HOME D:apache-ant-1.10.7
path后面追加
;%ANT_HOME%/bin
(3)打开CMD验证是否成功
C:UsersAdministrator>ant -version Apache Ant(TM) version 1.10.7 compiled on September 1 2019
5、构建tomcat源码
(1)解压tomcat源码
我的解压目录是:E:srcsapache-tomcat-8.5.54-src
(2)进入解压目录,新建一个build.properties文件和目录tomcat-build-libs,添加如下内容:
# basepath是下载的jar包的路径 base.path=E:/srcs/apache-tomcat-8.5.54-src/tomcat-build-libs compile.source=1.8 compile.target=1.8 compile.debug=true
(3)使用ant下载依赖包
打开cmd,进入E:srcsapache-tomcat-8.5.54-src,输入ant,在tomcat-build-libs目录中下载依赖包,最后出现BUILD SUCCSSFUL即可。
E:srcsapache-tomcat-8.5.54-src>ant ... BUILD SUCCESSFUL Total time: 1 minute 46 seconds
(4)转换eclipse工程
(4.1)生成ide-eclipse
E:srcsapache-tomcat-8.5.54-src>ant -p
(4.2)源码目录下生成了.classpath 和.project文件
E:srcsapache-tomcat-8.5.54-src>ant ide-eclipse
下载所有依赖包:
6、导入eclipse
(6.1)方式一:打开eclipse,File->import->General->existing Projects into workspace,找到源码录入,然后finish即可。
(6.2)方式二:方式一导入会引入很多没用的文件,可以换一种方式:
新建java project工程,删掉src目录,然后File->import->General->File System导入如下目录
7、解决工程报错
(1)添加JUnit4单元测试。
(2)添加依赖:
对于方式一 添加ANT_HOME和TOMCAT_LIBS_BASE这两个变量:在.classpath文件里配置了依赖
对于方式二:直接到ant目录和tomcat-build-libs目录拷贝依赖jar包,然后直接build path:
3、缺少CookieFilter
/tomcat8.5/test/util/TestCookieFilter.java报错,缺少CookieFilter.java依赖
新建类CookieFilter.java来解决:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package util; import java.util.Locale; import java.util.StringTokenizer; /** * Processes a cookie header and attempts to obfuscate any cookie values that * represent session IDs from other web applications. Since session cookie names * are configurable, as are session ID lengths, this filter is not expected to * be 100% effective. * * It is required that the examples web application is removed in security * conscious environments as documented in the Security How-To. This filter is * intended to reduce the impact of failing to follow that advice. A failure by * this filter to obfuscate a session ID or similar value is not a security * vulnerability. In such instances the vulnerability is the failure to remove * the examples web application. */ public class CookieFilter { private static final String OBFUSCATED = "[obfuscated]"; private CookieFilter() { // Hide default constructor } public static String filter(String cookieHeader, String sessionId) { StringBuilder sb = new StringBuilder(cookieHeader.length()); // Cookie name value pairs are ';' separated. // Session IDs don't use ; in the value so don't worry about quoted // values that contain ; StringTokenizer st = new StringTokenizer(cookieHeader, ";"); boolean first = true; while (st.hasMoreTokens()) { if (first) { first = false; } else { sb.append(';'); } sb.append(filterNameValuePair(st.nextToken(), sessionId)); } return sb.toString(); } private static String filterNameValuePair(String input, String sessionId) { int i = input.indexOf('='); if (i == -1) { return input; } String name = input.substring(0, i); String value = input.substring(i + 1, input.length()); return name + "=" + filter(name, value, sessionId); } public static String filter(String cookieName, String cookieValue, String sessionId) { if (cookieName.toLowerCase(Locale.ENGLISH).contains("jsessionid") && (sessionId == null || !cookieValue.contains(sessionId))) { cookieValue = OBFUSCATED; } return cookieValue; } }
8、启动
java/org/apache/catalina/startup/Bootstrap.java 里面有main函数,启动这个类即可。
问题:控制台打印乱码。
原因:在java中, 读取文件的默认格式是iso8859-1, 而我们中文存储的时候一般是UTF-8. 所以导致读出来的是乱码
解决:修改两个类
1)org.apache.tomcat.util.res.StringManager类中的getString(final String key, final Object... args)方法
public String getString(final String key, final Object... args) { String value = getString(key); if (value == null) { value = key; } //解决乱码 try { value = new String(value.getBytes("ISO-8859-1"), "UTF-8"); }catch(Exception e){ e.printStackTrace(); } MessageFormat mf = new MessageFormat(value); mf.setLocale(locale); return mf.format(args, new StringBuffer(), null).toString(); }
2)org.apache.jasper.compiler.Localizer类的getMessage(String errCode)方法
public static String getMessage(String errCode) { String errMsg = errCode; try { if (bundle != null) { errMsg = bundle.getString(errCode); } } catch (MissingResourceException e) { } //解决乱码 try { errMsg = new String(errMsg.getBytes("ISO-8859-1"), "UTF-8"); }catch(Exception e){ e.printStackTrace(); } return errMsg; }
改完 重启:
参考: