Maven笔记——基础篇
Maven笔记——基础篇

Maven笔记——基础篇

Maven 基础

部分内容来自:www.yjxxt.com

简介

Maven 这个词可以翻译为“专家”,内行”。作为 Apache 组织中的一个颇为成功的开源项目 Maven 主要服务于基于 java 平台的项目构建,依赖管理和项目信息管理。

Maven 的四大特性

1,管理系统

Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理, jar 升级时修改配置文件即可。在 Java 世界中,可以用 groupld、artifactld、version 组成的 Coordination (坐标)唯一标识一个依赖

任何基于 Maven 构建的项目自身也必须定义这三项属性,生成的包可以是 jar 包,也可以是 war 包或者 jar 包。个典型的依赖引用如下所示:

坐标属性的理解:

Maven 坐标为各种组件引入了秩序,任何一个组件都必须明确定义自己的坐标。

  • groupld

定义当前 Maven 项目隶属的实际项目-公司名称。(jar 包所在仓库路径) 由于 Maven 中模块的概念,因此一个实际项目往往会被划分为很多模块。比如 spring 是一个实际项目,其对应的 Maven 模块会有很多,如 springcore,spring-webmvc 等

  • artifactld

该元素定义实际项目中的一个 Maven 模块-项目名,推荐的做法是使用实际项目名称作为 artifactld 的前缀。

比如: spring-bean,spring-webmvc 等。

  • version

该元素定义 Maven 项目当前所处的版本

2,多模块构建

项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。

在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用<modules>标签来定义一组子模块。parent POM 不会有什么实际构建产出。而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。

3,一致的项目结构

Ant 时代大家创建 ava 项目目录时比较随意,然后通过 Ant 配置指定哪些属于 source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion over configuration (约定大于配置),其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题

手动构建一个 maven 项目

新建目录如下

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/maven-v4_0_0.xsd">
  
  <modelVersion>4.0.0</modelVersion>
  
  <groupId>com.companyname.bank</groupId>
  <artifactId>MavenDemo1</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>MavenDemo1</name>
  <url>http://maven.apache.org</url>
  
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  
</project>

进入项目根目录,即 pom 文件所在目录:

在地址栏 cmd,输入 mvn compile

第一次时间会比较长,耐心等待下,后面就两三秒的事

出现 build success 即成功:

然后输入执行命令:

mvn exec:java -Dexec.mainClass="com.companyname.hello.hello"

第一次稍等下,后面就快了,可以看到成功打印 hello world:

Maven 常用命令

maven 命令如下:

mvn [plugin-name]:[goal-name]

命令代表的含义:执行 plugin-name 插件的 goal-name 目标

命令描述
mvn clean对项目进行清理,删除 target 目录下编译的内容
mvn compile编译项目源代码
mvn test对项目进行运行测试
mvn package打包文件并存放到项目的 target 目录下,打包好的文件通常都是编译后的 class 文件
mvn install在本地仓库生成仓库的安装包,可供其他项目引用,同时打包后的文件放到项目的 target 目录下
mvn site生成项目相关信息的网站
Mvn dependency:tree打印出项目的整个依赖树
Mvn archetype;generate创建 Maven 的普通 java 项目
mvn tomcat7:run在 tomcat 容器中运行 web 应用
mvn jetty:run调用 Jetty 插件的 Run 目标在 Jetty Servlet 容器中启动 web 应用

注意:运行 maven 命令的时候,需要首先定位到 maven 项目的目录,也就是项目的 pom.xml 文件所在的目录。否则,必须通过参数来指定项目的目录

命令参数

  1. -D:传入属性参数

例如:

mvn package -Dmaven.test.skip=true

以-D 开头,将 maven.test.skip 的值设为 true,就是告诉 maven 打包的时候跳过单元测试。同理 mvn deploy-Dmaven.test.skip=true 代表部署项目并跳过单元测试。

  1. -P: 使用指定的 Profile 配置

比如项目开发需要有多个环境,一般为开发,测试,预发,正式 4 个环境,每个环境对应的数据库、配置文件都不同,可以通过该参数来配置环境

通过 maven 可以实现按不同环境进行打包部署,例如:

mvn package -Pdev -Dmaven.test.skip=true

表示打包本地环境,并跳过单元测试

IDEA 集成 maven 环境

进入 settings,修改 home 和 settings 文件路径:

创建普通 java 项目

新建项目,配置如下:

刚进来可能没有 resources 目录

手动创建一个,和 main 同级:

记得把该目录设置为资源目录:

同理,test 下也要有 resources,设置为测试资源目录:

配置编译环境

注意 run 行写“compile”

点击 debug 图标,开始编译,出现报错:

和上面一样,pom 文件加上 jdk 信息:

<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>

重新编译:

成功,运行

创建打包环境

点击 debug 图标,开始打包,完成后,在 out 目录可以看到被打包的 jar 文件:

创建 JavaWeb 项目

javaweb 项目需要服务器插件支持,目前主要有 jetty 和 tomcat 服务器

选择 webapp:

jetty 服务器

build 里面配置:

<build>
  <finalName>mavenProDemo2</finalName>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.3.7.v20160115</version>
        <configuration>
          <scanIntervalSeconds>1</scanIntervalSeconds>
          <httpConnector>
            <port>8080</port>
            <host>localhost</host>
          </httpConnector>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

配置 run 为 jetty:run

运行项目,浏览器输入 http://localhost:8080/index.jsp

成功 hello world

tomcat 服务器

<build>
  <finalName>mavenProDemo2</finalName>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat6-maven-plugin</artifactId>
        <version>2.2</version>
      </plugin>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

启动成功后控制台 log 会告诉你访问地址:

访问成功 hello world:

Maven 仓库的基本概念

当第一次运行 Maven 命令的时候,你需要 Internet 链接,因为它需要从网上下载一些文件。那么它从哪里下载呢?它是从 Maven 默认的远程库下载的。这个远程仓库有 Maven 的核心插件和可供下载的 jar 文件。

对于 Maven 来说,仓库只分为两类: 本地仓库和远程仓库

当 Maven 根据坐标寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在,则直接使用;如果本地没有,Maven 就会去远程仓库查找,发现需要的构件之后,下载到本地仓库再使用。如果本地仓库和远程仓库都没有,Maven 就会报错。

远程仓库分为三种: 中央仓库,私服, 其他公共库

中央仓库是默认配置下,Maven 下载 jar 包的地方

私服是另一种特殊的远程仓库,为了节省带宽和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有外部的远程仓库。内部的项目还能部署到私服上供其他项目使用。

一般来说,在 Maven 项目目录下,没有诸如 lib/这样用来存放依赖文件的目录。当 Maven 在执行编译或测试时,如果需要使用依赖文件,它总是基于坐标使用本地仓库的依赖文件。

默认情况下,每个用户在自己的用户目录下都有一个路径名为。m2/repository/的仓库目录。有时候,因为某些原因(比如 c 盘空间不足),需要修改本地仓库目录地址。

对于仓库路径的修改,可以通过 maven 配置文件 conf 目录下 settings.xml 来指定仓库路径

中央仓库

由于原始的本地仓库是空的,maven 必须知道至少一个可用的远程仓库,才能执行 maven 命令的时候下载到需要的构件。中央仓库就是这样一个默认的远程仓库。

maven-model-builder-3.3.9.jar maven 自动的 jar 中包含了一个超级 POM。定义了默认中央仓库的位置中央仓库包含了 2000 多个开源项目,接收每天 1 亿次以上的访问。

私服

私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的 maven 用户使用。当 maven 需要下载构件时,它去私服当中找,如果私服没有,则从外部远程仓库下载,并缓存在私服上,再为 maven 提供。

此外,一些无法从外部仓库下载的构件也能从本地上传到私服提供局域网中其他人使用配置方式项目 pom.xml 配置

实际项目中如何导入其他依赖

实际一个项目可能需要很多 jar,手写 pom 很麻烦,直接使用 mvn repository 仓库即可:

https://mvnrepository.com/

进去直接搜索所需要的 jar,点进去,找最多使用的版本,点进去:

复制<dependency>依赖模板即可

Maven 环境下构建多模块项目

使用 maven 提供的多模块构建的特性完成 maven 环境下多个模块的项目的管理与构建

这里以四个模块为例来搭建项目,以达到通俗易懂的初衷

模块 maven_parent -- 基模块,就是常说的parent (pom)
模块 maven_dao--数据库的访问层,例如jdbc操作 (jar)
模块 maven_service -- 项目的业务逻辑层 (jar)
模块 maven_contro11er -- 用来接收请求,响应数据 (war)

1,new-project,然后不要点 maven 直接 new project 页面,新版 maven 是这样的

直接点 create 就好:

可以看到父模块的 pom 文件:

然后右键父模块,新建模块,archetype 为 quickstart:

service 模块同样步骤,controller 模块需要选择 webapp,完成后可以看到父模块 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>

    <groupId>org.example</groupId>
    <artifactId>mavenDemo3_parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>maven_dao</module>        <module>maven_service</module>        <module>maven_controller</module>
    </modules>


    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

配置模块间依赖关系

假设 service 想要引入 dao 模块,使用其中的方法,可以通过 diea 自动引入,写一 UserDao userDao=new UserDao();然后点击引入 dependency,或者手动引入,打开 dao 层的 pom 文件,复制:

<groupId>org.example</groupId>
<artifactId>maven_dao</artifactId>
<version>1.0-SNAPSHOT</version>

然后再 service 层的 pom 文件添加一个新的 dependency,粘贴进去即可:

controller 实现 servlet:

package com.ozy.controller;
import javax.servlet.http.HttpServlet;
public class UserServlet extends HttpServlet{

}

这里有个问题,命名 pom 文件以及引入,却点不出 HttpServlet,打开本地 respository 一看,没有 servlet 的 api:

可以看到根本没有下载,这时候打开 idea 的右边的 maven 图标,点一下刷新(ctrl+shift+o)就好了:

然后重写 service 方法,下面粘出三个层的方法:

dao:
public class UserDao{
    public static void test(){
        System.out.println( "dao..." );
    }
}


service:
public class UserService{ 
    UserDao userDao=new UserDao();
    public void test(){
        System.out.println( "service..." );
        userDao.test();
    }
}


controller:
@WebServlet("/user")
public class UserServlet extends HttpServlet{
    UserService userService=new UserService();
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("controller...");
        userService.test();
    }
}

然后需要在 controller 层配置 tomcat 服务器,在 build 标签内添加:

<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <path>/web</path>
        <port>8080</port>
        <uriEncoding>UTF-8</uriEncoding>
        <server>tomcat7</server>
    </configuration>
</plugin>

如果写上去显示红的,点一下 maven 刷新按钮即可:

接着给三个模块和父模块配置 maven install,挨个执行下

当执行到 controller 的 install 时,报错:

Cannot access defaults field of Properties

解决方法:在 controller 的 pom 加入:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.1</version>
            </plugin>
        </plugins>
    </build>

成功 install

最后,配置 tomcat 启动:

最后,配上完整的 controller 的 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/maven-v4_0_0.xsd">
    <parent>
        <artifactId>mavenDemo3_parent</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>maven_controller</artifactId>
    <packaging>war</packaging>
    <name>maven_controller Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
<!--        引入service模块依赖-->
        <dependency>
            <artifactId>maven_service</artifactId>
            <groupId>org.example</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
<!--        引入servlet的api-->
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>
    <build>
        <finalName>maven_controller</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/web</path><port>8080</port>
                    <uriEncoding>UTF-8</uriEncoding>
                    <server>tomcat7</server>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.1</version>
            </plugin>
        </plugins>
    </build>
</project>

启动项目,访问 http://localhost:8080/web,显示 hello world,访问 http://localhost:8080/web/user,后端控制台显示:

成功

Maven 的打包操作

对于企业级项目,无论更进行本地测试,还是测试环境测试以及最终的项目上线,都会涉及项目的打包操作。对于每个环境下的项目打包,对应的项目所需要的配置资源都会有所区别,实现打包的方式有很多种,可以通过 ant,或者通过 idea 自带的打包功能实现项目打包,但当项目很大并且需要的外界配置很多时,此时打包的配置就会异常复杂,对于 maven 项目,我们可以用过 pom.xml 配置的方式来实现打包时的环境选择,相比较其他形式打包工具,通过 maven 只需要通过简单的配置,就可以轻松完成不同环境下项目的整体打包。

比如下面这样一个项目,项目中配置了不同环境下项目所需要的配置文件

添加 profile 配置

放在 pom 下:

  <profiles>
    <profile>
      <id>dev</id>
      <properties>
        <env>dev</env>
      </properties>
<!--      表示dev是默认环境-->
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
    </profile>
    <profile>
      <id>test</id>
      <properties>
        <env>test</env>
      </properties>
    </profile>
    <profile>
      <id>product</id>
      <properties>
        <env>product</env>
      </properties>
    </profile>
  </profiles>

设置资源文件配置

放在 build 下:

    <resources>
      <resource>
        <directory>src/main/resources/${env}</directory>
      </resource>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
          <include>**/*.tld</include>
        </includes>
<!--        不进行过滤-->
        <filtering>false</filtering>
      </resource>
    </resources>

默认环境打包

可以看到生成了 war 包:

打开其中的 bean.xml 可以看到是 dev 环境:

指定环境打包

测试生产环境打包:

clean compile package -Pproduct -Dmaven.test.skip=true

后面的-Dmaven.test.skip=true 表示跳过测试,能减少 war 包体积

生成的 war 包可以看到环境是 product

依赖的基本配置

根元素 project 下的 dependencies 可以包含多个 dependence 元素,以声明多个依赖。每个依赖都应该包含以下元素:

  1. groupld,artifactld,version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven 根据坐标才能找到需要的依赖
  2. Type: 依赖的类型,大部分情况下不需要声明。 默认值为 jar
  3. Scope: 依赖范围 (compile,test,provided,runtime,system)
    1. compile: 编译依赖范围
    2. test: 测试依赖范围。
    3. provided: 已提供依赖范围
    4. runtime: 运行时依赖范围
    5. system:系统依赖范围。
  4. Optional:标记依赖是否可选
  5. Exclusions:用来排除传递性依赖。

依赖范围

首先需要知道,Maven 在编译项目主代码的时候需要使用一套 lasspath。比如: 编译项目代码的时候需要用到 spring-core,该文件以依赖的方式被引入到 classpath 中。其次,Maven 在执行测试的时候会使用另外一套 classpath。如: junit。

最后在实际运行项目时,又会使用一套 classpath,spring-core 需要在该 dasspath 中,而 iunit 不需要。

那么依赖范围就是用来控制依赖与这三种 classpath(编译 classpath,测试 classpath,运行时 casspath)的关系 Maven 有以下几种依赖范围:

  • Compile 编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的 Maven 依赖,对于编译,测试,运行都有效。
  • Test: 测试依赖范围。 只在测试的时候需要。比如 jiunit
  • Provided: 已提供依赖范围。 使用此依赖范围的 Maven 依赖,对于编译和测试有效,但在运行时无效。典型的例子是 servlet-APl,编译和测试项目的需要,但在运行项目时,由于容器已经提供,就不需要 Maven 重复地引入一遍。
  • Runtime: 运行时依赖范用。使用此依赖范围的 Maven 依赖,对于测试和运行有效,但在编译代码时无效典型的例子是:idbc 驱动程序,项目主代码的编译只需要 idk 提供的 idbc 接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体 idbc 驱动
  • System:系统依赖范围。一般不使用

传递性依赖

传递依赖机制,让我们在使用某个 jar 的时候就不用去考虑它依赖了什么。也不用担心引入多余的依赖。

Maven 会解析各个直接依赖的 POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前项目中。

注意: 传递依赖有可能产生冲突! !

冲突场景:

A–>B—>C (v2.0)

A–>E—>C (v1.0)

如果 A 下同时存在两个不同 version 的 C,冲突!! (选取同时适合 A、B 的版本)

如果出现此问题,需要一个<exclusion>标签来排除一个 C 依赖版本

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Index