一、从Hello world说起
Maven是一个项目管理工具,在安装好并配置系统环境变量后,在终端执行:mvn -version
,保证输出以下内容:
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T02:33:14+08:00) Maven home: /usr/local/soft/maven Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/local/soft/jdk8/jre Default locale: zh_CN, platform encoding: UTF-8 OS name: "linux", version: "4.15.0-29deepin-generic", arch: "amd64", family: "unix"
首先约定好开发工具为IDEA,在IDEA中配置好Maven,怎么配置就不多说了。
1. 创建项目
打开IDEA,点击菜单File -> New -> Project...,然后在左侧选择Maven后Next,填写好GAV
,然后填写好项目路径等创建完成。
GAV:是groupId、artifactId、version的缩写。
groupId:一般是公司的域名倒置,artifactId:项目名称或模块名称,version:版本号
打开项目会在IDEA左侧看到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"> <modelVersion>4.0.0</modelVersion> <groupId>com.ajn</groupId> <artifactId>spring-boot</artifactId> <version>1.0.0-SNAPSHOT</version> </project>
2. 添加依赖
首先添加spring相关父pom配置,我个人不建议使用spring-boot-starter-parent
而使用spring-boot-dependencies
,我们只需要继承父pom的依赖,不需要其他配置,项目的掌控权要牢牢握在自己手里。然后添加spring-boot-starter-web
依赖。
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.4.RELEASE</version> </parent> <groupId>com.ajn</groupId> <artifactId>spring-boot</artifactId> <version>1.0.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project> 3. 添加插件
3. 添加插件
添加完插件和部分配置属性,我们可以看到添加的插件有:
maven-compiler-plugin
:编译maven-jar-plugin
:打包maven-assembly-plugin
:自定义打包maven-source-plugin
:打包源码maven-release-plugin
:版本发布
Maven是基于微核心插件化思想设计的,功能全部体现在插件上,我们在后面详细讲到。还添加的属性有:
project.build.sourceEncoding
:编码project.reporting.outputEncoding
:编码java.version
:JDK版本maven.test.skip
:跳过测试maven.install.skip
:跳过安装到本地仓库maven.deploy.skip
:跳过发布到远程仓库(私服)
一般Web项目跳过 install 和 deploy,因为它不需要被别人依赖。需要被别人依赖的API项目,都应将 Jar 包和源码包发布到私服,供调用方使用。
Profile可以用来区分环境,具体用法以后说,添加的Profile有:
test
:表示测试环境prod
:表示生产环境
到这差不多pom的配置就完成了,除了以后添加依赖。
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.4.RELEASE</version> </parent> <groupId>com.ajn</groupId> <artifactId>spring-boot</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <maven.test.skip>true</maven.test.skip> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <finalName>${project.name}</finalName> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>*</include> </includes> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <addMavenDescriptor>false</addMavenDescriptor> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.ajn.spring.boot.Application</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <descriptor>src/main/config/package.xml</descriptor> </descriptors> </configuration> <executions> <execution> <id>mvn-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-source-plugin</artifactId> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> </plugin> </plugins> </build> <profiles> <profile> <id>test</id> <properties> <profile.active>test</profile.active> </properties> </profile> <profile> <id>prod</id> <properties> <profile.active>prod</profile.active> </properties> </profile> </profiles> </project>
添加assembly
插件打包配置,根据maven-assembly-plugin
插件配置的文件路径,在main
下新建文件夹config
,然后新建文件package.xml
,插件配置用法后面会说到。
package com.ajn.spring.boot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
每个项目都可以有自己定制的打包方式,并不拘泥于某类插件。
4. 添加代码
到这里我们项目已经搭建好了并添加Springboot的依赖,就可以直接写代码启动测试。在java
目录下创建包com.ajn.spring.boot
,在包下新建类Application.java
:
package com.ajn.spring.boot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
在包com.ajn.spring.boot
下新建包controller
,在包下新建类HelloController.java
:
package com.ajn.spring.boot.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("hello") public class HelloController { @GetMapping public String hello() { return "Hello world"; } }
运行主方法来启动项目,输出日志中最后一行如下表示启动成功。
2018-11-28 20:23:00.754 INFO 22671 --- [main] com.ajn.spring.boot.Application: Started Application in 1.296 seconds (JVM running for 1.64)
然后用浏览器访问 http://localhost:8080/hello
,返回:
Hello world
到这里我们只是先把项目跑起来,在Maven中配置的插件也似乎没有用到,后面会深入讲解Maven是如何管理和构建项目的。
二、Maven
Apache Maven的定义:Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后Maven可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。
它运用的设计思想:
- 继承思想:子pom可以继承父pom的配置和属性。
- 仓库思想:本地仓库、远程仓库(私服)
- 约定大于配置:功能配置和指令约定俗成
- 微核心插件化:本身安装包核心不大,插件也像依赖一样可由仓库管理
1. 目录结构
Maven的基本目录结构,不管是自动生成,还是自己手动创建,都需按照这种格式,当然你可以自定义,但不建议这样做,会增加更多没必要的配置。
spring-boot ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ └── resources │ └── test │ └── java │ └── resources └── target
spring-boot
:项目根目录src/main/java
:源代码目录src/main/resources
:资源文件目录,默认打包至类路径(classpath)下src/test/java
:测试源代码目录src/test/resources
:测试资源文件目录pom.xml
:(Project Object Model),项目对象模型配置文件
2. pom
Maven把Java项目抽象成一个项目对象模型(Project Object Model),而pom.xml
配置就是这抽象过程的体现。它不仅能把源代码编译成字节码,这些配置还包含:项目的描述信息,项目唯一的坐标,描述项目的属性,依赖的其他项目等等。
2.1 配置详解
2.1.1 基本配置
modelVersion
:pom模型版本,maven2只能为4.0.0groupId
:项目组编号artifactId
:项目编号version
:版本号
packaging
:打包方式,有pom,jar,war,maven-plugin
等等,缺省值:jar
properties
:定义常量属性,可以在pom其他地方通过${}
来引用name
:项目名,Maven产生的文档使用description
:项目描述url
:主页URL,maven文档中保存developers
:开发人员contributors
:其他贡献人员
2.1.2 依赖配置
parent
:项目继承中指定父项目,子节点为GAV
1modules
:项目聚合中指定子模块项目,子节点为module
dependencies
:依赖配置,需提供引用的依赖的GAV
,可以从父项目继承dependencyManagement
:依赖管理配置,子节点配置dependencies
,管理子项目依赖
2.1.3 构建配置
build
:构建配置
finalName
:build目标文件的名称,缺省值:${artifactId}-${version}
resources
:资源拷贝插件配置plugins
:插件配置filters
:定义过滤的.properties
文件,该文件的配置会应用到resources
中
reporting
:发布配置profiles
:配置文件组,可以配置多种配置,按需激活其中一种
2.1.4 仓库配置
repositories
:配置获取依赖的仓库,一般配置私服中的中央仓库,缺省值:Maven中央仓库pluginRepositories
:配置获取插件的仓库,和repositories
意思一样distributionManagement
:发布管理,可以配置依赖发布的仓库scm
:软件配置管理,可以配置web站点和CVS
3. 依赖
3.1 依赖范围(scope)
3.1.1 compile
编译范围:scope
配置的缺省值,编译范围依赖在所有的classpath
中可用,同时也会被打包。
3.1.2 provided
已提供范围:该类型依赖只有在当JDK或者一个容器已提供该依赖之后才使用,已提供范围的依赖在编译classpath
(不是运行时)可用,它们不是传递性的,也不会被打包。
3.1.3 runtime
运行时范围:该类型依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如:你可能在编译的时候只需要JDBC API jar,而只有在运行的时候才需要JDBC驱动实现。
3.1.4 test
测试范围:该类型依赖在一般的编译和运行时都不需要,只有在测试编译和测试运行阶段可用。
3.1.5 system
系统范围:该依赖范围与provided
类似,但是必须显式的提供一个对于本地系统中jar文件的路径。这么做是为了允许基于本地对象编译,而这些对象是系统类库的一部分。
3.2 传递性依赖
一个传递性依赖就是对于一个依赖的依赖。如果project-a依赖于project-b,后者接着依赖于project-c,那么project-c就被认为是project-a的传递性依赖。如果project-c依赖于project-d,那么project-d就也被认为是project-a的传递性依赖。
3.3 冲突解决(exclusions)
有很多时候你需要排除一个传递性依赖,比如当你依赖于一个项目,后者又继而依赖于另外一个项目,但你的希望是,要么整个的排除这个传递性依赖,要么用另外一个提供同样功能的依赖来替代这个传递性依赖。比如:
<dependency> <groupId>org.sonatype.mavenbook</groupId> <artifactId>project-a</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>org.sonatype.mavenbook</groupId> <artifactId>project-b</artifactId> </exclusion> </exclusions> </dependency>
3.4 依赖管理(dependencyManagement)
在一个系统中有很多相互关联个Maven项目,如果每一个使用如MySQL数据库驱动依赖的项目都需要独立的列出该依赖的版本,在你需要升级到一个新版本的时候你就会遇到问题。由于这些版本号分散在你的项目树中,你需要手工的编写每一个引用该依赖的pom.xml
,确保每个地方的版本号都更改了。此时我们可以在父项目中使用dependencyManagement
来集中管理依赖。比如:
<!-- 父项目 --> <project> [...] <dependencyManagement> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.2</version> </dependency> </dependencies> </dependencyManagement> [...] </project> <!-- 子项目 --> <project> [...] <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> [...] </project>
4. 生命周期
在前面Maven的定义中我们说到它是运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑,一个生命周期是一些阶段的序列,一个生命周期阶绑定了零个或多个目标(goal)。我们可以使用mvn
指令直接运行生命周期和目标。
- 运行生命周期:
mvn clean, mvn package, mvn clean package
等等 - 运行插件目标:
mvn clean:clean, mvn resources:resources
等等
在项目根目录下我们可以直接在终端运行
mvn clean
,如果一个项目自己配置了多个pom.xml
文件,需要指定执行的pom,使用mvn clean -f pomTest.xml
4.1 clean
- phases:
clean
- goals:
clean:clean
该生命周期用于移除你项目构建的文件,默认是删除 project.build.directory, project.build.outputDirectory, project.build.testOutputDirectory
和 project.reporting.outputDirectory
目录。
4.2 compile
- phases:
process-resources -> compile
- goals:
resources:resources -> compiler:compile
该生命周期用于编译源码和测试源码,默认编译source
和target
的JDK配置是1.6,如果想使用更高版本,需要自己手动配置。
4.3 package
- phases:
process-resources -> compile -> process-classes -> process-test-resources -> test-compile -> test -> prepare-package -> package
- goals:
resources:resources -> compiler:compile -> resources:testResources -> compiler:testCompile -> surefire:test ->jar:jar
该生命周期用于项目打包,默认打的是jar
类型。
4.4 install
- phases:
process-resources -> compile -> process-classes -> process-test-resources -> test-compile -> test -> prepare-package -> package -> pre-integration-test -> integration-test -> post-integration-test -> verify -> install
- goals:
resources:resources -> compiler:compile -> resources:testResources -> compiler:testCompile -> surefire:test -> jar:jar -> install:install
该生命周期用于将你打好的包安装到本地仓库,会根据项目的GAV来确定它在仓库中的路径。本地仓库目录可以在Maven的配置文件setting.xml
中的节点配置,缺省值:
~/.m2/repository
。
打包执行顺序图:
打包日志分析:
[INFO] Scanning for projects... [INFO] [INFO] ----------------< com.ajn.springboot:study-springboot >----------------- [INFO] Building study-springboot 1.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ study-springboot --- [INFO] Using 'UTF-8'encoding to copy filtered resources. [INFO] Copying 2resources [INFO] Copying 3resources [INFO] Copying 1resource [INFO] [INFO] --- maven-compiler-plugin:3.7.0:compile (default-compile) @ study-springboot --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 32source files to /home/ajn/Code/study/study-springboot/target/classes [INFO] [INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ study-springboot --- [INFO] Not copying test resources [INFO] [INFO] --- maven-compiler-plugin:3.7.0:testCompile (default-testCompile) @ study-springboot --- [INFO] Not compiling test sources [INFO] [INFO] --- maven-surefire-plugin:2.21.0:test (default-test) @ study-springboot --- [INFO] Tests are skipped. [INFO] [INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ study-springboot --- [INFO] Building jar: /home/ajn/Code/study/study-springboot/target/study-springboot.jar [INFO] [INFO] --- maven-assembly-plugin:3.1.0:single (mvn-assembly) @ study-springboot --- [INFO] Reading assembly descriptor: src/main/config/package.xml [INFO] Building zip: /home/ajn/Code/study/study-springboot/target/study-springboot-bin.zip [INFO] [INFO] --- maven-source-plugin:3.0.1:jar-no-fork (attach-sources) @ study-springboot --- [INFO] Building jar: /home/ajn/Code/study/study-springboot/target/study-springboot-sources.jar [INFO] [INFO] --- maven-install-plugin:2.5.2:install (default-install) @ study-springboot --- [INFO] Installing /home/ajn/Code/study/study-springboot/target/study-springboot.jar to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT.jar [INFO] Installing /home/ajn/Code/study/study-springboot/pom.xml to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT.pom [INFO] Installing /home/ajn/Code/study/study-springboot/target/study-springboot-bin.zip to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT-bin.zip [INFO] Installing /home/ajn/Code/study/study-springboot/target/study-springboot-sources.jar to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT-sources.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.022s [INFO] Finished at: 2018-12-02T22:50:31+08:00 [INFO] ------------------------------------------------------------------------
上面为我们前面搭建好的项目执行mvn install
时打印的日志,从日志中可以看出执行的goal
,完全是按照默认或者我们配置的生命周期阶段的顺序,比如:配置的maven-assembly-plugin
打包插件,执行的目标(goal
)是single
,绑定的生命周期阶段(phase
)是package
,所以在执行目标jar:jar
的后面。在pom中配置跳过测试true
。