1 概述
Maven是Java中较为重要的项目构建工具,它跟npm工具一样都是依赖的管理工具。同时,它还包含了打包,编译,测试,发布等功能的像webpack的工具。
Maven是相比Java的Ant更为好用的工作,主要改进是:
- 约定方式定义目录,所有的Maven工程,src与test目录都是相同雷同的,约定方式的定义避免配置的麻烦。
- 约定方式定义打包流程,Maven工具定义了多个phrase
- validate: validate the project is correct and all necessary information is available
- compile: compile the source code of the project
- test: test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
- package: take the compiled code and package it in its distributable format, such as a JAR.
- integration-test: process and deploy the package if necessary into an environment where integration tests can be run
- verify: run any checks to verify the package is valid and meets quality criteria
- install: install the package into the local repository, for use as a dependency in other projects locally
- deploy: done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects.
Maven的打包流程是固定的,不能被改变的,我们要配置的是不同的打包阶段(phrase),应该调用什么样的插件。而不是去配置哪个命令完成之后执行哪个命令,这大大简化了配置的难度。在之前的Ant工具,Makefile工具,npm工具中我们都是定义命令的执行流程,而不是定义阶段的需要插件是什么。
- 约定方式的Super Pom,每个配置文件都有一个父级的配置文件,即使当前配置文件是空的,它也有每个phrase阶段默认绑定的插件是什么,这为零配置的配置文件做好了基础。
最后是,Maven的其他功能包括:
- 脚手架生成,根据脚手架生成Maven项目
- 插件绑定phrase,为不同的phrase绑定不同的插件
- filter source,为资源文件注入变量
- 条件配置,根据不同的环境配置使用不同的配置文件
- 依赖查找,依赖分析,查找,选择与安装。Java与node的依赖不同的是,同一个包在一个项目中只能有一个版本,不允许同一个包的多版本共存,这涉及到依赖包该如何抉择的问题。
- 仓库,依赖的仓库分为模块仓库,本地仓库,远程私有仓库,远程公有仓库等的选择。
- 模块属性继承,就是parent模块的功能了
- 多模块编译,多模块编译与打包的时候,需要按照依赖次序先后执行多个模块的编译与打包。
2 安装
export MVN_HOME=/Users/fish/Util/maven
export PATH=$PATH:$MVN_HOME/bin
官网下载二进制包,解压后加入以上的环境变量
mvn -v
成功后运行以上命令能看到结果,我的是3.6.3版本
settings>
<mirrors>
<mirror>
<id>aliyun</id>
<name>aliyun</name>
<mirrorOf>central</mirrorOf>
<<!-- 国内推荐阿里云的Maven镜像 -->
url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirror>
</mirrors>
</settings> </
在主目录的.m2文件夹下创建一个settings.xml配置文件,配置如上来切换国内的maven镜像
3 SpringBoot入门
3.1 入门
mvn archetype:generate
这样会在全交互提示的情况下创建项目结构
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=helloworld -Dpackage=com.mycompany.app -Dversion=1.0-SNAPSHOT
预先配置项目名称,仅交互提示项目结构就可以了
➜ maven git:(master) ✗ tree .
.
└── helloworld
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── mycompany
│ └── app
│ └── App.java
└── test
└── java
└── com
└── mycompany
└── app
└── AppTest.java
12 directories, 3 files
一般选择7: internal -> org.apache.maven.archetypes:maven-archetype-quickstart就可以了。项目结构如上
mvn clean package
打包生成jar包
java -cp target/helloworld-1.0-SNAPSHOT.jar com.mycompany.app.App
指定jar文件启动
3.2 Spring Boot的启动
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>mvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<
name>mvc</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
<parent>
</properties>
<start-class>com.mycompany.app.App</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<dependency>
</
dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</
build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<plugin>
</plugins>
</build>
</project> </
新建项目以后,新增parent和dependency,以及plugin,最后记得加上properties的starter-class。由于有额外的plugin,所以方便了使用Spring-Boot的特殊命令。
package com.mycompany.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
.run(App.class,args);
SpringApplication}
}
App.java的文件
package com.mycompany.app;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
@RequestMapping(path="/design",produces="application/json")
public class HomeController{
@GetMapping("/recent")
public String home(){
return "123";
}
}
HomeController.java的文件
server.port=8081
src/resources/applications.properties的文件,描述了项目属性
mvn package spring-boot:run
启动项目
mvn package
java -jar target/mvc-1.0-SNAPSHOT.jar
打包后,用JRE启动,很方便
4 入门
代码在这里
4.1 脚手架
mvn -B archetype:generate -DgroupId=com.myapp -DartifactId=basic -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4
创建一个maven项目,脚手架是maven-archetype-quickstart的1.4版本,而且,项目的groupId是com.myapp,artifactId是basic。groupId是用来区别不同项目的全名,artifactId是项目名称。
.
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── myapp
│ └── App.java
└── test
└── java
└── com
└── myapp
└── AppTest.java
9 directories, 3 files
这是生成的目录结构,可以看到groupId
4.2 打包
mvn package
进行打包,编译和单元测试都会进行,会自动生成target目录
java -cp target/basic-1.0-SNAPSHOT.jar com.myapp.App
启动项目
4.3 清理
mvn clean
清空项目的生成文件,删除target目录
4.4 文档
mvn site
在target/site目录中的生成文档网站
打开index.html就能打开网站了
4.5 安装
mvn install
安装当前包到本地local仓库去
显示是安装到用户目录的~/.m2/repository目录上了
4.6 配置说明
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.myapp</groupId>
<artifactId>app</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<
name>app</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</project> </
modelVersion就是Maven配置文件的版本,dependencies就是依赖的描述,其他都没啥好说的。
5 资源
资源filter就是在编译时对资源文件替换属性(properties)的过程
5.1 内置文件属性
代码在这里
资源filter,就是在编译时,对资源进行属性值替换的操作
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.mycompany.source -DartifactId=my-source
创建项目,注意这次有填入DarchetypeGroupId,-B参数是免交互方式提示的
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── mycompany
│ │ └── source
│ │ └── App.java
│ └── resources
│ └── application.properties
└── test
└── java
└── com
└── mycompany
└── source
└── AppTest.java
12 directories, 4 files
建立application.properties文件
application.name=${project.name}
application.version=${project.version}
message=${my.filter.value}
资源文件的内容
package com.mycompany.source;
import java.io.*;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
try{
new App().run();
}catch(Exception e){
.printStackTrace();
e}
}
public void run()throws Exception{
InputStream is = getClass().getResourceAsStream( "/application.properties" );
byte b[]=new byte[1024];
.read(b);
is.close();
isSystem.out.println("data is ["+ new String(b)+"]");
}
}
App.java文件的内容
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.source</groupId>
<artifactId>my-source</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<
name>my-source</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<my.filter.value>hello</my.filter.value>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<resource>
</resources>
</build>
</project> </
pom.xml的文件的内容,主要是加入了build的resources的配置,指定哪些资源文件执行属性filter的操作。
➜ my-source git:(master) ✗ java -cp target/my-source-1.0.jar com.mycompany.source.App
data is [application.name=my-source
application.version=1.0
message=hello]
➜ my-source git:(master) ✗
这是输出描述,即使我们没有定义任何的属性,Maven也会有自己预定义的属性,就是project开头的属性了。另外一方面,我们可以在pom.xml的properties标签中加入自己自定义的属性,这就是message输出为hello的原因了。
5.2 外置命令行属性
代码在这里
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.mycompany.source -DartifactId=my-source3
创建项目
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── mycompany
│ │ └── source
│ │ └── App.java
│ └── resources
│ └── application.properties
└── test
└── java
└── com
└── mycompany
└── source
└── AppTest.java
12 directories, 4 files
目录结构,和刚才的一样
myname=${commandline.props}
application.properties的内容
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.source</groupId>
<artifactId>my-source3</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<
name>my-source3</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<resource>
</resources>
</build>
</project> </
pom.xml和刚才的也是一样的
mvn package "-Dcommandline.props=Hello Cat"
打包的时候,使用-D参数传入属性值
➜ my-source3 git:(master) ✗ java -cp target/my-source3-1.0-SNAPSHOT.jar com.mycompany.source.App
data is [myname=Hello Cat]
➜ my-source3 git:(master) ✗
这是输出结果,可以看到资源文件的内容,在编译时,根据命令行的输入参数改变了
5.3 外置文件属性
代码在这里
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.mycompany.source -DartifactId=my-source2
创建项目
.
├── filter.properties
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── mycompany
│ │ └── source
│ │ └── App.java
│ └── resources
│ └── application.properties
└── test
└── java
└── com
└── mycompany
└── source
└── AppTest.java
目录结构,加入了filter.properties文件
message=${my.filter.value}
message2=${my.filter.value2}
application.properties的资源文件内容
my.filter.value=hello!
my.filter.value2=Cat
filter.properties的内容,这是外置的属性文件描述
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.source</groupId>
<artifactId>my-source2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<
name>my-source2</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<my.filter.value>hello</my.filter.value>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</build>
<filters>
<filter>filter.properties</filter>
<filters>
</resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<resource>
</resources>
</build>
</project> </
这是pom文件,在build标签下,加入了filters的配置,指定了外置配置文件的来源。
➜ my-source2 git:(master) ✗ java -cp target/my-source2-1.0-SNAPSHOT.jar com.mycompany.source.App
data is [message=hello
message2=Cat]
这是输出内容
6 生命周期
Maven 有以下三个标准的生命周期:
- clean:项目清理的处理
- default(或 build):项目部署的处理
- site:项目站点文档创建的处理
这是一个典型的build工作的生命周期
6.1 插件与生命周期
代码在这里
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.mycompany.app -DartifactId=lifecycle
创建项目
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>lifecycle</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<
name>lifecycle</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>id.compile</id>
<phase>compile</phase>
<goals>
<goal>run</goal>
<goals>
</configuration>
<tasks>
<echo>compile phase</echo>
<tasks>
</configuration>
</execution>
</execution>
<id>id.test</id>
<phase>test</phase>
<goals>
<goal>run</goal>
<goals>
</configuration>
<tasks>
<echo>test phase</echo>
<tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </
pom.xml文件的配置。
在Maven中,使用插件是由两部分组成,插件和goal。所以,我们先在plugins中声明一个maven-antrun-plugin插件,然后在executions声明每个任务。每个任务包括要执行插件的哪个goal,以及插件执行时的配置configuration,id是可选的,phase也是可选的。
plugin>
<groupId>org.codehaus.modello</groupId>
<artifactId>modello-maven-plugin</artifactId>
<version>1.8.1</version>
<executions>
<execution>
<configuration>
<models>
<model>src/main/mdo/maven.mdo</model>
<models>
</version>4.0.0</version>
<configuration>
</goals>
<goal>java</goal>
<goals>
</execution>
</executions>
</plugin> </
有些插件的goal默认就绑定到哪些phase上面了,所以就不需要再配置phase了。当有多个execution任务的时候,才需要去配置id标签
输出以上,可以看到不同编译阶段,会触发插件的输出
6.2 生命周期默认绑定
不同的生命周期下,Maven已经有默认的插件的绑定,这里就不啰嗦了,具体看这里
7 多模块聚合与继承
7.1 模块继承
代码在这里
.
├── parent
│ └── pom.xml
└── subModule
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── mycompany
│ └── app
│ └── App.java
└── test
└── java
└── com
└── mycompany
└── app
└── AppTest.java
13 directories, 4 files
目录结构如上,我们在同一个目录中建立了两个模块,parent模块与subModule模块。
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>multi1</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<
name>multi1</name>
<url>http://maven.apache.org</url>
<properties>
<subModuleVersion>1.0.0</subModuleVersion>
<properties>
</project> </
这是parent/pom.xml的配置,注意声明了subModuleVersion的属性,以及packaging的值为pom。
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">
modelVersion>4.0.0</modelVersion>
<
parent>
<groupId>com.mycompany.app</groupId>
<artifactId>multi1</artifactId>
<version>1.0</version>
<relativePath>../parent/pom.xml</relativePath>
<parent>
</
artifactId>subModule</artifactId>
<version>${subModuleVersion}</version>
<packaging>jar</packaging>
<
name>subModule</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</project> </
这是subModule/pom.xml的配置,注意,声明了parent配置,并用relativePath指向到同级的parent目录。在这里,subModule无需再声明groupId,version也可以直接用父级的属性subModuleVersion。这是模块继承带来的意义,子模块会继承父模块的groupId,以及父模块的properties。另外,在parent标签中,依然需要指定版本号version,而relativePath默认值为../pom.xml,如果默认值不对的话,需要显式指定。
mvn package
在subModule的目录中,执行mvn package,发现可以执行运行,生成的版本号的确也是父模块定义的版本号
mvn package
在parent的目录中,执行mvn package。什么工作都没有做,这是一个正常的现象。
7.2 模块聚合
代码在这里
模块聚合的目的是,将多个互相依赖的模块进行统一的编译打包等操作
.
├── parent
│ └── pom.xml
├── subModule
│ ├── pom.xml
│ └── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── mycompany
│ │ └── app
│ │ ├── App.java
│ │ └── Service.java
│ └── test
│ └── java
│ └── com
│ └── mycompany
│ └── app
│ └── AppTest.java
└── subModule2
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── mycompany
│ └── app2
│ └── App.java
└── test
└── java
└── com
└── mycompany
└── app
└── AppTest.java
25 directories, 8 files
这是多模块的目录结构,有一个主模块,以及两个子模块。
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<
name>parent</name>
<url>http://maven.apache.org</url>
<
modules>
<module>../subModule</module>
<module>../subModule2</module>
<modules>
</project> </
这是parent/pom.xml,模块聚合的话,需要设置modules标签,并且以相对路径的方式指向子模块的位置
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>subModule</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<
name>subModule</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</project> </
这是subModule/pom.xml的配置,普通的配置,没啥特别的,注意没有parent标签配置
package com.mycompany.app;
public class Service
{
public String go(){
return "Hello SubModule Service Go!";
}
}
这是subModule下的Service.java文件,一个模块对外的实现
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app2</groupId>
<artifactId>subModule2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<
name>subModule2</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependency>
<groupId>com.mycompany.app</groupId>
<artifactId>subModule</artifactId>
<version>1.0-SNAPSHOT</version>
<dependency>
</dependencies>
</project> </
这是subModule2/pom.xml的配置,注意没有parent的配置。但是加入了一个dependency,指向subModule的模块。
package com.mycompany.app2;
import com.mycompany.app.Service;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
Service service = new Service();
System.out.println(service.go());
}
}
这是subModule2的入口方法,注意应用了subModule模块的Service类。
mvn package
这个时候,如果我们在subModule2中进行打包操作,会提示失败。因为没有找到subModule模块,这点和模块继承是不同的。
mvn package
我们需要在parent模块,执行mvn package操作,才是正常的
java -cp ../subModule2/target/subModule2-1.0-SNAPSHOT.jar:../subModule/target/subModule-1.0-SNAPSHOT.jar com.mycompany.app2.App
在parent目录下,执行以上命令就能启动subModule2模块了
从实验中我们可以看出,Maven在模块依赖的时候,可以绕过本地仓库,远程仓库,直接在当前模块modules中寻找对应的依赖实现,相当方便。相比之下,npm的依赖查找相当僵硬地一定要放在node_modules目录,为了解决开发时的多模块查找的问题时,引入了麻烦的npm link命令。
7.3 组合使用
代码在这里
模块继承就是子模块中进行parent标签的配置,目的是为了继承父模块的配置。而模块聚合就是父模块中进行modules标签的配置,目的是为了同时编译打包和识别多个模块。两者并不冲突,我们也经常混合在一起经常使用。
.
├── parent
│ └── pom.xml
├── subModule
│ ├── pom.xml
│ └── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── mycompany
│ │ └── app
│ │ ├── App.java
│ │ └── Service.java
│ └── test
│ └── java
│ └── com
│ └── mycompany
│ └── app
│ └── AppTest.java
└── subModule2
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── mycompany
│ └── app2
│ └── App.java
└── test
└── java
└── com
└── mycompany
└── app
└── AppTest.java
25 directories, 8 files
跟刚才一样的目录和代码,仅仅是配置文件不同
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<
name>parent</name>
<url>http://maven.apache.org</url>
<
properties>
<subModuleVersion>1.2.0</subModuleVersion>
<subModuleVersion2>1.3.0</subModuleVersion2>
<properties>
</
modules>
<module>../subModule</module>
<module>../subModule2</module>
<modules>
</project> </
parent/pom.xml文件,加入了properties
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">
modelVersion>4.0.0</modelVersion>
<
parent>
<groupId>com.mycompany.app</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<relativePath>../parent/pom.xml</relativePath>
<parent>
</
groupId>com.mycompany.app</groupId>
<artifactId>subModule</artifactId>
<version>${subModuleVersion}</version>
<packaging>jar</packaging>
<
name>subModule</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</project> </
subModule/pom.xml文件,加入了parent,并且version使用了属性引用
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">
modelVersion>4.0.0</modelVersion>
<
parent>
<groupId>com.mycompany.app</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<relativePath>../parent/pom.xml</relativePath>
<parent>
</
groupId>com.mycompany.app2</groupId>
<artifactId>subModule2</artifactId>
<version>${subModuleVersion2}</version>
<packaging>jar</packaging>
<
name>subModule2</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependency>
<groupId>com.mycompany.app</groupId>
<artifactId>subModule</artifactId>
<version>${subModuleVersion}</version>
<dependency>
</dependencies>
</project> </
subModule2/pom.xml的配置文件,加入了parent的配置,以及version与dependence的version都是用属性引用。
mvn package
在subModule2的目录下输入命令,依然出错,可见parent指令依然只是继承属性,不会影响依赖查找
mvn package
在parent的目录下输入命令,和之前一样正常运行
7.4 FAQ
7.4.1 指定模块打包
mvn -pl xxx -am package
默认的mvn package会打包所有模块,我们可以指定-pl来指定只打包特定的模块,但是要注意的是,需要加上-am参数,表示将依赖的模块也一起编译。
7.4.2 指定模块执行Maven指令
mvn -pl app flyway:clean
我们只想执行app模块的flyway:clean指令,但是运行就会报错。因为app模块,依赖于common模块,而common模块没有在-pl列表中,所以app模块在执行flyway:clean的时候就会失败(需要编译common模块)。
mvn -pl app,common flyway:clean
这一次,我们将app与common模块一起加入-pl后执行,发现还是报错。因为common模块是公用模块,是没有flyway-maven-plugin的。
# 在顶级目录,将所有模块安装到本地目录
mvn install
# 在app模块中,直接执行flyway:clean指令即可
cd app
mvn flyway clean
最终,我们使用其他方法来迂回实现。在app模块(非顶级模块)直接执行maven指令的时候,依赖是只会在local repository或者remote repository中寻找,不会在当前项目的多模块中寻找的。因此,我们需要将当前项目所有模块都提前install到local repository,就能实现单独模块的maven指令执行了。
相关资料:
8 条件配置
8.1 条件配置与激活
代码在这里
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>profile</artifactId>
<packaging>jar</packaging>
<
name>profile</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</
profiles>
<profile>
<id>profile_a</id>
<activation>
<<!--默认触发-->
activeByDefault>true</activeByDefault>
<activation>
</properties>
<my_version>1.0</my_version>
<properties>
</profile>
</
profile>
<id>profile_b</id>
<<!--命令行触发,mvn package -P profile_b -->
properties>
<my_version>1.1</my_version>
<properties>
</profile>
</
profile>
<id>profile_c</id>
<<!--指定属性触发,mvn package -Ddebug=123 -->
activation>
<property>
<name>debug</name>
<value>123</value>
<property>
</activation>
</properties>
<my_version>1.2</my_version>
<properties>
</profile>
</
profile>
<id>profile_d</id>
<<!--jdk版本触发,mvn package //jdk版本为1.8-->
activation>
<jdk>1.7</jdk>
<activation>
</properties>
<my_version>1.3</my_version>
<properties>
</profile>
</
profile>
<id>profile_e</id>
<<!--os与cpu架构触发,mvn package //x86的cpu-->
activation>
<os>
<arch>x86</arch>
<os>
</activation>
</properties>
<my_version>1.4</my_version>
<properties>
</profile>
</profiles>
</
version>${my_version}</version>
<project> </
在profiles中声明条件配置,然后可以通过以下方式来激活对应的配置:
- 命令行指定哪个配置文件,-P profile-1,profile-2,
- Profile下的activation触发条件,包括有activeByDefault,property,jdk,os等等。
project>
<
...repositories>
<repository>
<id>global-repo</id>
<
...repository>
</repositories>
</
...profiles>
<profile>
<id>profile-1</id>
<activation>
<activeByDefault>true</activeByDefault>
<activation>
</repositories>
<repository>
<id>profile-1-repo</id>
<
...repository>
</repositories>
</profile>
</profile>
<id>profile-2</id>
<activation>
<activeByDefault>true</activeByDefault>
<activation>
</repositories>
<repository>
<id>profile-2-repo</id>
<
...repository>
</repositories>
</profile>
</
...profiles>
</
...project> </
注意,条件配置是允许同时触发多个配置的,当多个配置文件的配置同时激活的时候,配置就会合并在一起。例如,多个激活的配置文件都有repository配置的时候,最终就有多个repository的配置,那么它们各自的优先级为:
- Profile放在更前面的,优先级更高。
- 没有Profile的外层配置,优先级最低。
8.2 多级配置文件的条件配置
Maven有多级的配置文件,包括有
- 每个项目,在pom.xml中定义
- 每个用户,在%USER_HOME%/.m2/settings.xml中定义
- 全局,在${maven.home}/conf/settings.xml中定义
在额外文件的定义的配置文件(settings.xml或者profiles.xml),为了保证Maven配置文件的可移植性,这类的配置文件中只允许在Profile标签下面进行配置以下内容:
- repositories
- pluginRepositories
- properties
在pom.xml的配置文件中,我们允许在Profile标签下面进行配置以下的内容:
- repositories
- pluginRepositories
- dependencies
- plugins
- properties, (not actually available in the main POM, but used behind the scenes)
- modules
- reports
- reporting
- dependencyManagement
- distributionManagement
build元素下允许包含以下的内容 * defaultGoal * resources * testResources * directory * finalName * filters * pluginManagement * plugins
8.3 条件配置激活查看
mvn help:active-profiles
查看哪些条件配置激活的
mvn help:effective-pom
查看进行了Super BOM,多级配置文件合并以后,最终计算得到的pom配置结果
9 依赖机制
Java的依赖与npm的依赖不同的是,同一个包只能有一个版本号。所以,Maven在选定依赖版本的时候,需要深度遍历每个模块的依赖,然后最终选定一个依赖的包的版本作为整个项目的唯一版本。
9.1 依赖调解
A
├── B
│ └── C
│ └── D 2.0
└── E
└── D 1.0
Maven使用最近路径优先的版本选择,例如,A项目通过依赖B,和依赖E,都最终依赖了D库,但是各自D库的版本都不同。那么,最终会选择D 1.0版本,因为A-E-D的路径最短。
当路径一致的时候,Maven选择依赖放在前面的版本。
A
├── B
│ └── C
│ └── D 2.0
├── E
│ └── D 1.0
│
└── D 2.0
我们可以利用这个机制,在A项目顶部显式指定依赖D 2.0版本,来强行指定Maven最终必须使用D 2.0版本,而不是D 1.0版本。因为A-D的路径最短。
9.2 依赖作用域
project>
<
...dependencies>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
<dependency>
</dependencies>
</
...project> </
依赖有6种作用域,分别是:
- compile,默认值,编译,测试,和打包都包含该依赖
- provided,编译和测试包含该作用域,打包不包含该依赖,依赖由运行时的容器提供。例如:servlet-api,运行项目时,容器已经提供,就不需要Maven重复地引入一遍了。一般就相当于头文件的作用,指示有这些接口,打包时并没有加入这个依赖,运行时由容器提供真正的实现。
- runtime,测试包含该作用域,打包和编译不包含该依赖,依赖由运行时的实现提供。例如:JDBC驱动实现,项目代码编译只需要JDK提供的JDBC接口,只有在测试或运行项目时才需要实现上述接口的具体JDBC驱动。相当于通过动态链接库的方式包含它,编译的时候也不会需要它。编译的时候为什么不需要它,因为程序依赖的可能是一个约定的接口。例如,JDBC驱动,我们用的是JDBC的接口,实际的实现通过程序运行时附近的jar包来提供的。
- test,测试包含该作用域,打包和编译不包含该依赖,依赖在运行时也不会提供。例如,junit依赖,它仅仅是测试时需要用到的。
- system,与provided类似,只不过运行时的依赖需要显式指定
- import,仅仅在dependencyManagement中的依赖需要这样设置,后面会说到
9.3 依赖Optional
project>
<
...dependencies>
<<!-- declare the dependency to be set as optional -->
dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>compile</scope>
<optional>true</optional> <!-- value will be true or false only -->
<dependency>
</dependencies>
</project> </
声明一个依赖为optional的方式,就是在optional标签设置为true
Project-A -> Project-B
Project-X -> Project-A
ProjectA声明ProjectB的依赖为optional的时候,对于ProjectA的编译和打包操作没有任何区别。但是当ProjectX声明ProjectA为依赖的时候,ProjectX仅仅会导入ProjectA依赖,而不会导入ProjectB依赖,因为ProjectA对Project的依赖声明为optional的。可选依赖不会影响直接上级的依赖图计算,但是会影响祖父级及以上的依赖图计算
可选依赖的使用场景是这样的,当一个类似Hibernate的ORM框架,它能透明地支持MySql,PostgresSQL,Sql Server等等这些的驱动层。显然,在编译时,它需要逐一指定包含这些数据库的连接器依赖。但是,对于使用方来说,他可能仅仅是使用MySql的Hibernate,他并不希望因为Hibernate对各种各样的数据库支持,导致所有数据库的连接器依赖都包含进来项目,这样过分多余了。
因此Hibernate可以将这些驱动层依赖都标记为Optional,在它编译的时候,这些驱动层都包含过来。但是对于使用方来说,包含Hibernate依赖的时候,不会自动将Hibernate的那些标记为Optional的驱动层依赖也包含进来。使用方需要显式依赖Hibernate的同时,也要去显式依赖哪些驱动层依赖,以显式的方式避免默认的导入过多依赖。
9.4 依赖Exclusion
project>
<
...dependencies>
<dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>compile</scope>
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<exclusion>
</exclusions>
</dependency>
</dependencies>
</
...project> </
声明exclusion的方法也简单,在一个依赖的exclusions指定groupId和artifactId就可以了,由使用方来显示剔除那些依赖。注意,不需要指定剔除依赖的版本号,因为Maven会从ProjectA对ProjectB的依赖分析中计算得到。
Optional是库开发者为库使用者隐式剔除哪些依赖,而Exclusion是库使用者指定哪些依赖需要显式剔除。Exclusion一般是使用方用来替换一部分依赖实现。例如,我不喜欢库开发者默认指定的Log4j,我更喜欢Slf4j,那么就需要用Exclusion来显式剔除,然后在依赖中加入显式Slf4j的依赖即可。
最后,注意点有:
- Exclusion支持跨多个层级的剔除依赖
- Exclusion是依赖级别的,不是POM级别的,你不能全局指定剔除某个依赖
与npm对比
- provided,runtime,exclusion这种依赖设定在npm是没有的
- test依赖相当于npm的devDependencies
- optional依赖相当于npm的peerDependencies,但是缺少对父版本的版本限制功能
9.5 依赖管理的继承
我们在多模块一节中,探讨了可以用parent标签来继承父级的配置,例如是properties属性。在这里,我们探讨,parent标签可以指定子级的依赖版本号,从而让多个子级统一依赖版本。
9.5.1 parent传递依赖版本
代码在这里
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>multi1</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<
name>multi1</name>
<url>http://maven.apache.org</url>
<
dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
<dependency>
</dependencies>
</dependencyManagement>
</project> </
这是parent/pom.xml文件,通过dependencyManagement来指定需要子级的依赖需要用什么版本号
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">
modelVersion>4.0.0</modelVersion>
<
parent>
<groupId>com.mycompany.app</groupId>
<artifactId>multi1</artifactId>
<version>1.0</version>
<relativePath>../parent/pom.xml</relativePath>
<parent>
</
artifactId>subModule</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<
name>subModule</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencies>
<dependency>
<<!--不需要指定版本号,因为parent的dependencyManagement标签中指定了版本号-->
groupId>junit</groupId>
<artifactId>junit</artifactId>
<dependency>
</dependencies>
</project> </
这是subModule/pom.xml文件,先用parent来继承父级配置,然后在dependency中指定什么依赖就可以了,版本号会自动从父级的dependencyManagement中查找获取。
但是,每个项目只有一个parent标签配置,当我们需要同时继承多个父级的依赖配置的时候怎么办?可以用import
9.5.2 import传递依赖版本
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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.mycompany.app</groupId>
<artifactId>multi1</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<
name>multi1</name>
<url>http://maven.apache.org</url>
<
dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<dependency>
</dependencies>
</dependencyManagement>
</
modules>
<module>../subModule/pom.xml</module>
<modules>
</project> </
这是parent/pom.xml文件,依然用了dependencyManagement来指定子级依赖的版本号,同时使用了modules。
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">
modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>subModule</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<
name>subModule</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</
dependencyManagement>
<dependencies>
<dependency>
<groupId>com.mycompany.app</groupId>
<artifactId>multi1</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
<dependency>
</dependencies>
</dependencyManagement>
</
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<dependency>
</dependencies>
</project> </
这是subModule/pom.xml文件,注意,这次没有再使用parent标签了。而是在dependencyManagement里面用import方式导入父级的项目。然后,我们又能愉快地直接在dependencies指定依赖就行,不需要版本号。
这种方法的优势在于可以同时import多个父项目,但是只继承依赖配置,不继承properties等配置。
9.6 依赖查看
mvn dependency:tree
可以查看依赖树结构
9.7 版本号
1.0-SNAPSHOT
快照版本号的,每次刷新依赖都会重新从远程仓库拉取,以保证最新版本
1.2
普通版本号的,会优先从本地仓库拉取,本地仓库没有该依赖才向远程仓库拉取
10 仓库与镜像
10.1 仓库
Maven有三大远程仓库,包括有:
- MavenCenter,官方最权威,提交较为严格
- JCenter,提交稍为严格,但是已经停止服务了
- JitPack,像npm一样开放的仓库,提交最为开放,所以库也是最多的。
repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<repository>
</repositories> </
我们一般在pom.xml中添加远程仓库地址就可以了,不要在settings.xml添加,以免影响配置文件的可移植性。
10.2 镜像
~/.m2/settings.xml
打开用户所在目录的settings.xml文件
mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirror>
</
<!-- 中央仓库1 -->
mirror>
<id>repo1</id>
<mirrorOf>central</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>https://repo1.maven.org/maven2/</url>
<mirror>
</
<!-- 中央仓库2 -->
mirror>
<id>repo2</id>
<mirrorOf>central</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>https://repo2.maven.org/maven2/</url>
<mirror> </
加入阿里云公共仓库的镜像,注意阿里云只是对应central仓库的,所以mirrorOf的配置为central,不能填写为*。这样会镜像所有的仓库,导致jitpack这样的远程仓库无法正常拉取。
如果你遇到了这类错误,就只能打开代理服务器了
11 发布到远程仓库
我们将演示如何将github仓库的发布到JitPack的远程仓库,主要步骤为:
- 上传代码到github(必须操作),并将模块发布到某一分支(可选操作),并发布Release版本(可选操作)
- Jitpack,手动触发编译(必选操作)
- 项目加入依赖下载使用
11.1 上传代码
先将代码,以Maven的格式上传到github,例如像这个项目,从这个项目中可以看出,这是一个多模块的Maven项目,但是我们不想将整个项目到上传到JitPack,只想上传它的其中一个模块。
npm init
npm install gh-pages --save-dev
于是,我们这里用到node的一个gh-pages的小工具
{
"name": "spring-boot-starter-id-generator",
"version": "1.0.0",
"description": "Java的ID生成器",
"main": "index.js",
"scripts": {
"deploy":"mvn clean && gh-pages -d spring-boot-starter-id-generator -b jitpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/fishedee/spring-boot-starter-id-generator.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/fishedee/spring-boot-starter-id-generator/issues"
},
"homepage": "https://github.com/fishedee/spring-boot-starter-id-generator#readme",
"devDependencies": {
"gh-pages": "^3.2.3"
}
}
在package.json中设置deploy的scripts。这个脚本的意思是,每次先执行mvn clean,然后将spring-boot-starter-id-generator文件夹发布到jitpack分支上
这是我执行的结果
然后在Github首页的右侧,点击Release,再点击Draft a New Release
选择分支为jitpack,填写好版本号,以及发布描述就可以了
这里,我们发布了一个1.4版本的Release。发布Release的目的是让Jitpack知道从哪里拉数据去编译代码
11.2 JitPack触发编译
打开官网,右上角选择Sign In
然后选择,我们的仓库,选择Releases,以及我们的版本号1.4,最后选择Get It就可以了。它就会自动拉代码下来编译打包,成为可被远程仓库加载的模块。
11.3 引用JitPack依赖
repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<repository>
</repositories>
</
dependencies>
<dependency>
<groupId>com.github.fishedee</groupId>
<artifactId>spring-boot-starter-id-generator</artifactId>
<version>1.4</version>
<dependency>
</dependencies> </
最后,在项目的pom.xml文件中填写以上的配置,就在任意项目里面加载我们的这个模块了。为JitPack点个赞,真的简单又好用!
12 其他配置
12.1 环境变量
SPRING_PROFILES_ACTIVE=test mvn test
12.2 Java属性
mvn test -Dspring.profiles.active=test
-D是java在命令行传递属性的方式,也是可以覆盖环境变量的配置一种方式。
mvn -B clean package -Dmaven.test.skip=true -Dautoconfig.skip
也可以用-D来设置系统配置,从而影响Maven的运行配置。系统配置可以看这里
12.3 默认jdk
profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
<activation>
</properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
<properties>
</profile> </
加入默认的jdk
12.4 .m2/setting
IDE读取的是自己打包的Maven包,但是它依然能读取我们的配置。
settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
< xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<
mirror>
</mirrors>
</proxies/>
<activeProfiles/>
<settings> </
在mac下,IDE的Maven工具会读取~/.m2/settings.xml的配置。以上配置写入了阿里云镜像的配置。
12.5 导入到IDE
使用IntelliJ来导入Maven项目,选择Import Project
选择项目的pom.xml即可
启动的时候,要点击任意一个源文件以后,再点击菜单栏的Run运行。
当pom.xml更新了以后,要执行Maven的Reimport来刷新Maven仓库
13 插件开发
开发属于自己的Maven插件,代码在这里
参考资料有:
- https://www.iteye.com/blog/rept-693415
- https://zhuanlan.zhihu.com/p/354308625
- https://www.cnblogs.com/kiwifly/p/12602407.html
13.1 插件开发
使用Idea创建插件项目,选择mojo脚手架即可
mvn archetype:create -DgroupId=* -DartifactId=* -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-mojo
或者使用mvn脚手架生成器,都是一样的
两种方式都可以,但是最重要的是要定义好artifactId,artifactId有一套固定的规范。例如artifactId为cc-maven-plugin,那么IDEA就会识别这个插件名为cc,注意这个规范不能改,否则maven和IDEA会识别不了这个插件。
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/maven-v4_0_0.xsd">
modelVersion>4.0.0</modelVersion>
<groupId>com.fishedee</groupId>
<artifactId>cc-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<version>1.0-SNAPSHOT</version>
<name>myPlugin Maven Mojo</name>
<url>http://maven.apache.org</url>
<
properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
<properties>
</
dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.5.0</version>
<dependency>
</dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.5</version>
<dependency>
</dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.5.0</version>
<dependency>
</dependencies>
</
build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.5.2</version>
<plugin>
</plugins>
</build>
</project> </
更新pom文件,使用以上的依赖
package com.fishedee;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@Mojo(name="go")
public class MyMojo
extends AbstractMojo
{
@Parameter(
= "project",
property = true,
required = true
readonly )
private MavenProject mavenProject;
@Parameter(
= "codegen.outputDir",
property = "src/main/java"
defaultValue )
private String outputDir;
@Parameter(
= "codegen.packageName",
property = true
required )
private String packageName;
public void execute()
throws MojoExecutionException
{
= getLog();
Log log
.info("baseDir "+mavenProject.getBasedir());
log.info("outputDir "+outputDir);
log.info("packageName "+packageName);
log}
}
一个简单的插件代码,使用@Mojo来定义插件的目标,使用@Parameter来获取配置参数
mvn install
最重要的这一步是,将插件安装到本地的mvn库
13.2 使用插件
<?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">
modelVersion>4.0.0</modelVersion>
<
groupId>com.fishedee</groupId>
<artifactId>plugin_sample</artifactId>
<version>1.0-SNAPSHOT</version>
<
name>plugin_sample</name>
<<!-- FIXME change it to the project's website -->
url>http://www.example.com</url>
<
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
<dependency>
</
dependencies>
</
build>
<plugins>
<plugin>
<groupId>com.fishedee</groupId>
<artifactId>cc-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<outputDir>ac</outputDir>
<packageName>com.kk</packageName>
<configuration>
</<!--定义在执行compile的阶段执行插件的hello目标-->
executions>
<execution>
<goals>
<goal>go</goal>
<goals>
</phase>compile</phase>
<execution>
</executions>
</plugin>
</plugins>
</build>
</project> </
然后我们定义一个新的项目,在plugin中指定我们的插件。注意,我们定义了该插件是在compile阶段,顺带执行它的go目标。另外,注意configuration是如何传入配置的。
最后,我们在Maven视图中可以看到这个插件。或者直接对项目compile的时候也会触发这个插件。
这是执行结果,没啥好说的
20 FAQ
20.1 Process terminated
在IDEA中发现了这个错误,肯定是因为pom.xml有语法错误了
改掉语法错误,重新reload就可以了
20.2 IDEA多模块无法识别
在多模块项目导入到IDEA以后,有时候修改了其中一个模块的版本号以后,其他模块即使修改了版本号也无法正常编译。
解决办法是在,顶级的POM文件中,选择Reload Project就可以了
20.3 java.util.zip.ZipException:invalid distance distance too far back
启动SpringBoot的时候报出以上错误,将~/.m2/repository里面的本地缓存包删掉,重新mvn clean 和mvn install就可以了。
20.4 IDEA无法识别多模块的类
导入一个多模块到IDEA以后,一个常见的问题是,编译可以成功,但是IDEA却总是提示这个类找不到,哪个类找不到。Module Settings里面对文件的配置都是正确的,问题在于缺少.iml文件,这听说是IDEA的bug。
mvn idea:module
在顶层pom.xml文件,输入以上命令即可补充缺失的.iml文件。
20.5 IDEA启动报错,无法找到org.springframework.boot
idea开发 SpringBoot启动报错 程序包org.springframework.boot不存在,而使用maven 命令直接执行时无任何问题,不会报错。
看这里
![](/assets/img/2022-02-21-14-23-20.png
解决方法是将启动分发给Maven来做,但是这样做会有很多额外的问题,例如是停止Maven的时候,并不能关闭程序,导致网络端口被占用。
mvn idea:idea
也有人说通过以上命令来解决,看这里
20.6 Maven打包不进行单元测试
mvn -B clean package -Dmaven.test.skip=true
使用系统参数,使得package的过程跳过单元测试的步骤
20.7 Maven单元测试的时候指定SpringBoot和Maven参数
mvn test -Dspring.profiles.active=test -Dmaven.test.failure.ignore=true
使用-D的系统配置参数就可以做到了,不需要用环境变量
20.8 Maven指定测试时的资源文件夹
build>
<testResources>
<testResource>
<directory>src/test/java/com/fishedee/erp</directory>
<testResource>
</testResources>
</build> </
在build里面指定testResources就可以了
20.9 Maven解决编译的乱码问题
properties>
<start-class>com.fishedee.erp.App</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<properties>
</build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<propertiesEncoding>UTF-8</propertiesEncoding>
<encoding>UTF-8</encoding>
<configuration>
</plugin>
</plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<configuration>
</plugin>
</pulugins>
</build> </
指定resources与compiler,以及默认的project.build.sourceEncoding
21 总结
maven有固定的项目目录格式,比ant严谨多了。
上述Demo的代码在这里
参考资料:
- 本文作者: fishedee
- 版权声明: 本博客所有文章均采用 CC BY-NC-SA 3.0 CN 许可协议,转载必须注明出处!