maven权威指南读书笔记
mvn clean install -X
maven默认目录
maven,在没有自定义的情况下,源码假定在\${basedir}/src/main/java;资源文件假定在\${basedir}/src/main/resources;测试代码假定在\${basedir}/src/test;测试资源文件假定在\${basedir}/src/test/resources;编译好的字节码在\${basedir}/target/classes;可以分发的jar包在\${basedir}/target。
其中\${basedir}/src/main/java和\${basedir}/src/main/resources里的文件,打包后都在classpath目录下。
资源目录并非一定要放在src/main/resources下,假设有个项目包含数百个xml文档和数百个图片,可以创建两个目录src/main/xml和src/main/images,并且配置如下:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/xml</directory>
</resource>
<resource>
<directory>src/main/images</directory>
</resource>
</resources>
</build>
超级pom里资源目录就是这样配置的。
超级pom是maven安装的一部分。settings.xml和pom.xml文件的配置可以覆盖超级pom的设置。
maven插件、目标
第一次使用全新的maven运行 mvn install命令的时候,它会从中央仓库下载大部分核心插件,然后存储在maven仓库里。
mvn help:describe -Dplugin=help
help是一个插件,describe是此插件的一个目标,使用-Dplugin指定想要查看的插件
mvn archetype:generate -DgroupId=com.sto -DartifactId=simple
接下来一路回车,就会生成一个maven项目
archetype,[ˈɑːkitaɪp],典型。archetype是“一个原始的模型或者类型”,maven有许多可用的archetype,从生成一个简单的Swing应用,到一个复杂的Web应用。
artifact [ˈɑːtɪfækt] 手工制品,这里是构件、项目的意思。
上面的命令默认使用maven-archetype-quickstart
mvn install
在包含pom.xml的目录下运行,打包应用
运行程序
java -cp target/simple-1.0-SNAPSHOT.jar com.sto.App
maven运行时,会根据pom.xml的设置来运行,pom.xml是有多个层级的,maven需要组合多个层级的pom文件。查看组合后的pom.xml文件:
mvn help:effective-pom
effective [ɪˈfektɪv] 有效的;实际的
maven插件是单个或多个maven目标的集合。maven插件有archetype、jar、compiler、surefire。
maven目标是一个明确的任务。compiler插件有compile目标,surefire插件有test目标。
目标可以通过配置属性来定义其行为,比如:mvn archetype:generate -DgroupId=com.sto -DartifactId=simple,配置了组Id和项目Id。
插件前缀
mvn org.apche.maven.plugins:maven-jar-plugin:2.2:jar
mvn jar:jar
生命周期和生命周期阶段
mvn package,package是一个生命周期阶段
maven有三个标准的生命周期,clean、default、site。
clean包含三个生命周期阶段,pre-clean、clean、post-clean。
default其中包含test、package、install、deploy这些阶段。deploy,复制包到远程maven仓库。
生命周期包含一系列有序的阶段,每个阶段可以绑定零个或多个目标,比如package生命周期阶段绑定了jar:jar插件目标;clean生命周期阶段绑定了clean插件的clean目标;site生命周期包含有site生命周期阶段,该阶段绑定了site插件的site目标。
maven执行一个阶段时,会有序地执行前面的所有阶段,直到指定的那个阶段为止。
mvn package,相当于 mvn resources:resources compiler:compile resources:testResources compiler:testCompile surefire:test jar:jar
packaging有jar、war、maven-plugin。packaging是jar和war不同类型的构件,绑定在package生命周期阶段的插件目标也不同,有jar:jar,有war:war。
\${}的使用
maven有三个隐式的变量,env、project、settings。
\${env.PATH}
\${settings.offline}
\${project.groupId}
还可以在pom.xml或settings.xml中提供properties元素设置自己的属性。
<properties>
<foo>bar</foo>
</properties>
\${project.foo}或\${foo}
还可以访问java系统属性,所有可以通过java.lang.System的getProperties()方法访问的属性,${user.name}
maven资源过滤
resources:resources目标首先过滤资源,然后将资源复制到输出目录。
就像在pom文件中使用\${}引用变量一样,也可以在资源文件中使用。
资源过滤即替换资源文件中的一些符号,与profile联系起来,就可以生成针对不同部署平台的构件;。
maven默认会跳过资源过滤,我们需要显示地配置资源过滤:
<build>
<filters>
<filter>src/main/my.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
1、建立一个maven项目 mvn archetype:generate -DgroupId=com.sto -DartifactId=simple 2、打开resources目录,随便自定义一个文件,取名service.xml,文件内容如下: <service> <!-- ${project.version} --> <!-- ${env.PATH} --> <url>${jdbc.url}</url> <user>${jdbc.username}</user> <password>${jdbc.password}</password> </service> 3、再建立一个my.properties文件,内容如下: jdbc.url=jdbc:hsqldb:mem:mydb jdbc.username=sa jdbc.password=123 4、配置资源过滤 <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <filters> <filter>src/main/my.properties</filter> </filters> </build> 5、执行mvn package 6、查看target/classes/service.xml
7、复制service.xml并粘贴,改名为service2.xml 8、只对某个资源过滤并复制输出到指定目录 <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>service2.xml</include> </includes> <!-- 定义资源输出目录,可以不配置,默认输出到classPath --> <targetPath>${basedir}</targetPath> <filtering>true</filtering> </resource> </resources> <filters> <filter>src/main/my.properties</filter> </filters> </build>
profile
profile是用来做什么的?它可以为特殊环境自定义特殊的构建。Maven能让你定义任意数量的profile,这些定义可以覆盖pom.xml中的任何配置。
你可以在开发profile中,访问本地数据库,在产品profile中,访问产品数据库。
profile也可以通过环境和平台被激活,你可以自定义一个profile,它根据不同的操作系统或不同的JDK版本有不同的行为。
profile激活元素可以包含一个或多个选择器:包含JDK版本,操作系统参数,文件,及maven属性。
maven3不再支持base目录下的profiles.xml文件。
可以在settings.xml中添加profiles元素。
<project> <profiles> <profile> <id>dev</id> <activation> <activeByDefault>false</activeByDefault> <jdk>1.5</jdk> <os> <name>Windows XP</name> <family>Windows</family> <arch>x86</arch> <version>5.1.2600</version> </os> <property> <!-- maven属性 --> <name>mavenVersion</name> <value>2.0.5</value> </property> <file> <!-- 在基础目录中存在及不存在file2和file1才可激活 --> <exists>file2.properties</exists> <missing>file1.properties</missing> </file> </activation> </profile> </profiles> </project>
示例:为生产环境和开发环境配置不同的profile
1、新建项目。mvn archetype:generate -DgroupId=com.sto -DartifactId=simple
2、添加资源文件。打开resources目录,新建自定义文件service.xml,文件内容:
<service>
<env>${my.os}</env>
</service>
3、在main文件夹下新建文件夹config,添加文件 dev.properties,文件内容:my.os=win10。添加文件 pro.properties,文件内容 my.os=centos。
4、打开pom.xml,在build元素内添加:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
在project元素内添加:
<profiles> <profile> <id>dev</id> <activation> <os> <name>Windows 10</name> </os> </activation> <build> <filters> <filter>src/main/config/dev.properties</filter> </filters> </build> </profile> <profile> <id>pro</id> <build> <filters> <filter>src/main/config/pro.properties</filter> </filters> </build> </profile> </profiles>
5、执行mvn package,查看target里的service.xml文件;执行mvn package -Ppro,查看target里的文件service.xml
插件配置、目标配置、compiler插件配置
compile阶段绑定compiler插件的compile目标,compile目标会调用javac。 compiler插件假设所有的java源码遵循java 1.3,运行在java 1.1 JVM。 更改配置: <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build> 或者: <build> <pluginManagement> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <executions> <execution> <goals> <goal>compile</goal> </goals> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </execution> </executions> </plugin> </plugins> </pluginManagement> </build> 建议使用第一种配置方式,第一种方式配置的是compiler插件,第二种方式配置的是compile目标。 我们为整个插件配置source和target,是因为compiler:compile并不是我们唯一感兴趣的目标,另外还有compiler:testCompile目标。
maven坐标可以唯一标识一个项目,或一个插件。maven的坐标有组id,项目id,版本号,classifier。classifier,发布同样的代码,但需要生成两个独立的包,就可以使用分类器,一个使用java1.4编译,一个使用java1.6编译。分类器常用于打包构件的源码、javaDoc或者二进制集合。
maven依赖的传递性,A项目依赖B项目,B项目又依赖C项目,我们在A项目的pom.xml文件里只需要添加对B项目的依赖即可。因为B项目也是一个maven项目,B项目的pom文件里已经定义了对C项目的依赖。
依赖范围控制依赖在哪些classpath中可用。
compile,默认范围,编译、测试、运行都依赖,所有的classpath中有。
test,只在测试编译和测试运行时在classpath中有效的依赖。
provided,servlet依赖已经由运行容器提供,不会被包含到war包中。
runtime,在运行和测试系统时需要。
mvn site
会生成htlm文件,可以查看依赖,查看项目信息,查看maven插件
mvn dependency:tree
查看依赖树
执行package生命周期阶段,如果surefire:test执行失败,默认会停止构建项目。
忽略测试失败配置
1、pom文件配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
</plugins>
</build>
2、命令行配置
mvn test -Dmaven.test.failure.ignore=true
跳过单元测试
1、pom文件配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
2、命令行配置
mvn install -Dmaven.test.skip=true
mvn Assembly插件用来创建应用程序的压缩包
<build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build> mvn install assembly:assembly
示例二,将项目源码以压缩包的形式复制一份给别人
1、mvn archetype:generate -DgroupId=com.sto -DartifactId=simple
2、mvn assembly:single -DdescriptorId=project
示例三,创建一个可运行的jar文件
1、mvn archetype:generate -DgroupId=com.sto -DartifactId=simple 2、修改pom.xml 添加依赖,用以观察依赖被一起打包 <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.4</version> </dependency> </dependencies> 配置assembly <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2-beta-2</version> <executions> <execution> <id>create-executable-jar</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptorRefs> <descriptorRef> jar-with-dependencies </descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.sto.App</mainClass> </manifest> </archive> </configuration> </execution> </executions> </plugin> </plugins> </build> 3、mvn package 4、java -jar simple-1.0-SNAPSHOT-jar-with-dependencies.jar 5、观察 此jar包比标准生成的jar包大很多,因为此jar包内部还真实地包含了依赖;标准jar包只是通过pom文件声明了需要哪些依赖。 此jar包的MANIFEST.MF文件中包含有主类位置——Main-Class: com.sto.App
示例四,通过套件依赖组装套件
1、mvn archetype:generate -DgroupId=com.sto -DartifactId=simple;并删除src目录,只保留pom文件 2、修改pom文件 类型改为pom <build> <pluginManagement> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2-beta-2</version> <executions> <execution> <id>create-project-bundle</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptorRefs> <descriptorRef>project</descriptorRef> </descriptorRefs> </configuration> </execution> </executions> </plugin> </plugins> </pluginManagement> </build> 3、新建两个子项目first、second mvn archetype:generate -DgroupId=com.sto -DartifactId=first mvn archetype:generate -DgroupId=com.sto -DartifactId=second 4、修改first、second项目的pom文件 <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> </plugin> </plugins> </build> 5、执行mvn install 观察:maven把first、second两个项目的标准jar文件、pom文件、及项目压缩文件都复制到了maven仓库。 6、创建一个新项目bundle,和simple并列关系或者说没有关系的项目 mvn archetype:generate -DgroupId=com.bundle -DartifactId=bundle 7、修改bundle的pom文件 <packaging>pom</packaging> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>com.sto</groupId> <artifactId>first</artifactId> <version>1.0-SNAPSHOT</version> <classifier>project</classifier> <type>zip</type> </dependency> <dependency> <groupId>com.sto</groupId> <artifactId>second</artifactId> <version>1.0-SNAPSHOT</version> <classifier>project</classifier> <type>zip</type> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2-beta-2</version> <executions> <execution> <id>bundle-project-sources</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptorRefs> <descriptorRef> jar-with-dependencies </descriptorRef> </descriptorRefs> </configuration> </execution> </executions> </plugin> </plugins> </build> 8、mvn package
mvn archetype:generate -DgroupId=com.sto -DartifactId=simple-webapp -DarchetypeArtifactId=maven-archetype-webapp
maven jetty插件配置
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
</plugin>
</plugins>
</build>
在命令行运行 mvn jetty:run
最后提示[INFO] Started Jetty Server就成功运行了
浏览器访问:localhost:8080/simple-webapp
优化依赖
dependencyManagement放在父pom,并声明依赖版本,这时不会把依赖添加进来。接着,在子模块里声明依赖时,不需要添加版本,会采用父pom里dependencyManagement内声明的版本。
<project>
<dependencyManagement>
<dependency>
</dependency>
</dependencyManagement>
</project>
优化插件
pluginManagement元素定义在父项目,子项目会继承。
maven-resources-plugin插件在父pom的pluginManagement元素里配置后,子项目不需要再次声明组织ID及项目ID。
maven-assembly-plugin插件在父pom的pluginManagement元素下配置后,还必需在子项目声明artifactId。
新建父项目a
1、mvn archetype:generate -DgroupId=a -DartifactId=a
2、删除src目录,修改pom文件packaging为pom。并修改为:
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<resources>
<resource>
<directory>src/main/config</directory>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
新建父项目b
3、mvn archetype:generate -DgroupId=b -DartifactId=b
4、新建src/main/resources/resource.txt,新建src/main/config/config.txt
5、b项目pom:
<parent>
<artifactId>a</artifactId>
<groupId>a</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>b</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
6、执行mvn clean package,查看target/classes文件夹
排除传递性依赖
<dependency>
<groupId>xxx</groupId>
<artifactId>a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>xxx</groupId>
<artifactId>b</artifactId>
</exclusion>
</exclusions>
</dependency>
此项目依赖于a项目,a项目又依赖于b项目,这里排除了对b项目的依赖。
可选依赖
依赖范围和依赖传递的关系
maven版本管理
SNAPSHOT,开发版本,maven在安装或发布这个构件时会将该符号展开为一个日期和时间值,转换为UTC。
LATEST指仓库中构件的最新发布版或快照版,即最近部署的那个版本。
从maven 2.0.9开始,超级pom中锁住了一组核心插件的版本,提高了maven构建的稳定性。但非核心插件或者说没有在超级pom中指定版本的插件maven仍然会自动去仓库下载LATEST版本。不过,因为在超级pom中插件管理部分配置了不下载快照版本,插件更新策略也被设置为永不更新。所以实际上不会自动更新插件的。
聚集和继承。聚集用来模块构建;继承父文件内容。
maven执行带有子模块的项目时,首先载入父POM,然后定位所有子POM,然后把这些POM放入reactor(反应堆),reactor负责分析模块间的依赖关系,从而确保各模块能以适当的顺序被编译和安装。
POM最佳实践
如果你有一组逻辑上归类在一起的依赖,可以创建一个打包方式为pom的项目来将这些依赖集中在一起。比如,假设应用程序使用hibernate,所有使用hiberna的项目同时依赖Spring和MySql Jdbc驱动。这时,创建一个打包方式为pom的项目A,为其添加hibernate、spring、Mysql Jdbc的依赖。
之后,哪个项目需要这一组依赖,直接依赖项目A。
如果把A项目设置为父项目,不灵活,因为一个maven项目只能有一个父项目。