本文的目的是基于Idea IDE,利用maven创建webapp,自己实现一个简单的servlet,将其运行到tomcat中。
希望透过这个过程可以理解一些关键内容:
学习前提是你懂classpath,如果不懂翻一下我之前的一篇博客。
idea如何配置阿里云的maven源头自己了解一下即可,否则maven用起来会很慢。
选择archtype是webapp,这样maven会初始化一个目录结构,符合maven制作web应用的目录规范。
所谓规范,就是我们把代码和前端资源放到指定路径,maven就帮我们打包war。
让maven自动导入pom.xml中配置的包。
maven会递归扫描src/main下面的java代码。
对于webapp来说,会规范放置一个webapp目录,里面规范有一个WEB-INF目录存放servlet配置,稍后会讲。除此之外,webapp目录下可以放置要打包发布的前端资源,这里我没用到。
pom.xml在项目根目录,用于配置maven项目。
为了开发servlet提供HTTP接口服务,需要引入包依赖。
maven会识别到新依赖,并下载。
为了项目结构好看一点,我们创建一个和webapp平行的java目录作为source root,简单的说就是作为classpath,在其下面创建的类使用相对路径作为package名字。
java目录显示蓝色是因为我右键mark as source root,这个信息仅仅对IDE和开发者有用(相当于告知IDE classpath),对maven没有用(稍后解释),仅仅是帮助IDE正确进行package和classname的索引用的。
没啥好说的,servlet就是java web开发的规范,1个servlet相当于实现一个接口。
大家可以类比python中的wsgi规范,我觉得套路都是一码事。
web.xml相当于配置路由,这个文件也是java servlet规范的一部分,稍后我们用tomcat去加载这个webapp的时候web.xml就是要被tomcat读取的。
首先要配置有哪些servlet类,因为我们把ServletDemo类放在source root下面,也就是没有包名(也叫默认包,或者说直接放在了classpath目录内),所以servlet-class就是ServletDemo,没有包名前缀即可加载到该类。
servlet-mapping配置/hello的URI请求交给ServletDemo处理,仅此而已。
通过maven package命令可以编译项目。
maven扫描src/main目录下所有.class文件,根据其.class文件中的package名,将其编译后的.class放置到了target/classes中。
另外还有一个servlet-demo目录(这是我的项目名),里面拷贝过来了WEB-INF目录(包括里面的web.xml),但是额外的还把classes目录也拷贝了进来,以及我们透过maven安装的servlet-api依赖jar包也拷贝了进来。
这个servlet-demo目录可不得了,它包含了要启动这个webapp所有的东西,包括项目class、依赖class、web.xml,其实这个目录压缩一下就是servlet-demo.war包的由来了。
war包是一个可以直接部署到tomcat的完整应用环境,我猜测war包内的结构都是遵循servlet规范的固定摆放方式,这样tomcat会将war内的classes和lib下的jar包都作为classpath,并且根据web.xml的路由加载对应的servlet class(透过Class.forName()方法)处理HTTP请求。
对于webapp来说,打包的是包含了class、web.xml、前端资源的一个war包,所以我们看pom.xml中packaging是war而不是jar。
各位去官方下载tomcat后,解压到磁盘上某个地方就行。
点击edit,找到tomcat server -> local,然后点上方的+号。
配置一下tomcat的路径:
把tomcat的目录配置进来,idea就识别了。
把war包指定为要部署的东西,并且把该应用的URL根路径设置为/。
现在点运行,idea会帮我们先maven package,再把编译出来的war包丢到tomcat下拉起。
访问http://localhost:8080/hello,可以看到我们实现的servlet的应答:
我还没仔细研究代码自动热加载,目前我是通过这个按钮重新触发代码编译和tomcat重新部署的:
在前面的部分我说过,source root仅仅是给IDE还有开发者看的,这是啥意思?
JAVA都是基于classpath定义packge路径的,package名是相对于classpath的,并且对应相同的目录结构,这里source root就是告诉IDE我把这个目录作为classpath。
但是maven并不知道这个事情,maven是扫描src/main下面所有.java文件,根据.java中的package名反推出classpath是哪一层目录,怎么证明这个事情呢?
ServletDemo类直接放在source root下,本应该属于默认包,不能写包名。
我强行给它写了一个package noexist,IDE自然会给出错误提示,因为noexist包应该对应java/noexist目录才对。
但是这并不影响这个类的编译,并且javac会将该类编译放到target/classes/noexist/目录下:
这足以证明,source root只是给IDE和人看的,maven根本无从知晓这个信息,它只是根据package去推断classpath是哪个目录。
当然,实际我们不需要考虑这么复杂。我们要做的就是在src/main下,随便找一个目录(无论多深)作为source root,然后编写的类都相对于source root指定package名就行,大家理解上述解释就不会觉得这是什么奇怪的事情,太平常了。
掌握classpath和maven的原理非常重要,其实学什么语言都差不多,先明白类是如何查找的,再明白如何使用包管理工具,也就有了继续深入学习开发框架的基础。
上述demo的地址: https://github.com/owenliang/servlet-demo
如果文章帮到了你,请您乐于扫码捐赠1元钱,以便支持服务器运转。