标签:enabled cts ict lin 阶段 apach proc lse 编译
官网下载-->配置环境变量-->mvn --version测试可用
自行apt安装...
# Displays a list of the platform details like system properties and environment variables.
# Maven会自动下载maven-help-plugin,包括pom和jar等依赖
mvn help:system
如果公司禁止访问外网,这也就限制了Maven中央仓库的访问。设置代理,通过代理访问
setting.xml(apache-maven-3.6.1\conf\下)新增结点
<proxies>
<!-- proxy
| Specification for one proxy, to be used in connecting to the network.
|
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<username>proxyuser</username>
<password>proxypass</password>
<host>proxy.host.net</host>
<port>80</port>
<nonProxyHosts>local.net|some.host.com</nonProxyHosts>
</proxy>
-->
</proxies>
pom文件类似于Makefile或build.gradle等配置文件,即使没有代码,空的文件夹也是可以进行maven管理。一个最简单的pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- 指定pom模型的版本,对于maven2及3只能是4.0.0 -->
<modelVersion>4.0.0</modelVersion>
<!-- 公司名+项目名 -->
<groupId>com.hito.maven</groupId>
<!-- 项目名子模块 -->
<artifactId>hello-maven</artifactId>
<!-- 模块版本号 snapshot为快照,既不稳定版本 -->
<version>1.0-SNAPSHOT</version>
<!--classfiier标签很少使用,用来帮助定义构建输出的一些附属构件。如一些jar,包含java文档和源代码-->
</project>
直接通过idea新建一个空的Java Maven项目,注意下面main与test文件夹,默认规定测试代码位置
│ pom.xml
└─src
├─main
│ ├─java
│ │ └─com
│ │ └─hito
│ │ └─maven
│ │ HelloMaven.java
│ │
│ └─resources
└─test
└─java
引入依赖坐标
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
测试代码编写
package com.hito.maven;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author HitoM
* @date 2019/8/20 23:25
**/
public class HelloTest {
@Test
public void testHello(){
HelloMaven helloMaven = new HelloMaven();
assertEquals(helloMaven.sayHello(), "HelloMaven");
}
}
mvn clean test
进行测试
在上面的依赖坐标中你应该注意到:scope标签,它表示依赖范围,它有五个值
test:依赖只对test中的代码有效。在main中你会发现无法导入junit包,如果改为compile则可以。
compile:缺省值,表示在运行,打包,测试几个声明周期中,对应的jar包都是存在可用的。classpath 中可用,同时它们也会被打包
runtime:运行时范围。编写代码时,不会参加编译,仅运行时有效。即对于测试和运行class-path有效,编译无效。
比如JDBC驱动,我们在配置文件中只会配置JDBC的驱动类的完整路径,运行时才会使用反射获取真正的实例.
再比如log4j,log4j其实是一套日志框架和规范。apache有api接口定义和core实现,我们可以使用apache的core实现也可以使用其他的实现,如jdk中的Logging。那么我就可以这么去实现pom,正常编码时使用接口,程序真正运行是core
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
<scope>runtime</scope>
</dependency>
在这其中发现:log4j-core中其实是有log4j-api依赖的,为什么没有发生冲突呢?因为两者版本一致。那如果版本不一致log4j-api 2.11.1 log4j-core2.12.1,我们应该怎么处理冲突呢?
版本不一致是要注意,一般高版本是兼容低版本的,所以在使用exclusions时保留的应该是高版本。在这里如果排除log4j-core2.12.1中的log4j-api,在pom dependencies目录中的log4j-api2.11.1是无法满足core要求的。解决办法:升高log4j-core版本号,然后exclusion。
provided:类似compile,对于编译和测试class-path有效,运行时无效。期望JDK、容器或使用者会提供这个依赖,正式包中不会有依赖。eg:servlet-api
system:与provided的依赖范围完全一致,但是必须使用systemPath显示指定依赖文件的路径,容易造成不可移植性,慎用!
<dependency>
<groupId>com.google.code</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/kaptcha-2.3.2.jar</systemPath>
</dependency>
依赖范围(Scope) | 对于编译class-path有效 | 对于测试class-path有效 | 对于运行时class-path有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | N | Y | N | JUnit |
provided | Y | Y | N | servlet-api |
runtime | N | Y | Y | JDBC驱动 |
system | Y | Y | N | 本地的,Maven仓库之外的类库文件 |
? 通过maven-compiler-plugin的package命令mvn clean package
即可打包。mvn install
可以将生成的包安装在本地,这样其他依赖可以直接使用。
上面的方式生成的是依赖jar包,如果想要生成带有Main函数的可执行jar包需要怎么办呢?有三种常见的Maven打包插件,maven-jar-plugin、maven-shade-plugin和maven-assembly-plugin,这里通过maven-shade-plugin来实现打可执行包操作
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.hito.maven.Main</mainClass>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
mvn package
即可在target下发现打包成功,通过java -jar xxx.shade.jar
即可执行。
A依赖于B,B依赖于C,那么A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。
依赖范围影响传递性依赖如下表,其中第一列表示第一直接依赖范围,第一行表示第二直接依赖范围。
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | —— | —— | runtime |
test | test | —— | —— | test |
provided | provided | —— | provided | provided |
runtime | runtime | —— | —— | runtime |
Maven是如何处理依赖冲突的呢?如何调解?
B是一个持久层隔离工具包,它支持多种数据库,Mysql、PostgreSQL,构建时需要两种jar,但是B作为依赖被使用时只会依赖一种数据库。这样的话,B中需要声明两种可选依赖,而且是只对B可见可用。A如果想要依赖B,还需显示声明MySQL依赖或PostgreSQL中一种。
<!--B-->
<dependencies>
<dependency>
<groupId>xxx</groupId>
<artifactId>xxx</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>yyy</groupId>
<artifactId>yyy</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!--A-->
<dependencies>
<dependency>
<groupId>B</groupId>
<artifactId>B</artifactId>
</dependency>
<!--\声明B中所用具体实现-->
<dependency>
<groupId>yyy</groupId>
<artifactId>yyy</artifactId>
</dependency>
</dependencies>
对于Maven的总结,就是最佳实践辣
传递依赖会给项目带来很多隐式依赖,这就导致会跟项目本身显示添加的依赖产生冲突,可以通过排除依赖的方式使项目使用显示的依赖而不是隐式依赖。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<!--这里并不需要指定版本号-->
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
依赖与依赖之间是有关联的,比如Spring全家桶。这样我们就可以在Pom文件的properties标签下声明统一的版本号,然后通过${}
引用,可以做到依赖版本的统一管理。
可以运用如下的命令查看解析当前项目的依赖:
依赖列表 mvn dependency:list
查看依赖树 mvn dependency:tree
依赖分析 mvn dependency:analyze
这个结果中有两个重要的部分:
- clean build compile install 是什么?怎么来的?
- maven为什么能够做到这些?
在Maven出现之前,项目的生命周期就已经存在了,Developer对项目进行清理、编译、测试和部署,Maven对所有的构建过程进行了抽象和统一。 Maven的生命周期是抽象的,这就意味着生命周期本身不做任何实际的工作,而是由插件来完成。如同Maven声明抽象方法,而具体实现由插件完成。
一开始以为Maven的生命周期为一个整体,其实不然,Maven拥有三套相互独立的生命周期:
从命令行执行Maven任务的最主要方式就是调用Maven的生命周期阶段。各个生命周期是相互独立的,而一个生命周期阶段是有前后依赖关系的
我们知道Maven的核心仅仅定义了抽象的生命周期,具体的任务交与插件完成,插件以独立的构件形式存在。Maven的核心分发包3MB左右,Maven会在需要的时候下载并使用插件。插件是如何和生命周期绑定关系的呢?在这之前我们必须了解插件目标
对于插件本身,为了能够复用代码,它往往能够完成多个任务。例如maven-dependency-plugin,它能够分析项目依赖(mvn dependency:analyze
),列出项目依赖树等。为每个这样的功能编写独立的插件显然是不可取的,因为这些任务背后有可以复用的代码,因此将这些功能聚集在一个插件里,每个功能就是一个插件的目标。你可以在Mavne官网插件列表中查看 各个plugin的goals。
Maven的生命周期与插件相互绑定,用以完成实际的构建任务。具体而言,生命周期的阶段与 插件的目的相互绑定,已完成某个具体的构建任务。例如项目编译这一任务,它对应了default生命周期的compil这一阶段,而maven-compiler-plugin这一插件的compile目标能够完成该任务。因此将它们绑定,就能实现编译的目的。可是我们在pom中并没有配置绑定关系,它们是如何标定的呢?
为了让用户几乎不用任何配置就能构建Maven项目,Maven在核心为一些主要的生命周期阶段绑定了很多插件的目标,当用户通过命令行调用生命周期阶段的时候,对应的插件目标就会执行相应的任务。
clean生命周期与插件目标待定绑定关系
生命周期阶段 | 插件目标 |
---|---|
pre-clean clean post-clean |
maven-clean-plugin:clean |
default生命周期的内置插件绑定关系和具体任务
生命周期阶段 | 插件目标 | 执行任务 |
---|---|---|
process-resources | maven-resources-plugin:resources | 复制主资源文件到主输出目录 |
compile | maven-compiler-plugin:compile | 编译主代码值主输出目录 |
test | maven-surefire-plugin:test | 执行测试用例 |
package | maven-jar-plugin:jar | 创建项目jar包 |
install | maven-install-plugin:install | 将项目输出构建安装到本地仓库 |
... | ... | ... |
这些都可以在Idea中的maven插件中找到。
除了内置绑定,当然还可以自己选择将某个插件目标绑定到生命周期的某个阶段。
一个常见的例子就是创建项目的源码jar包,内置的插件并没有涉及到这一任务。maven-source-plugin可以帮助我们完成任务,他的jar-no-fork目标能够将项目的主代码打成jar文件,可以将其绑定到default生命周期的verify阶段上,在执行完集成测试和安装构件之前创建源码jar包。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
执行mvn verify
就会发现target文件下打包成功。
[INFO] --- maven-source-plugin:2.1.1:jar-no-fork (attach-sources) @ hello-maven ---
[INFO] Building jar: E:\IdeaProjects\hellomaven\target\hello-maven-1.0-sources.jar
有时候,即使不用过phase配置生命周期阶段,插件目标仍能绑定到生命周期中去,这是因为很多插件的目标在编写时已经定义了默认的绑定阶段。可以在 maven官网中查看。
用户可以在Maven命令中使用-D参数,并伴随一个参数键=参数值的裤衩,来配置插件目标的参数。下面的例子就会跳过执行测试,
mvn install -Dmaven.test.skip = true
很多时候,有些参数很少变动,我们可以把它配置在pom文件中
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
- 为什么SpringBoot项目中的pom.xml文件中很少的依赖信息,却发现其实依赖了一些未声明的信息?
- 那么多依赖冲突就没有什么管理办法吗?
一个 项目往往不是单一的模块,比如一个Web项目可能分为Service、Bean和Repositroy等,我们可以方便的使用Maven来管理模块之间的依赖和聚合关系。
<modules>
<module>bean</module>
</modules>
在bean中
<parent>
<artifactId>maven-integration</artifactId>
<groupId>com.hito</groupId>
<version>0.0.1-SNAPSHOT</version>
<!--如果父模块不在上一层目录,可以通过此配置设置-->
<relativePath>xxx</relativePath>
</parent>
通过声明parent,子模块即可使用父模块中的依赖,可是子模块并不需要父模块中的所有依赖,那就需要依赖管理了。
Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖的灵活性。在dependencyManagement中声明的依赖不会引入实际的依赖,不过能够约束dependencies下的依赖使用。
<dependencyManagement>
<!--在这里声明的依赖不会被引入-->
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.2.0.RC2</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--真正引入,子模块也可以用这种方式引入,统一控制版本信息-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
</dependencies>
build元素下的pluginManagement有同样的功能,子模块可灵活继承父模块中声明的插件。
任何一个项目都隐式的继承一个超级pom文件,该文件在MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中,这就是为什么一个只声明proupID和artifactId的maven工程能够运行的原因。
<project>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<build>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources>
<pluginManagement>
<!-- NOTE: These plugins will be removed from future versions of the super POM -->
<!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) -->
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
</plugins>
</pluginManagement>
</build>
....
</project>
一个多模块的项目中,反应堆指所有模块的一个构建结构。
一般来说,构建顺序是按照pom中声明的顺序自上而下的,但是如果子模块中间存在依赖关系的话,会优先构建依赖的模块。所以mavne的依赖结构是一个有向的非循环图(DAG)
有时候,我只关心某一个或某些模块的构建,这时候就需要对反应堆进行剪裁。mvn命令后追加参数即可完成
例子
mvn clean install -pl account-email,account-persist
,只构建声明的模块mvn clean install -pl account-email,account-persist -am
构建声明的模块及其依赖mvn clean install -rf account-email
,从account-email开始构建标签:enabled cts ict lin 阶段 apach proc lse 编译
原文地址:https://www.cnblogs.com/hitomeng/p/11449511.html