18.Maven

18.1.介绍
  • Maven 是一个项目管理工具,它包含了一个项目对象模型 (POM:Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。
  • Maven仓库:
    • 本地仓库 :用来存储从远程仓库或中央仓库下载的插件和 jar 包,项目使用一些插件或 jar 包,优先从本地仓库查找
      • 默认本地仓库位置在 u s e r . d i r / . m 2 / r e p o s i t o r y , {user.dir}/.m2/repository, user.dir/.m2/repository{user.dir}表示 windows 用户目录。
      • 修改方式:在MAVE_HOME/conf/settings.xml 文件中配置本地仓库位置
        <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 | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository -->
            <!-- 添加本地仓库 -->
            <localRepository>D:\repository</localRepository>
        
    • 远程仓库:如果本地需要插件或者 jar 包,本地仓库没有,默认去远程仓库下载。
      • 远程仓库可以在互联网内也可以在局域网内。
      • 设置国内 maven 镜像:因为maven中央仓库位于国外,所以如果本地仓库没有对应的 jar 包,那么需要从中央仓库去***比较慢。
        • 在MAVE_HOME/conf/settings.xml 文件中配置本地仓库位置
        <mirrors>
            <!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | <mirror> <id>mirrorId</id> <mirrorOf>repositoryId</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://my.repository.com/repo/path</url> </mirror> -->
            <!-- 添加国内镜像 -->
            <mirror>
                <id>nexus-aliyun</id>
                <mirrorOf>central</mirrorOf>
                <name>Nexus aliyun</name>
                <url>http://maven.aliyun.com/nexus/content/groups/public</url>
            </mirror>
        </mirrors>
        
    • 中央仓库 :在 maven 软件中内置一个远程仓库地址http://repo1.maven.org/maven2,它是中央仓库,服务于整个互联网,它是由 Maven 团队自己维护,里面存储了非常全的 jar 包,它包含了世界上大部分流行的开源项目构件。
18.2.环境配置
  • 下载并解压Maven压缩包,到某个目录下
  • 配置 MAVEN_HOME ,变量值就是你的 maven 安装 的路径(bin 目录之前一级目录)
  • 通过 mvn -v 命令检查 maven 是否安装成功,看到 maven 的版本为 3.5.2 及 java 版本为 1.8 即为安装成功。
    • 打开 cmd 命令,输入 mvn –v 命令
  • Maven文件结构:
    • bin:存放了 maven 的命令,主要是二进制文件,比如我们前面用到的 mvn tomcat:run
    • boot:存放了一些 maven 本身的引导程序,如类加载器等
    • conf:存放了 maven 的一些配置文件,如 setting.xml 文件
    • lib:存放了 maven 本身运行所需的一些 jar 包
18.3.项目构建
18.3.1.演示
  • 在桌面上新建了一个maven目录,在maven目录中新建了一个hello目录表示一个应用,编写java文件
  • 报错原因是缺少一个pom.xml文件,maven在执行之前会到一个叫做pom.xml文件中读取一些数据
  • 新建一个pom.xml文件
    <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> <!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如 com.companyname.project-group,maven 会将该项目打成的 jar 包放本地路径: /com/companyname/project-group -->
        <groupId>com.cskaoyan.project</groupId> <!-- 项目的唯一ID,一个groupId下面可能多个项目,就是依靠于artifactId来区分的 -->
        <artifactId>hello</artifactId> <!-- 版本号 -->
        <version>1.0</version>
        
        <!-- 依赖坐标 -->
        <dependencies>
            <!-- 依赖设置 -->
            <dependency>
                <!-- 依赖组织名称 -->
                <groupId>junit</groupId>
                <!-- 依赖项目名称 -->
                <artifactId>junit</artifactId>
                <!-- 依赖版本名称 -->
                <version>4.12</version>
                <!-- 依赖范围:test 包下依赖该设置,若没有此标签默认是compile -->
                <scope>test</scope>
            </dependency>
    	</dependencies>
        
    </project>
    
18.3.2.Maven工程结构
  • .settings:工具生成的文件
  • src:源代码
    • main
      • java:存放项目的.java 文件
      • resource:存放项目资源文件,如 spring, hibernate 配置文件
      • webapp:页面素材
    • test
      • java:存放所有单元测试.java 文件,如 JUnit 测试类
      • resource:测试资源文件
  • target:项目输出位置,编译后的 class 文件会输出到此目录
  • pom.xml:Maven工程的配置信息,如坐标
18.3.3.项目的一键构建
  • 项目往往都要经历编译、测试、运行、打包、安装 ,部署等一系列过程。
  • 项目构建:指的是项目从编译、测试、运行、打包、安装 ,部署整个过程都交给 maven 进行管理,这个过程称为构建。
  • 一键构建:指的是整个构建过程,使用 maven 一个命令可以轻松完成整个工作。
  • 流程:清理—编译—测试—报告—打包—部署
18.3.4. Maven 工程的运行
  • 进入 maven 工程目录(当前目录有 pom.xml 文件),运行 tomcat:run 命令。
18.3.5.pom.xml的基本配置
  • pom.xml 是 Maven 项目的核心配置文件,位于每个工程的根目录,基本配置如下:
<project > :文件的根节点 .
<modelversion > : pom.xml 使用的对象模型版本
<groupId > :项目名称,一般写项目的域名
<artifactId > :模块名称,子项目名或模块名称
<version > :产品的版本号 .
<packaging > :打包类型,一般有 jar、war、pom 等
<name > :项目的显示名,常用于 Maven 生成的文档。
<description > :项目描述,常用于 Maven 生成的文档
<dependencies> :项目依赖构件配置,配置项目依赖构件的坐标
<build> :项目构建配置,配置编译、运行插件等。
18.2.依赖管理
  • Maven 的一个核心特性就是依赖管理。当我们涉及到多模块的项目(包含成百个模块或者子项目),管理依赖就变成一项困难的任务。Maven 展示出了它对处理这种情形的高度控制。传统的 WEB 项目中,我们必须将工程所依赖的 jar 包复制到工程中,导致了工程的变得很大
  • maven 工程中不直接将 jar 包导入到工程中,而是通过在 pom.xml 文件中添加所需 jar包的坐标,这样就很好的避免了 jar 直接引入进来,在需要用到 jar 包的时候,只要查找 pom.xml 文件,再通过 pom.xml 文件中的坐标,到一个专门用于”存放 jar 包的仓库”(maven 仓库)中根据坐标从而找到这些 jar 包,再把这些 jar 包拿去运行。
<!-- 依赖坐标 -->
<dependencies>
    <!-- 依赖设置 -->
    <dependency>
        <!-- 依赖组织名称 -->
        <groupId>junit</groupId>
        <!-- 依赖项目名称 -->
        <artifactId>junit</artifactId>
        <!-- 依赖版本名称 -->
        <version>4.12</version>
        <!-- 依赖范围:test 包下依赖该设置,若没有此标签默认是compile -->
        <scope>test</scope>
    </dependency>
</dependencies>
18.3.Maven指令
  • mvn compile
    • compile 是 maven 工程的编译命令,作用是将 src/main/java 下的文件编译为 class 文件输出到 target目录下。
    • cmd 进入命令状态,执行 mvn compile
    • 查看 target 目录,class 文件已生成,编译完成。
  • mvn test
    • test 是 maven 工程的测试命令 mvn test,会执行 src/test/java 下的单元测试类。
    • cmd 执行 mvn test 执行 src/test/java 下单元测试类
  • mvn clean
    • clean 是 maven 工程的清理命令,执行 clean 会删除 target 目录及内容。
  • mvn package
    • package 是 maven 工程的打包命令,对于 java 工程执行 package 打成 jar 包,对于 web 工程打成 war包。
  • mvn install
    • install 是 maven 工程的安装命令,执行 install 将 maven 打成 jar 包或 war 包发布到本地仓库。
    • 从运行结果中,可以看出:当后面的命令执行时,前面的操作过程也都会自动执行,
  • Maven 指令的生命周期
    • maven 对项目构建过程分为三套相互独立的生命周期,注意这里说的是“三套”,而且“相互独立”,调用 clean 不会对 default、site 产生作用。
    • 每个生命周期都包含一些阶段,比如 clean 包含 pre-clean 阶段、clean 阶段、post-clean 阶段。当调用 pre-clean 时,只有 pre-clean 执行;调用 clean 时,pre-clean和 clean 都会顺序执行;执行 post-clean,pre-clean、clean、post-clean 都会按照顺序执行
    • 当我们调用某个指令的时候,同一套内,当前阶段生命周期指令之前的指令全部会被执行,包括其本身
    • 生命周期其实就是一些指令,这三套生命周期分别是:
      • Clean Lifecycle:在进行真正的构建之前进行一些清理工作。
      • Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。
        • validate
        • compile
        • test
        • package
        • verify
        • install
        • deploy
      • Site Lifecycle 生成项目报告,站点,发布站点。
    • Maven 生命周期不做具体实现,而实际上是插件在背后默默做这些事情,插件对生命周期做了具体的实现。
18.4.Maven 核心概念
18.4.1.坐标
  • 在平面坐标系,需要定位一个点需要 x 坐标和 y 坐标,在空间坐标系需要 x、y、z 三个坐标。
  • 在 Maven 中,确定唯一的一个 jar 包也需要三个坐标,groupId、artifactId、version 三个信息。
    • groupId:当前的组织名称
    • artifactId:当前项目的名称
    • version:当前项目的当前版本
  • 在每个Maven项目的pom.xml文件中,有对应的键值对来配置。
18.4.2.scope依赖范围
依赖范围 对于编译代码classpath有效 对于测试代码classpath有效 被打包时,对于运行的classpath有效
compile log4j
test × × junit
provided × servlet-api
runtime × JDBC Driver Implementation
system ×
  • compile:编译范围,指 A 在编译时依赖 B,此范围为默认依赖范围。
    • 编译范围的依赖会用在编译、测试、运行,由于运行时需要所以编译范围的依赖会被打包。
  • provided:provided 依赖只有在当 JDK 或者一个容器已提供该依赖之后才使用, provided 依赖在编译和测试时需要,在运行时不需要
    • 比如:servlet api 被 tomcat 容器提供,运行时不需要,打包是不会进入压缩包里
  • runtime:runtime 依赖在运行和测试系统的时候需要,但在编译的时候不需要
    • 比如:jdbc的驱动包。由于运行时需要所以 runtime 范围的依赖会被打包。
  • test:test 范围依赖 在编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用,
    • 比如:junit。由于运行时不需要所以 test 范围依赖不会被打包。
  • system:system 范围依赖与 provided 类似,但是你必须显式的提供一个对于本地系统中 JAR文件的路径
    • 需要指定 systemPath 磁盘路径,system 依赖不推荐使用。
18.4.3.依赖传递
  • 在项目中引入一个依赖,引入的该依赖又会引入其他的依赖。这种就被称作依赖的传递
    • 项目一依赖项目二(第一依赖),项目二依赖commons-fileupload(第二依赖)
    • 根据依赖的传递性,项目一也可以拿到commons-fileupload依赖。
第一依赖/第二依赖 compile test provided runtime
compile compile runtime
test test test
provided provided provided
runtime runtime runtime
  • 如果第二依赖是test、provided,传递会断层,如果需要用这个依赖,必须自己再引入。
18.4.4.依赖冲突
  • 某项目
    • 依赖于A,A依赖于Cv5.3.3
    • 依赖于B,B依赖于Cv5.3.4
  • A可以理解为spring-jdbc,B可以理解为spring-context,C可以理解为spring-beans
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.3</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.4</version>
</dependency>
  • 两个依赖原则:
    • 第一声明者优先
    • 路径近者优先
  • 解释:
    • 对于这个配置,spring-jdbc和spring-context均依赖于spring-beans,二者平级,那么谁在上面就依赖于谁
    • 对于下列配置,虽然spring-beans在下面,但是,spring-beans路径更短,所以spring-beans优先
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.3</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.3.4</version>
</dependency>
  • 依赖排除:将spring-beans排除在外,所以spring-jdbc有效
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • 提取常量:对于一个系列的jar包,我们建议全部都采用同一个版本,可以通过设置一个常量,固定版本,后面全部沿用该版本。
<properties>
    <spring.version1>5.3.4</spring.version1>
</properties>

<dependencies>
    <dependency>
        <groupId>com.cskaoyan.28ee</groupId>
        <artifactId>mavenProject</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version1}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version1}</version>
    </dependency>
</dependencies>
18.5.IDEA构建Maven项目
18.5.1.IDEA设置
  • 打开File—Settings ,配置 maven
  • 选择本地 maven 安装目录,指定 maven 安装目录下 conf 文件夹中 settings 配置文件
    • 修改Maven home Directory,指定本地Maven安装目录
    • 修改Using settings file,指定settings配置文件路径
    • local repository,自动检测本地仓库的位置
18.5.2.创建Maven项目
  • Creat New Project,选择 idea 提供好的 maven 的 web 工程模板
  • 创建好以后,可以选择File—Other Settings—Settings for New Projects,再次选中Maven,然后按上述操作改好
18.6.Maven与IDEA及Tomcat的关联
  • 首先,我们应该配置一个虚拟映射,到Tomcat的部署根目录,这样,在IDEA中的html等资源就会在部署的时候自动复制到部署根目录里去
    • 在src—main下,新建一个webapp文件夹,该文件夹应该有个蓝色的圆点标记
      • 打开Project Structure,选择module,右侧的Deployment Descriptors设置好web.xml路径,应该是src下的main文件夹下的webapp
      • 在下面设置web资源目录,同样修改为src下的main文件夹下的webapp
    • 设置部署根目录
      • 同样,打开Project Structure,选择Artifacts,点击Web Application:Exploded----From module,选择当前的module,点击apply。
      • 每次Maven项目编译的class文件都会自动复制到Artifacts下的WEB-INF文件夹下的classes文件夹内

        在maven项目中,可以在pom.xml文件中添加一对标签war,就可以免去设置artifacts

  • 使用Tomcat来部署该项目
    • 点击项目右上角的ScopeMainTest,选择一个本地的Tomcat来部署,修改应用的名称
  • 添加jar包到编译环境
    • 选择jar包,右键As a Library
  • 选择jar包加载到内存中
    • 在WEB-INF下新建一个lib目录,右键lib目录,Add Copy of,选择Library Files,选择需要加载的jar包
    • 或者,在pom.xml文件中,添加一对标签
      <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> <!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如 com.companyname.project-group,maven 会将该项目打成的 jar 包放本地路径: /com/companyname/project-group -->
          <!-- 添加packaging标签 -->
          <packaging>war</packaging>
          <groupId>com.cskaoyan.project</groupId> <!-- 项目的唯一ID,一个groupId下面可能多个项目,就是依靠于artifactId来区分的 -->
          <artifactId>hello</artifactId> <!-- 版本号 -->
          <version>1.0</version>
      
      <dependencies>
          <dependency>
              <groupId>com.cskaoyan.28ee</groupId>
              <artifactId>mavenProject</artifactId>
              <version>1.0-SNAPSHOT</version>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-jdbc</artifactId>
              <version>${spring.version1}</version>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>${spring.version1}</version>
          </dependency>
      </dependencies>
      
  • Maven内嵌的编译器是jdk1.5版本的,有时jdk1.8的新特性Maven会报错
    • 解决办法:找到settings.xml文件的profiles标签,添加如下
      <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>
      
    • 或者,在pom.xml文件中,新增properties
      <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>
      
18.7.SE和EE项目获取配置文件的方式
  • SE项目配置文件的存放位置和EE项目文件的存放位置不同
    • SE项目的配置文件其实可以放置在应用的根目录里面
    • EE项目的配置文件必须要放置在WEB-INF或者它的子目录中。
  • SE项目配置文件和EE项目配置文件存放需要两套策略。如果要获取配置文件,肯定需要去写两套代码。
public class PropertiesTest {
   
    public static void main(String[] args) throws IOException {
   
        //SE获取配置文件方式,一般将配置文件与src同级,相对路径是src同级目录,可以在IDEA里修改相对路径
        File file = new File("application.properties");
        FileInputStream fileInputStream = new FileInputStream(file);
        Properties properties = new Properties();
        properties.load(fileInputStream);
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        System.out.println(username);
        System.out.println(password);
    }
}



@WebServlet("/index")
public class IndexServlet extends HttpServlet {
   
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        //EE项目获取配置文件的方式
        String realPath = getServletContext().getRealPath("WEB-INF/application.properties");
        FileInputStream fileInputStream = new FileInputStream(new File(realPath));
        Properties properties = new Properties();
        properties.load(fileInputStream);
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        System.out.println(username);
        System.out.println(password);
    }
}
  • 类加载器能够将class文件加载到内存,那么它肯定知道class文件的绝对路径-------file------inputStream
  • 把配置文件和class文件放在一起,那么可以利用类加载器拿到配置文件的绝对路径
public class PropertiesTest2 {
   

    public static void main(String[] args) throws IOException {
   
        String path = PropertiesTest2.class.getClassLoader().getResource("application.properties").getPath();
        FileInputStream fileInputStream = new FileInputStream(new File(path));
        //除此之外还有另外一种简便的方式
        InputStream inputStream = PropertiesTest2.class.getClassLoader().getResourceAsStream("application.properties");
    }
}



@WebServlet("/index2")
public class IndexServlet2 extends HttpServlet {
   
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        //拿到当前类中的类加载器
        ClassLoader classLoader = IndexServlet2.class.getClassLoader();
        String path = classLoader.getResource("application.properties").getPath();
        FileInputStream fileInputStream = new FileInputStream(new File(path));
    }
}