架构那点事系列四 - Maven优化篇

来源:互联网 发布:c语言dijkstra算法 编辑:程序博客网 时间:2024/04/28 03:36

       Ant的出现,填补了Java领域 compile kit的空白。而Maven的出现,则算是更近了一步(除了它之外,还有比较著名的同类编译套件IVY等)。构建在之上的CI(Sonar,Hudson,Jenkins等)构件为我们的项目管理带来了极大的方便。这篇文章,源自于工作中Maven的一些高级特性应用,开发后的不断思考,总结。希望能给大家带来一些帮助。

       学习一门技术,先要了解它的历史,之后,没准你会和我一样,深深地迷上它。谈及Maven的发展历程,我们这里可以用此处省略。。。来概括。唯独不能省略的是,我们必须了解现行存在的三个并行版本:2.0.11,2.2.1,3.0.4。在工作中,我用后面两个版本对项目进行交叉验证,以便掌握之间的不同。

       我们都知道Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成,例如编译源代码是由maven-compiler-plugin完成的。进一步说,每个任务对应了一个插件目标(goal),每个插件会有一个或者多个目标,例如maven-compiler-plugin的compile目标用来编译位于src/main/java/目录下的主源码,testCompile目标用来编译位于src/test/java/目录下的测试源码。用户可以通过两种方式调用Maven插件目标。第一种方式是将插件目标与生命周期阶段(lifecycle phase)绑定,这样用户在命令行只是输入生命周期阶段而已,例如Maven默认将maven-compiler-plugin的compile目标与compile生命周期阶段绑定,因此命令mvn compile实际上是先定位到compile这一生命周期阶段,然后再根据绑定关系调用maven-compiler-plugin的compile目标。第二种方式是直接在命令行指定要执行的插件目标,例如mvn archetype:generate 就表示调用maven-archetype-plugin的generate目标,这种带冒号的调用方式与生命周期无关。认识上述Maven插件的基本概念能帮助你理解Maven的工作机制,不过要想更高效率地使用Maven,了解一些常用的插件还是很有必要的,这可以帮助你避免一不小心重新发明轮子。

       Maven将插件体系分为两大块,Build plugins & Reporting plugins,可以参看官网http://maven.apache.org/plugins/index.html的说明。从另一个维度分类的话,可以分为

Bundled plugins & Third party plugins。下面列举出自己项目中定制化的Bundled plugins属性:

<!-- maven plugin version --><compiler.plugin.version>2.3.2</compiler.plugin.version><resource.plugin.version>2.5</resource.plugin.version><surefire.plugin.version>2.11</surefire.plugin.version><findbugs.plugin.version>2.3.2</findbugs.plugin.version><cobertura.plugin.version>2.5.1</cobertura.plugin.version><war.plugin.version>2.2</war.plugin.version><failsafe.plugin.version>2.11</failsafe.plugin.version><enforcer.plugin.version>1.0.1</enforcer.plugin.version>

      为了项目编译方便,自己写了一套编译脚本,这里也和大家分享一下,先从直观上感受一下Maven的强大吧。

@echo off:input_project_homeSET PROJECT_HOME=SET /p PROJECT_HOME=【输入应用根目录,默认为%cd%】if /i "%PROJECT_HOME%"=="" SET PROJECT_HOME=%cd%if not exist "%PROJECT_HOME%" ECHO 找不到路径%PROJECT_HOME%if not exist "%PROJECT_HOME%" goto input_project_homecd /d %PROJECT_HOME%ECHO 应用根目录为%PROJECT_HOME%:mvn_commandECHO ************************ECHO 请选择需要执行的常用命令(注意:不执行测试但还是会编译测试文件和测试资源文件)ECHO ************************ECHO 1-生成eclipse工程文件(注意:如果无法正常通过,请先执行选项3或者4)ECHO 2-编译打包,不执行测试ECHO 3-编译打包(解压war包),不执行测试ECHO 4-编译打包,并执行测试ECHO 5-编译打包(解压war包),并执行测试ECHO 6-执行整个工程的测试ECHO 7-执行单个项目的测试ECHO 8-启动jetty(关闭调试器)ECHO 9-启动jetty(启动调试器)ECHO 0-退出菜单set isopt=set /p isopt=【选择命令】if /i "%isopt%"=="1" goto mvn_eclipseif /i "%isopt%"=="2" goto mvn_install_not_testif /i "%isopt%"=="3" goto mvn_install_not_test_unzip_warif /i "%isopt%"=="4" goto mvn_install_with_testif /i "%isopt%"=="5" goto mvn_install_with_test_unzip_warif /i "%isopt%"=="6" goto mvn_all_testif /i "%isopt%"=="7" goto mvn_project_testif /i "%isopt%"=="8" goto mvn_jettyif /i "%isopt%"=="9" goto mvn_jetty_debugif /i "%isopt%"=="0" goto exitecho "无效选项,请选择(0-9)"goto mvn_command:mvn_eclipsecd %PROJECT_HOME%\allecho 开始生成eclipse工程文件call mvn eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=truegoto mvn_end:mvn_install_not_testcd %PROJECT_HOME%\allECHO 当前路径为%cd%echo 开始编译打包,但不执行测试call mvn clean install -DskipTests=truegoto mvn_end:mvn_install_not_test_unzip_warcd %PROJECT_HOME%\allECHO 当前路径为%cd%echo 开始编译打包(解压war包),但不执行测试call mvn clean install -DskipTests=true -Dunzip.war=truegoto mvn_end:mvn_install_with_testcd %PROJECT_HOME%\allECHO 当前路径为%cd%echo 开始编译打包,并且执行测试call mvn clean installgoto mvn_end:mvn_install_with_test_unzip_warcd %PROJECT_HOME%\allECHO 当前路径为%cd%echo 开始编译打包(解压war包),并且执行测试call mvn clean install -Dunzip.war=truegoto mvn_end:mvn_all_testcd %PROJECT_HOME%\allecho 开始执行整个工程的测试call mvn clean testgoto mvn_end:mvn_project_testcd %PROJECT_HOME%echo 当前路径为%cd%echo 应用根目录为%PROJECT_HOME%set /p subprj=【输入项目路径,如biz\service】if /i "%subprj%"=="" goto mvn_project_testif not exist "%PROJECT_HOME%\%subprj%" ECHO 找不到路径%PROJECT_HOME%\%subprj%if not exist "%PROJECT_HOME%\%subprj%" goto mvn_project_testcd %PROJECT_HOME%\%subprj%echo 项目完整路径为%PROJECT_HOME%\%subprj%echo 开始执行单个项目的测试call mvn clean testgoto mvn_end:mvn_jettycd %PROJECT_HOME%\webecho 当前路径为%cd%echo 启动jetty容器call mvn jetty:run :mvn_jetty_debug    cd %PROJECT_HOME%\webecho 当前路径为%cd%echo debug模式启动jetty容器set MAVEN_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=52000,server=y,suspend=ncall mvn jetty:run:mvn_endcd %PROJECT_HOME%pausegoto mvn_command:exitcd %PROJECT_HOME%

        下面,我们一起看下,架构设计多模块Maven工程时,优化点有哪些?

        首先,做依赖优化&插件优化时,我喜欢share配置,这完全仰仗于Maven的dependencyManagement,dependencyManagement两个标签,你不妨也试试?做依赖优化时,注意“未使用的,但声明的依赖”是比较难优化的(ASM 2静态分析),除非你有非常完备的单元测试,否则我不建议去优化这块的~

        其次,当检测到依赖版本冲突时,Maven内建的版本冲突算法(nearest definition)会帮助你选择最合适的版本。而对于传递依赖,则要使用依赖图来检测,正如Sonar里面的依赖矩阵一样。如:


         如果大家喜欢手动解决冲突,可以使用maven的dependency,-X 或者是 M2eclipse 的dependency hierarchy来诊断

         最后,对于profile和assemble的一些说明。在此之前,我不得不提一下著名理论COC。好的软件,一般都有比较合理的默认值。在做JVM调优时,我就偏爱使用jinfo和-XX:+PrintFlagsFinal(举个例子,如:java -server -XX:+UnlockExprimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+PrintFlagsFinal -XX:+AggressiveOpts version),很可惜Maven并没有类似的小功能,至少我现在还没发现(Maven只是提供了help插件,帮助我们查看其它插件的帮助信息,如你可以在命令行中输入:mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-compiler-plugin -Dfull=true 备注:可以使用这个命令 mvn help:expressions)。Maven的属性命名有这么几种约定,如下:

(1) env.X: Prefixing a variable with "env." will return the shell's environment variable. For example, ${env.PATH} contains the $path environment variable (%PATH% in Windows).(2) project.x: A dot (.) notated path in the POM will contain the corresponding element's value. For example: <project><version>1.0</version></project> is accessible via ${project.version}.(3) settings.x: A dot (.) notated path in the settings.xml will contain the corresponding element's value. For example: <settings><offline>false</offline></settings> is accessible via ${settings.offline}.(4) Java System Properties: All properties accessible via java.lang.System.getProperties() are available as POM properties, such as ${java.home}.(5) x: Set within a <properties /> element or an external files, the value may be used as ${someVar}.

        默认的属性可以通过查看这里http://docs.codehaus.org/display/MAVENUSER/MavenPropertiesGuide获得。同时,repository的配置也很重要,除了自己架设私服外,可以利用业界现有的开放仓库,我的配置一般如下:

<profile>      <id>mvnrepository</id><repositories>        <repository>          <id>mvnrepository</id>          <name>mvnrepository</name>          <url>http://mvnrepository.com</url>  <releases><enabled>true</enabled>  </releases>  <snapshots>  <enabled>false</enabled>    </snapshots>        </repository>     </repositories></profile><profile>      <id>jboss</id><repositories>        <repository>          <id>jboss</id>          <name>mvnrepository</name>          <url>http://repository.jboss.org</url>  <releases><enabled>true</enabled>  </releases>  <snapshots>  <enabled>false</enabled>    </snapshots>        </repository>     </repositories></profile><profile>      <id>jboss2</id><repositories>        <repository>           <id>JBoss repository</id>   <name>mvnrepository</name>       <url>http://repository.jboss.org/nexus/content/groups/public/</url>   <releases>    <enabled>true</enabled>   </releases>   <snapshots>      <enabled>false</enabled>   </snapshots>        </repository>     </repositories></profile><profile>      <id>sonatype</id><repositories>        <repository>          <id>sonatype</id>          <name>mvnrepository</name>          <url>http://repository.sonatype.org/content/groups/public</url>  <releases><enabled>true</enabled>  </releases>  <snapshots>  <enabled>false</enabled>    </snapshots>          </repository>     </repositories></profile>    <profile>    <id>javaNet</id>    <repositories>        <repository>            <id>java.net-Public</id>            <name>Maven Java Net Snapshots and Releases</name>            <url>https://maven.java.net/content/groups/public/</url><releases> <enabled>true</enabled>    </releases>    <snapshots>   <enabled>false</enabled></snapshots>        </repository>    </repositories>    </profile><profile>      <id>maven2</id><repositories>        <repository>          <id>maven2</id>          <name>maven2</name>          <url>http://search.maven.org</url>  <releases><enabled>true</enabled>  </releases>  <snapshots>  <enabled>false</enabled>    </snapshots>          </repository>     </repositories></profile>  </profiles>  <activeProfiles>    <activeProfile>sonatype</activeProfile><activeProfile>jboss</activeProfile><activeProfile>jboss2</activeProfile><activeProfile>mvnrepository</activeProfile><activeProfile>maven2</activeProfile><activeProfile>javaNet</activeProfile>  </activeProfiles>
      有时,在做servlet container迁移时,需要做deploy的兼容性测试。我通常会这么做,先在web module里面做如下声明:
<profiles>            <profile>              <id>jboss4x</id>              <activation>                <activeByDefault>true</activeByDefault>              </activation>              <properties>                <containerId>jboss4x</containerId>                <url>                  http://ovh.dl.sourceforge.net/sourceforge/jboss/JBoss-4.2.2.GA.zip                </url>              </properties>            </profile>            <profile>              <id>tomcat7x</id>              <properties>                <containerId>tomcat7x</containerId>                <url>http://www.apache.org/dist/jakarta/tomcat-7/v7.0.25/bin/                  apache-tomcat-7.0.25.zip</url>              </properties>            </profile>          </profiles> 

        然后,复写cargo插件配置,如下:

  <plugin>           <groupId>org.codehaus.cargo</groupId>           <artifactId>cargo-maven2-plugin</artifactId>           <configuration>             <container>               <containerId>${containerId}</containerId>               <zipUrlInstaller>                 <url>${url}</url>                 <installDir>${installDir}</installDir>               </zipUrlInstaller>             </container>             <configuration>               <properties>                 <cargo.servlet.port>8280</cargo.servlet.port>               </properties>             </configuration>           </configuration>         </plugin> 

        后面,你就可以通过命令进行兼容性测试了,如:mvn install cargo:start 或者mvn cargo:start -Ptomcat7x。


        至此,整篇文章就差不多了,关于Maven的插件编写,后面有机会会和原理篇放一起,一起分享给大家吧,如果有什么不明白的,欢迎留言。


参考资料:

1. http://www.infoq.com/cn/maven-practice

2. http://www.sonatype.com/books/mvnref-book/reference/resource-filtering-sect-properties.html

3. http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

4. http://apollo.ucalgary.ca/tlcprojectswiki/index.php/Public/Project_Versioning_-_Best_Practices

5. https://community.jboss.org/wiki/MavenRepository

6. http://www.juvenxu.com/2010/12/31/infoq-maven-pom-refactoring-add-or-delete/

原创粉丝点击