內容屬原創,轉載請注明出處
寫在前面的話
最近一直比較糾結,歸根結底在於工程的模塊化拆分。以前也干過這事,但是一直對以前的結果不滿意,這會重操舊業,希望搞出個自己滿意的結果。
之前有什么不滿意的呢?
1. 基於maven拆分模塊后,熱部署的效果不好,經常出故障。
2. 對於多個子web工程,不能做到任意一個web工程都可以放到tomcat里運行,要在外面搞個殼子組合多個工程。
於是,有了這糾結的一周,也有了此文。
本文關於什么
如標題所言,本文涉及到如下幾個內容:
1. maven多模塊工程
2. 基於tomcat插件的熱部署
且聽我一一道來。
maven多模塊
這次,采用了如下的結構來構建模塊:
1. 工具工程
之間的依賴關系相對較多,采用了子模塊的方式處理,所有工具工程都是 com.up.tool 下的模塊工程。
<modules> <module>com.up.tool.a</module> <module>com.up.tool.b</module> </modules>
2. 基礎工程
基礎工程都放在com.up.base目錄里,包含系統的基礎框架的內容,但是和具體業務無關。
所有第三方的依賴以及對工具工程的依賴,都在基礎工程里處理。
考慮到war類型的maven工程的依賴傳遞搞不定,com.up.base工程的packaging采用了jar,同時通過ant腳本另外再打war的內容以及發布。
ant插件打war且發布的腳本:

<properties> <project.build.finalName>${project.artifactId}-${project.version}</project.build.finalName> <respositories.url>http://192.168.1.254:8080/nexus/content/repositories/</respositories.url> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.7</version> <executions> <execution> <id>打包其他文件</id> <phase>package</phase> <configuration> <target> <mkdir dir="${project.build.directory}/war"/> <copy todir="${project.build.directory}/war" overwrite="true" > <fileset dir="${basedir}/src/main/webapp"> <exclude name="WEB-INF/web.xml"/> </fileset> </copy> <zip destfile="${project.build.directory}/${project.build.finalName}.war"> <fileset dir="${basedir}/target/war"> <exclude name="work/**"></exclude> </fileset> </zip> <delete dir="${project.build.directory}/war"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <id>install到本地</id> <phase>install</phase> <configuration> <target name="install-ext"> <exec executable="cmd.exe" failonerror="true"> <arg value="/c" /> <arg value="mvn" /> <arg value="install:install-file"/> <arg value="-Dfile=${project.build.directory}/${project.build.finalName}.war" /> <arg value="-DgroupId=${project.artifactId}" /> <arg value="-DartifactId=${project.artifactId}.web" /> <arg value="-Dpackaging=war" /> <arg value="-Dversion=${project.version}" /> </exec> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <id>deploy到倉庫</id> <phase>deploy</phase> <configuration> <target name="deploy-ext"> <condition property="project.deploy.type" value="snapshots" else="releases"> <contains string="${project.version}" substring="SNAPSHOT" /> </condition> <exec executable="cmd.exe" failonerror="true"> <arg value="/c" /> <arg value="mvn" /> <arg value="deploy:deploy-file"/> <arg value="-Dfile=${basedir}\target\${project.build.finalName}.war" /> <arg value="-DgroupId=${project.artifactId}" /> <arg value="-DartifactId=${project.artifactId}.web" /> <arg value="-Dpackaging=war" /> <arg value="-Dversion=${project.version}" /> <arg value="-Durl=${respositories.url}${project.deploy.type}" /> <arg value="-DrepositoryId=${project.deploy.type}" /> </exec> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
為了避免版本號到處飛以及引入其他不需要的第三方依賴,對所有依賴的第三方包做了如下處理:

<properties> <spring.version>3.1.4.RELEASE</spring.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.1.4.RELEASE</version> <exclusions> <exclusion> <artifactId>*</artifactId> <groupId>*</groupId> </exclusion> </exclusions> </dependency> </dependencies> </dependencyManagement>
3. 業務工程
都以com.up.web為父工程,同時,依賴基礎工程 com.up.base

<parent> <groupId>com.up</groupId> <artifactId>com.up.web</artifactId> <version>1.0.0.1-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>com.up</groupId> <artifactId>com.up.base</artifactId> <version>1.0.0.1-SNAPSHOT</version> </dependency> </dependencies> <groupId>com.up.web</groupId> <artifactId>com.up.web.a</artifactId> <version>1.0.0.1-SNAPSHOT</version> <packaging>war</packaging>
同時,為了避免業務工程打出來的war包里包含依賴的其他的jar/war的內容,在com.up.web工程里做了如下處理:

<properties> <project.build.finalName>${project.artifactId}-${project.version}</project.build.finalName> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.7</version> <executions> <execution> <id>after-package</id> <phase>package</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <taskdef resource="net/sf/antcontrib/antcontrib.properties" /> <if> <equals arg1="${project.packaging}" arg2="war" /> <then> <!-- 備份原來的war --> <move file="${project.build.directory}/${project.build.finalName}.war" tofile="${project.build.directory}/${project.build.finalName}-old.war" /> <mkdir dir="${project.build.directory}/war/WEB-INF/classes" /> <copy todir="${project.build.directory}/war/WEB-INF/classes" overwrite="true"> <fileset dir="${project.build.directory}/classes"></fileset> </copy> <copy todir="${project.build.directory}/war" overwrite="true"> <fileset dir="${basedir}/src/main/webapp"></fileset> </copy> <zip destfile="${basedir}/target/${project.build.finalName}.war"> <fileset dir="${basedir}/target/war"> </fileset> </zip> <delete dir="${basedir}/target/war" /> </then> </if> </tasks> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>ant-contrib</groupId> <artifactId>ant-contrib</artifactId> <version>1.0b3</version> <exclusions> <exclusion> <artifactId>ant</artifactId> <groupId>ant</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant-commons-net</artifactId> <version>1.8.1</version> </dependency> </dependencies> </plugin> </plugins> </build>
這樣,既保留了可以直接運行的war包,也保證發布到倉庫的war包只包含工程自身的內容。
基於tomcat插件的熱部署
上面基本上把項目的多模塊結構描述了,接下來該是熱部署的事情了,怎么才能達到效果呢?
尋求方案的痛苦過程不再多說,在看到這篇博文后,把方案定下來了
參考博文:配合m2eclipse,改造tomcatPluginV321根據maven的依賴自動熱部署.
既然已經有大俠基於這個插件做了支持maven的改造,咱也可以這樣干。
開發環境:
1. Eclipse Java EE IDE for Web Developers. Version: Luna Release (4.4.0)
2. tomcat插件 org.uqbar.eclipse.tomcat.xt.features_2.0.0
3. maven3.2.1
實現方案:
1. 基於上面“參考博文”的方式,改造tomcat插件支持配置use Maven
2. 調整當設置use Maven的時候,設置webRoot為 target/webapp
3. 啟動tomcat前,根據maven依賴的jar以及war,拷貝相應內容到target/webapp並開啟監聽
具體為:
對於依賴的jar,拷貝jar到target/webapp/WEB-INF/lib
對於依賴的war,解壓縮后拷貝到target/webapp
對於依賴的jar或者是war,如果對應工程在工作空間而且是open狀態,那么對target/classes,src/main/webapp 目錄增加文件變化監聽,實時把修改的內容拷貝到target/webapp下對應目錄(文件變化監聽基於JNotify實現)
4. 啟動tomcat,docBase為target/webapp
5. 終止tomcat時,停止文件監聽(需要通過插件的終止功能,否則文件監聽未停止,可能導致maven clean 失敗)
6. 增加切換工程的功能,點擊時切換tomcat里的context為當前選中的工程(只支持一個工程)
實現效果
做了上面調整后,達到了如下效果:
1. 設置了工程為tomcat工程而且選中use Maven后,插件會在啟動前拷貝所有通過maven依賴的內容到target/webap
2. 在工作空間打開而且被當前tomcat運行的工程依賴的工程,修改的內容會實時同步到target/webapp(注意設置tomcat插件里的reloadable為false,即不選中該選項)。
由於tomcat運行的docBase為target/webapp,當所有修改的內容可以實時拷貝到target/webapp時,也就實現了熱部署的效果。
問題是什么
目前的方案有如下問題:
1. 啟動的時候的拷貝,會導致啟動tomcat延遲3到4秒。
2. 如果沒通過插件關閉tomcat而是直接通過控制台關閉,會導致文件監聽未停止,可能造成maven clean失敗。
3. 要求所有工程的output dir都是 target/classes,要求web的root都是 src/main/webapp 也就是maven的標准配置,否則會有問題。
4. 插件只支持eclipse4.4,連eclipse4.3都不支持,未追究原因。