用Intellij+Maven搭建Tomcat源码开发环境步骤

1 篇文章 0 订阅
1 篇文章 0 订阅

编程的实践性非常强,光看不练事倍功半,就像设计模式,除了看各种书籍记下23种设计模式,还需要在实际工作中非常“刻意”的去运用,才会加深理解,否则根本不知道为何要诞生某一种设计模式。


对于成熟的开源框架、容器的学习,除了看源码之外,在实践上似乎无从下手——这些家伙太成熟了,封装的很好,大多时候我们只是用他们的接口和注解,知道怎么配置就可以了;不少的人在工作中也很少对框架进行二次开发和扩展;想要对这些成功项目贡献commit,除非对项目已经深刻了解,否则无从下手。可是光看源码也很难做到深刻理解,比如spring最基本的BeanFactory类图,照着各种源码书走几遍,可能印象依旧不深,因为没有实践。


个人觉得如果能用源码在IDE里搭建起开发环境,通过断点反复跟踪程序的流转并监视变量值,能够有效加强对源码的理解,也是源码学习的一种实践方式。


本文讲如何搭建Tomcat源码的开发环境,以Tomcat 8.5.23为例,其他版本操作类似。网上有不少教程讲这方面,但是搭建后还是跑不起来,教程忽略了一些细节。本文参照http://www.jb51.net/article/95120.htm,并修正了一些地方,更加细化步骤。


先说下开发环境:Tomcat项目是用Ant构建的,笔者不熟悉Ant,采用Maven,其实就是在源码根目录里加个pom.xml文件的事。很多教程用的Eclipse搭建开发环境,笔者用Intellij:这两个IDE我都用过,各有优劣,但是对于源码学习,推荐用Intellij——对新手来说,IDE导入各种源码工程随之而来的就是满屏报错(其中以项目配置、依赖缺失为主),根本无从下手,积极性会受到很大打击。这方面感觉Intellij提供的信息更有效,提示的解决方案更有针对性,换句话说用Intellij更有安全感,会有一种一定能调通的信心;另外Intellij自带反编译和源码下载功能,我们在断点调试时,如果进入依赖包的文件(字节码),Intellij能够直接提供反编译后的文件继续单步执行,并且提供下载源码的选择,调试起来非常方便;而Eclipse则需要下载插件(Eclipse的插件下载安装不一定成功并能用哦),所以网上有人说Eclipse相比Intellij,没用的装一大堆,有用的都要自己弄。IDE只是个工具,只要能替我们完成功能,哪个方便自然用哪个。说个题外话,我觉得新手从接触编程到上岗干活,有两个基本拦路虎——编程阶段的指针、内存管理(别说Java没有指针,值传递、不可变对象的Collection类型传参能唬退不少初学者);项目阶段的开发环境搭建,尤其是各种依赖包的引用、管理和项目配置,初学者基本上连怎么在google里输入问题描述都感到难,搞了一个星期,别说要解决的问题,连环境都搭不起来,尤其对没有项目经验的自学者,很是挫败。当然后者在Java里随着项目构建工具和SpringBoot/Cloud的发展,以及网络资料的丰富,愈发简单。


下面介绍具体搭建步骤:


1.下载Tomcat源码包以及(同版本)软件包

可以到http://tomcat.apache.org/官网直接下载,以8.5.23版为例,源码下载Source Code Distributions,软件包下载Core就行,其实软件包只是为了启动源码项目时对应catalina.home,所以windows平台下载zip就好,没有必要下载installer进行安装。如图1示:


图1


如果想要对tomcat贡献commit,可以直接从github(https://github.com/apache/tomcat.git)上下载源码,不过对应的软件包要从官网上下最新的beta版了(目前是9.0.1)


2.生成Maven工程

分别解压源码包和软件包,解压后两者具体的位置无关紧要,笔者将其放于一个目录下,如图2所示:


图2


然后进入源码文件夹,新建pom.xml文件,针对8.5.23版的pom文件内容如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>Tomcat8.5</artifactId>
    <name>Tomcat8.5</name>
    <version>8.5.23</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.10.1</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt</groupId>
            <artifactId>org.eclipse.jdt.core</artifactId>
            <version>3.13.0</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>Tomcat8.5</finalName>
        <sourceDirectory>java</sourceDirectory>
        <testSourceDirectory>test</testSourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <directory>test</directory>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

不同版本的Tomcat依赖包不同,pom里面的dependency也会有变化,可以具体情况具体分析。文章http://www.jb51.net/article/95120.htm的pom里面引用的两个dependency:

<dependency>
    <groupId>ant</groupId>
    <artifactId>ant</artifactId>
    <version>1.7.0</version>
</dependency>

<dependency>
    <groupId>org.eclipse.jdt.core.compiler</groupId>
    <artifactId>ecj</artifactId>
    <version>4.5.1</version>
</dependency>

其中ant包已经被迁移到org.apache.ant,如果采用ant包maven会下载不到jar。本地maven仓库里的ant并没有下载到jar包,如图3所示:


图3


这里说个题外话,maven项目导入IDE时,会出现项目构建失败的情况,很多情况下提示类找不到,就有可能是图3所示的maven的依赖包下载失败,有可能是artifact已经迁移,也有可能是网络问题。如果是后者,我们要把maven仓库里artifactId对应的文件夹整个删掉(比如图3所代表的文件夹),然后reimport整个maven项目。有时候更换artfactId的version也有效果。


不管如何,导入一个项目失败,多多根据问题提示(尤其是错误类所处的package信息)到maven仓库http://mvnrepository.com/去查找,能解决很多问题。


而参考文只使用的ecj包,会导致org.eclipse.jdt里面其他包的类无法查询,报错信息如图4所示:


图4


所以我们使用整个org.eclipse.jdt的core包。此外有教程说要把build.properties.default文件的文件名改为build.properties才能构建项目成功,我们这里是maven项目,没有必要修改,保持不动。要是用Ant构建才需要变更文件名。


3.(可选)生成git库

如果是从官网下载的源码包,想要用git在本地管理源码,可以源码文件夹下git init建立git仓库,官网源码包里的gitignore文件里面的内容已经比较完善,但是还需要再加一行,内容是target/,用来忽略maven输出的class文件。


4.导入项目至IDE

打开Intellij—>Import Project—>选择导入Maven项目—>文件夹定位于源码文件夹—>导入。

导入后直接build项目会报错,如图5所示:


图5


因为我使用的pom包含了test工程,而其中的类TestCookieFilter使用的CookieFilter类找不到,解决办法就是把整个TestCookieFilter给注释掉好了,研究源码的who会care单元测试的内容。


5.配置启动参数

至此,项目导入后的所有报错信息已经解决,现在要配置程序入口和启动参数。Tomcat的程序入口在org.apache.catalina.startup.Bootstrap类里面,从中我们可以活捉main函数(熟悉的味道),但是我们如果直接在这个类右键然后run/debug Bootstrap.main()的话会报错,查看Bootstrap.java源码,其static代码块如下所示:

static {
	// Will always be non-null
	String userDir = System.getProperty("user.dir");

	// Home first
	String home = System.getProperty(Globals.CATALINA_HOME_PROP);
	File homeFile = null;

	if (home != null) {
		File f = new File(home);
		try {
			homeFile = f.getCanonicalFile();
		} catch (IOException ioe) {
			homeFile = f.getAbsoluteFile();
		}
	}

	if (homeFile == null) {
		// First fall-back. See if current directory is a bin directory
		// in a normal Tomcat install
		File bootstrapJar = new File(userDir, "bootstrap.jar");

		if (bootstrapJar.exists()) {
			File f = new File(userDir, "..");
			try {
				homeFile = f.getCanonicalFile();
			} catch (IOException ioe) {
				homeFile = f.getAbsoluteFile();
			}
		}
	}

	if (homeFile == null) {
		// Second fall-back. Use current directory
		File f = new File(userDir);
		try {
			homeFile = f.getCanonicalFile();
		} catch (IOException ioe) {
			homeFile = f.getAbsoluteFile();
		}
	}

	catalinaHomeFile = homeFile;
	System.setProperty(
			Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

	// Then base
	String base = System.getProperty(Globals.CATALINA_BASE_PROP);
	if (base == null) {
		catalinaBaseFile = catalinaHomeFile;
	} else {
		File baseFile = new File(base);
		try {
			baseFile = baseFile.getCanonicalFile();
		} catch (IOException ioe) {
			baseFile = baseFile.getAbsoluteFile();
		}
		catalinaBaseFile = baseFile;
	}
	System.setProperty(
			Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
}

代码细节不详述,这个静态代码块有个主要的逻辑是JVM加载Bootstrap.class时,会去读取系统设置的catalina.home变量代表的路径作为Tomcat安装路径。如果项目启动时没有设置catalina.home,会把当前路径(即项目所处的路径,对于我们就是tomcat源码文件夹)作为tomcat的安装路径(如果同时没有设置tomcat的工作路径,即catalina.base,那么就会把catalina.base设置为catalina.home,也就是当前路径。home和base的区别,大家可以另行查找文章,都解释的很清楚)


显然我们的源码路径肯定不是tomcat的安装路径。因为源码文件夹里没有tomcat启动需要的lib/,以及其他配置(conf/、logs/、temp/、webapps/、work/等,这是与catalina.base相关联的)是否和软件版一致。这就是为什么我们在下载源码的同时需要下载tomcat软件包,就是用来给我们启动源码项目时指定catalina.home(顺带间接指定catalina.base)。


配置启动参数如图6、7、8所示:


图6


图7


图8


然后在新建的启动配置下run或者debug,如图9所示:


图9


控制台显示启动,如图10所示:


图10


打开浏览器,输入127.0.0.1:8080,显示tomcat欢迎界面,如图11所示:


图11


大功告成,接下来可以畅游tomcat源码了。配置好的Tomcat源码开发环境,已经上传至我的Github:https://github.com/FlowerL/tomcat8.5-src-dev.git。可以直接下载导入Intellij。


源码之下,一切皆无秘密!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值