讨论一下项目发布的问题,因为我过去从没有认真发布过项目。
做过php的项目,在server上直接用php版本的eclipse进行开发,php的环境是早就配置好的,mysql也是事先安装好,在开发调试的过程中,都是直接使用server上的php+mysql+apache的环境,不存在项目发布的问题:开发好也就相当于部署好了。
但是,眼前这个项目不一样:
1.开发环境是虚拟机、生产环境是一台专门的server
2.开发环境和运行环境都是java语言环境下,eclipse开发的maven项目,运行在tomcat上
目前的情况是:
开发环境中,eclipse上直接对代码run on server的时候没有问题;
开发环境中,tomcat上直接运行,有问题,跑不起来;
生产环境,还没有部署过;
我作为一个发布小白,我想问:
1.不管是开发环境还是生产环境中,项目在tomcat上正式运行时,我的java代码或者js代码所使用到的jdk,jar,特别是maven引入的jar,是否就是eclipse中导入的?环境变量path?.m目录?(maven的仓库)
2.开发阶段通过maven导入的jar,在运行阶段,我的代码是怎么找到这些jar的?(maven的仓库)
3.部署项目的过程是否就是直接将真个目录粘贴到tomcat/webapps中就可以了?(如何部署到tomcat)
让我们一起探索:
首先,WebContent目录
上图中,您看到了一个拥有WebContent文件夹的项目,项目名为check。
但是用过eclipse的同学们都知道,你在eclipse中直接创建一个web的maven项目是压根没有WebContent文件夹的。比如,年少轻狂的我,最初就是创建了这样一个目录结构:
我很痛苦,原因是,很多教材上的项目都是有WebContent目录的,痛苦的原因,这个目录基本上就是整个项目的门面啊,你写了半天代码,结果web的访问入口的目录不见了,你不是在搞笑吗?
比如旺庄蒋锋老师的案例项目(虽然我跑不起来它,但是目录结构可以看看的)
那我们先看看,在eclipse上创建maven web项目的时候,如何让项目带有WebContent目录:
1.创建一个项目
看到了吧,项目刚出生的时候,默认没有WebContent目录
然后修改项目properties
之前的描述,都是现象和操作,下面我们来列举一下学到的原理:
这篇博文讨论的是发布web项目,那么从哪里发布到哪里呢?
很显然,我们需要从开发机的eclipse发布到生产机的tomcat(对,就tomcat,嘲笑我没出息吧)
也就是说,环境是固定了的,预先就已经固定了的。应用服务器,铁定就是tomcat的情况下,我们来看看在tomcat上如何发布一个项目。
tomcat我们过去理解,它是应用服务器,是servlet的容器。这种过去粗浅的理解其实还源自于那些朴素的国产java教材或者启蒙读物。
事实上我看了另一本书,受益匪浅,这本书的名字叫做《看透Spring MVC源代码分析与实践》作者韩路彪。
哇靠,醍醐灌顶。
比如:
tomcat确实是servlet容器,但是这个描述严格来说是错的,应该说,tomcat中包括了servlet容器。
tomcat本身是一个容器,但不能狭隘的理解为“servlet容器”。
我们来看看tomcat中最关键的一个配置文件就知道了:
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
这是一个完整的server.xml文件的内容
顶层元素是[server]里面包含一个名为catalina的[service]
[service]里面有两个[connector]和一个[engine]
[server]表示整个tomcat服务器;
[Engine]的字面意思是引擎,实际上是容器。
[connector]负责通讯,将请求封装为符合http协议规范的数据包,然后依靠socket进行通讯。
真正的servlet容器,实际上就是从Engine开始的,servlet容器只是tomcat的一部分它的另一部分就是connector.
接下来,什么是container? 什么是connector?
容器有四种:Engine Host Context Wrapper
配置这四种容器,需要配置文件。
Engine和Host在conf目录下server.xml文件中配置
通常默认,Host表示webapps目录
我们写的项目,需要被发布的项目,其实最终会对应一个Context也就是一个应用。
那么Context如何配置?
Context有三种配置方法:
1.通过配置文件
2.将war打包文件直接拷贝到Host目录(也就是webapps目录下)
3.将项目整个文件夹直接拷贝到Host目录下
如果通过配置文件进行配置,如同engine和host在server.xml文件中配置,context在配置文件中,有5个配置的地方:
conf/server.xml中的标签
conf/[engine]/[host]/这个目录下,与应用同名的xml文件
应用的/META-INF/context.xml文件
conf/context.xml文件
conf/[engine]/[host]/context.xml.default文件
Wrapper的配置,就是web.xml文件中的
那么我们最初的问题似乎找到了答案。
基于上述描述,我们可以得出结论,Tomcat部署web项目其实和你怎么开发的基本没有关系,也就是说你的开发过程不会影响Tomcat上的部署过程,开发对于部署来说,是透明的。
你可以打包成war文件让tomcat来自动解开这个war包来部署
也可以整个把文件夹拷贝过来,然后修改tomcat的配置文件来部署。
你的开发过程用eclipse或者不用,用maven或者不用,打包成war或者不打,对于tomcat来说没有影响,你只需要把对应的配置文件配置好,把对应的目录和文件拷贝过来,Tomcat这台服务器server就会依据配置文件识别站点和应用,让你的servlet在container中运行。
其实我们之所以大费周章的描述这个问题,是因为我们希望可以在这次项目中,用一种比较“正规”的方式去开发和部署自己的项目,脱离刀耕火种的土鳖状态,用现代化的工具将所有的行为打造成自己的作品。基于这种考虑,毫无疑问我们需要更加现代化的部署过程,也就是打包成war文件,然后发布到tomcat。(记住,是发布,不是拷贝)
那么在eclipse上如何将一个maven的web项目发布成war,以及如何将war发布到tomcat就是我们需要了解的过程。
我们聊聊下面这个话题:
eclipse和maven是什么关系?
在开发过程的最开始,我们使用eclipse market为eclipse添加了maven的插件,如下图所示:
但是这个地方似乎看不出来maven和eclipse的关系,以及maven是什么版本的。接下来我们再看看:
这里是整个eclipse的preferences中有关于maven的信息,看到eclipse中通过插件获取的maven的版本是3.3.9
那么这个maven的安装目录在哪里呢?
这个估计得查查eclipse的配置才知道,我想说的是,maven本来就是一个完全可以脱离任何环境(它必须依赖jdk)独立运行的东西,我们的eclipse仅仅是安装了maven插件而已,其实你可以自己安装一个maven的。
去maven官网下载最新3.5.2版本的maven然后解压缩,配置环境变量,就可以在命令行使用,如下图:
测试一下:
这样我们就在eclipse之外单独部署了一个maven
我们也可以在eclipse中使用这个maven,过程就不描述了,道理很简eclipse与maven之间的关系是使用关系,eclipse使用maven,就像eclipse使用tomcat一样。只需要再eclipse中配置maven的路径就可以用了。
所以之前问题的答案很明确:使用关系。
所以接下来的问题就很明确了,eclipse如何将maven的web项目发布成war?
我们先来看看许晓斌的《maven 实战》这本书关于发布项目的内容:
ps:许晓斌也是个人才,百度百科上说,他目前是阿里巴巴的技术专家。
这本书2.7.2部分特别指出:不要使用IDE自带的maven,原因有两个:太新的版本可能不稳定;IDE和命令行的maven必须保持一致,否则可能会出现莫名其妙的错误。
修改IDE中maven的过程就像上面所说的过程一样,看来还是应该用独立的maven。
此外,他也推荐使用用户范围(而不是全局范围的)settings.xml文件
全局范围的settings.xml文件的位置:
用户范围的settings.xml文件的位置:
但是你也看到了,默认情况下,用户.m2目录下只有一个repository文件夹,并没有settings.xml文件,因为需要我们开发者自己手动的,将MAVEN_HOME/conf/settings.xml文件复制过来一份。具体为什么我也不清楚。
下面,完全是许晓斌书中的内容:
java世界中,web项目的标准打包方式是war
作者
使用jetty-maven-plugin进行开发和测试
使用Cargo实现web项目的自动化部署
web项目中包含的WEB-INF目录,这个目录中包含两部分:lib目录和classes目录,前者存放jar包依赖,后者存放web项目中自己写的类。
maven的web项目中有一个webapp目录,这是比较特殊的地方,
webapp目录下,必须包括一个WEB-INF目录。webapp目录下的资源们,与WAR包中的web资源完全一致。
war包中有一个lib目录包含了所有依赖的jar包,但是maven项目中没有这样的目录,因为依赖都配置在POM中,maven在使用war方式打包的时候,会根据POM文件的配置信息,从本地仓库repository中拷贝对应的jar文件到war包的lib目录中。
我靠,所有的疑惑基本都解开了。
也就是说,在开发的过程中,我们使用maven构建项目,pom和本地.m文件目录中的repository文件夹,存储着我们开发环境(eclipse)中所需要的所有jar包。
但是开发结束以后,需要将整个maven的web项目发布成war包。
在这个发布的过程中,会将本地仓库中所有用到的的jar包拷贝到目的地war包中lib目录下。
这就解释了,部署maven的web项目时,我们的java代码所使用的jar包,到底在哪里的问题。
eclipse开发时,使用的jar在本地repository中
tomcat运行时,使用的jar包在tomcat服务器下webapps目录中的war包中(因为你只要将war包拷贝到tomcat的webapps目录下,然后启动tomcat,tomcat会主动、自动去解压缩这个war文件),所以相当于你是用的jar文件都在tomcat服务器上你的项目文件夹的lib目录中。
如上图所示,那么怎样将上图所示的eclipse中的maven的web项目打包成右边所示的war文件,这种格式的转化是如何做到的呢?
我们演示一下:
然后我们在eclipse中将这个项目发布成war文件
然后,我们将war文件拷贝到tomcat的webapp目录下,startup整个服务器就可以了。
startup之前
startup之后
去看看这个项目的目录结构
这个lib中的内容,就是开发过程中,我们使用maven导入的依赖,在部署前maven打包的过程中,被拷贝到了war文件夹的lib目录中。