实习日记-工具框架-Maven复习

429 阅读8分钟

个人博客:zhenganwen.top

安装和配置

环境变量

将解压后的bin目录配置到path中。主要是mvn命令,此命令的生命周期(后一个命令的执行将导致其前面的所有命令先执行一遍)如下:

  • mvn compile,根据src/main/java目录编译生成包含字节码的target目录
  • mvn test,执行src/test/java目录下的所有测试用例
  • mvn package,将此项目打包(jarwar)放至target目录下
  • mvn install,将此项目打包后的文件放至本地仓库中

mvn clean则会将本项目的target目录清楚,有时你拿到的不是一个干净的maven项目(即别人编译、打包过),为了避免目标文件因运行环境不同而导致运行出错,建议先mvn clean并重新构建一下

配置文件

maven只有一个配置文件,即maven_home/conf/settings.xml

本地仓库

默认将C盘下用户主目录作为本地仓库的位置,如果你不想占C盘空间,可以设置如下:

<!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ${user.home}/.m2/repository
  <localRepository>/path/to/local/repo</localRepository>
  -->
<localRepository>D:\Software\Maven\mvn_repo</localRepository>

中心仓库镜像

由于maven的中心仓库部署在国外的服务器上(下载依赖时如果在本地仓库中没有找到该依赖则会去中心仓库),网速不佳,因此可以使用国内的阿里云镜像:

<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>alimaven</id>  
        <mirrorOf>central</mirrorOf>  
        <name>aliyun maven</name> 
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    </mirror>  

</mirrors>

用Maven搭建一个web工程

如下使用IDEA创建一个带有骨架(在src/java/main/下添加了一个webapp用于存放视图文件)的Web工程

然后我们创建Servlet,发现HttpServelt报红找不到该类,说明servlet相关jar包没有引入,于是在pom.xml中添加servletjsp依赖:

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
    </dependency>
</dependencies>

作用域

接着,可以在项目根目录的命令行下键入tomcat:run运行(maven默认内嵌了一个tomcat6),你会发现报错:自己写的Servlet无法转换成HttpServelt,这是因为tomcat应用已依赖了servlet-apijsp-api,你在pom中又引入了一次,导致项目中这两个依赖都有两个jar包而产生了冲突。

由于我们只需要我们在pom中引入的servlet-apijsp-api帮助我们度过编译期,而运行期不要将其编入target使用内嵌tomcat中的就可以了,因此我们可以将这两个依赖的生命周期(scope)设置为provide

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

如此我们写的servletjsp便可以正常运行了。

如果不写scope则默认为compile,即作用域编译期、测试期、运行期;test则仅作用于测试代码/src/test/java的编译和运行(典型的如junit);runtime表示被依赖项目无需参与编译,但后期的测试和运行需要其参与,如jdbc驱动

注:如果你使用的JDK是Java8,仍然会抛出异常,因为tomcat6不兼容Java8。解决如下

Maven插件

有很多Maven插件可以集成进来帮助我们快速构建应用,比如上述tomcat6不兼容Java8的问题,我们就可以集成tomcat7插件:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <port>8888</port>
            </configuration>
        </plugin>
    </plugins>
</build>

如此,键入tomcat7:run命令就能使该项目运行在端口为8888tomcat7上,而键入tomcat:run命令则仍将此项目运行在默认8080端口的tomcat6上。

依赖冲突

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

当你引入上述依赖时,由于spring-context自身依赖5.0.2.RELEASE版本的core、beans、expression、aop等依赖,又由于maven的传递性,当前项目也会引入这些依赖。此时,context称为直接依赖,后者称为传递依赖。

如果此时你再手动引入一个4.2.4.RELEASE版本的beans依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.2.4.RELEASE</version>
    <scope>compile</scope>
</dependency>

因为contextbeans都依赖core,那么Maven间接依赖进来的core取哪个版本呢?

原则一:先进先留原则

如果两个直接依赖引入了两个不同版本的相同依赖,在pom中书写在前的将被保留。

也即,如果书写如下:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.2.4.RELEASE</version>
    <scope>compile</scope>
</dependency>

那么引入的将是5.0.2.RELEASE版本的spring-core,否则将两者的书写顺序颠倒:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.2.4.RELEASE</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

引入的spring-core就是4.2.4.RELEASE版本的了。

原则二:依赖链短者保留

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.2.4.RELEASE</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

如上,无论spring-beansspring-context的书写顺序如何,引入的spring-core均以第15行的5.0.2.RELEASE为准,因为通过依赖传递形成的依赖链最短。

原则三:使用exclusion排除依赖【推荐】

当发生依赖冲突时,我们可以将所有不想要的依赖通过exclusion显式声明的方式排除掉,这样最为直观。如,排除掉4.2.4版本的spring-core

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.2.4.RELEASE</version>
    <scope>compile</scope>
    <exclusions>
        <exclusion>
            <artifactId>org.springframework</artifactId>
            <groupId>spring-core</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

其他标签

dependencyManagement

dependencyManager用来统一管理版本号,让子项目中引用一个依赖而不用显示的列出版本号。Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号。这样,一旦整个系统需要迭代升级,只需将定义在父项目的dependencyManagement中的版本号更换即可实现其所有子项目的依赖升级。

properties

定义一些键值对,已达到复用、减少修改的目的。

父子工程

父工程通常不存放源代码,仅仅起到一个聚合的作用,将相关联的一些模块聚合在一起。需要注意一下几点:

  • 父工程的pom中的package必须是pom

  • 父工程通常通过定义dependencyManagement来控制子模块的依赖的版本号统一

  • 父工程可在dependency中定义各子模块都可能用到的依赖,这些依赖会自动被子模块继承,也即子模块可省略在其pom中对这些依赖的引入

  • 各子模块虽然继承同一父工程,但彼此之间没有任何直接关联关系,如果模块A需要用到模块B的功能,则需现将模块Bmvn install到本地仓库,然后在模块A的pom中引入模块B的坐标。

    注:对于任何maven工程,无论是父工程还是子模块,要想其编译成功,其所有依赖必须要能在本地仓库或远程仓库中找得到

创建子模块的方法:右键父工程,选择new module。子模块可以在父工程的目录下,也可以和父工程同一目录,这没有任何影响。是否是父子工程需要看父工程pom中的module定义和子模块pom中的parent定义,子模块的parent中写父工程的groupId、artifactId、version,自己的groupIdversion都将继承父工程的,只需自定义artifactId