首页 > 代码库 > Maven学习笔记(十) : 使用Maven构建Web应用

Maven学习笔记(十) : 使用Maven构建Web应用

     在实际工作中,我们创建的应用大部分是web应用。在Java的世界中,Web项目的标准打包方式是WAR。这一章,我们介绍怎么使用Maven构建一个Web应用,此外我们还会介绍如何借助jetty-maven-plugin来快速开发和测试Web测试,以及使用Cargo实现Web项目的自动化部署。

Web项目的目录结构

     基于Java的Web应用,其标准的打包方式是WAR。WAR与JAR类似,不过他可以包含更多的内容,如JSP文件、Servlet、Java类、Web.xml配置文件、依赖JAR包、静态web资源(如HTML、CSS、JavaScript文件)等。一个典型的war文件会有如下目录结构:
-war/
     +META-INF/
     +WEB-INF/
     | + classes/
     | | + ServletA.class
     | | + config.properties
     | | + ...
     | |
     | + lib/
     | | + dom4j-1.4.1.jar
     | | + mail-1.4.1.jar
     | | + ...
     | |
     | + web.xml
     |
     + img/
     |
     +css/
     |
     + js/
     |
     + index.html
     + sample.jsp
     一个war包下至少包含两个子项目:META-INF和WEB-INF。前者包含了一些打包元数据信息,一般不用关心,而后者是War包的核心。WEB-INF下必须包含一个Web资源描述文件web.xml,它的子目录classes包含所有该Web项目的类,而另一个子目录lib则包含所有该Web项目的依赖JAR包,classes和lib目录都会在运行时被加入到Classpath中。除了META-INF和WEB-INF外,一般的WAR包都会包含很多Web资源。
     Maven对于Web项目的打包方式必须显示指定为war,否则无法正确打包。如:
<project>
     ...
     <groupId>com.javenxu.mvnbook</goupId>
     <artifactId>sample-war</artifactId>
     <packaging>war</packaging>
     <version>1.0-SNAPSHOT</version>
     ...
</project>   
     一个典型的Web项目的Maven目录结构如下:
+ project     
     |
     + pom.xml
     |
     + src /
          + main /
          | + java /
          | | + ServletA.java
          | | + ...
          | |
          | + resources /
          | | + ...
          | |
          | + webapp /
          |   + WEB-INF /       
          |   | + web.xml
          |   |
          |   + img /
          |   |
          |   + css /
          |   |
          |   + js /
          |   + 
          |   +index.html                      
          |   +sample.jsp
          |
          + test /
          | + java /
          | + resources /
     在src/main/webapp/目录下,必须包含一个子目录WEB-INF,该子目录还必须要包含web.xml文件。src/main/webapp目录下的其他文件和目录包括html、jsp、css、JavaScript等,它们与WAR包中的Web资源完全一致。                      

<finalName>:
     在一些Web项目中,大家可能会看到finalName元素的配置。该元素用来标识项目生成的主构件的名称,该元素的默认值已在超级POM中设定,值为${project.artifactId} - ${project.version},不过,这样的名称太过于冗长,我们都不想在访问页面的时候输入冗长的地址,因此我们会需要名字更为简洁的war包。例,这时可以如下所示配置finalName元素:
     <finalName>account</finalName>
     经此配置后,项目生成的war包名称就会成为account.war,更方便部署。                

使用jetty-maven-plugin进行测试:

     传统的Web测试方法要求我们编译、测试、打包及部署,这往往会消耗数10秒至数分钟的时间,jetty-maven-plugin能够帮助我们节省时间,它能够周期性地检查项目内容,发现变更后自动更新到内置的Jetty Web容器中。换句话说,它帮我们省去了打包和部署的步骤。jetty-maven-plugin默认很好的支持了Maven的项目目录结构。在通常情况下,我们只需要直接在IDE中修改源码,IDE能够执行自动编译,jetty-maven-plugin发现编译后的文件变化后,自动将其更新到Jetty容器,这时就可以直接测试Web页面了。
     使用jetty-maven-plugin十分简单。指定该插件的坐标,并且稍加配置即可,代码如下:
<plugin>
     <groupId>org.mortbay.jetty</groupId>
     <artifactId>jetty-maven-plugin</artifactId>
     <version>7.1.6.v20100715</version>
     <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds>      
          <webAppConfig>
               <contextPath>/test</contextPath>
          </webAppConfig>
     </configuration>
</plugin>
     在该插件的配置中,scanIntervalSeconds顾名思义表示该插件扫描项目变更的时间间隔,这里的配置是每隔10秒。需要注意的是,如果不进行配置,该元素的默认值是0,表示不扫描,用户也就失去了所谓的自动化热部署的功能。上述代码中webappConfig元素下的contextPath表示项目部署后的context path。例如这里的值为/test,那么用户就可以通过http://hostname:port/test访问该应用。
     下一步启动jetty-maven-plugin。在这之前需要对settings.xml做个微小的修改。前面介绍过,默认情况下,只有org.apache.maven.plugins和org.codehaus.mojo两个groupId下的插件才支持简化的命令行调用,即可以运行mvn help: system,但mvn jetty:run就不行了。因为jetty-maven-plugin的groupId是org.mortbay,jetty,为了能在命令行直接运行mvn jetty:run,用户需要配置setting.xml如下:
<settings>
     <pluginGroups>
          <pluginGroup>org.mortbay.jetty</pluginGroup>
      </pluginGroups>
     ...
</settings>
     现在可以运行如下命令启动jetty-maven-plugin:
     $mvn jetty:run
     jetty-maven-plugin会启动Jetty,并且默认监听本地的8080端口,并将当前项目部署到容器中,同时还会根据用户配置扫描代码改动。
     如果希望使用其他端口,可以添加jetty.port参数。例如:
     $mvn jetty:run -Djetty.port=9999
     现在就可以打开http://localhost:9999/test/测试应用了。要停止Jetty,只需要在命令行输入Ctrl+C即可。          
     启动Jetty后,用户可以在IDE中修改各类文件。只要不是修改类名、方法名等较大的操作,jetty-maven-plugin都能够扫描到变更并正确地将变化更新至Web容器中。

使用Cargo实现自动化部署:

部署至本地Web容器:     
     Cargo能帮助用户在Web容器上实现自动化部署Web项目,它支持几乎所有的Web容器。Cargo通过cargo-maven2-plugin提供了Maven集成。虽然cargo-maven2-plugin和jetty-maven-plugin的功能看起来很相似,但它们的目的是不同的,jetty-maven-plugin主要用来帮助日常的快速开发和测试,而cargo-maven2主要服务于自动化部署。
     Cargo支持两种本地部署的方式,分别为standalone模式和existing模式。在standalone模式中,Cargo会从Web容器的安装目录复制一份配置到用户指定的目录,然后在此基础上部署应用,每次重新构建的时候,这个目录都会被清空,所有配置被重新生成。而在existing模式中,用户需要指定现有的Web容器配置目录,然后Cargo会直接使用这些配置并将部署到其相应的位置。
     下面代码展示了standalone模式的配置样例。
<plugin>
     <groupId>org.codehaus.cargo</groupId>
     <artifactId>cargo-maven2-plugin</artifactId>
     <version>1.0</version>
     <configuration>
          <container>
               <containerId>tomcat6x</containerId>
               <home>D:\cmd\apache-tomcat-6.0.29</home>
          </container>
          <configuration>
               <type>standalone</type>
               <home>${project.build.directory}/tomcat6x</home>
               <properties>
                    <cargo.servlet.port>8081</cargo.servlet.port>
               </properties>
           </configuration>
     </configuration>
</plugin>
     默认情况下,Cargo会让Web容器监听8080端口,上面我们修改了Cargo的cargo.servlet.port属性来改变这一配置。
     cargo-maven2-plugin的groupId是org.codehaus.cargo,这不属于官方的两个Maven插件groupId,因此用户需要将其添加到setting.xml的pluginGroup元素中以方便命令行调用。
     现在,要让Cargo启动Tomcat并部署应用,只需要运行:
     $mvn cargo:start
     然后我们就可以访问该web项目的页面了。
     要将应用直接部署到现有的Web容器下,需要配置Cargo使用existing模式,代码如下:
<plugin>
     <groupId>org.codehaus.cargo</groupId>
     <artifactId>cargo-maven2-plugin</artifactId>
     <version>1.0</version>
     <configuration>
          <container>
               <containerId>tomcat6x</containerId>
               <home>D:\cmd\apache-tomcat-6.0.29</home>
          </container>
          <configuration>
               <type>existing</type>
               <home>D:\cmd\apache-tomcat-6.0.29</home>
           </configuration>
     </configuration>
</plugin>

部署至远程Web容器:
     除了让Cargo直接管理本地Web容器然后部署应用之外,也可以让Cargo部署应用至远程的正在运行的Web容器中。前提是,拥有该容器的相应管理员权限。
<plugin>
     <groupId>org.codehaus.cargo</groupId>
     <artifactId>cargo-maven2-plugin</artifactId>
     <version>1.0</version>
     <configuration>
          <container>
               <containerId>tomcat6x</containerId>
               <home>remote</home>
          </container>
          <configuration>
               <type>runtime</type>
               <home>${project.build.directory}/tomcat6x</home>
               <properties>
                    <cargo.remote.username>8081</cargo.remote.username>
                    <cargo.remote.password>admin123</cargo.remote.password>
                    <cargo.tomcat.manager.url>http://localhost:8080/manager</cargo.tomcat.manager.url>
               </properties>
           </configuration>
     </configuration>
</plugin>
     对于远程部署的方式来说,container元素的type子元素的值必须为remote。configuration的type子元素值为runtime,表示既不使用独立的容器配置,也不使用本地现有的容器配置,而是依赖于一个已运行的容器。properties元素用来声明一些容器热部署相关的配置。例如,这里的Tomcat6就需要提供用户名、密码以及管理地址。需要注意的是,这部分配置对于所有容器来说都是不一致的,读者需要查阅对应的Cargo文档。
     有了上述配置之后,就可以让Cargo部署应用了。运行命令如下:
     $mvn cargo:redeploy       
           

Maven学习笔记(十) : 使用Maven构建Web应用