是什么

如果在A项目中需要用到B项目的类,那么就必须把B项目打成jar包,然后引到lib下。我们称A依赖于B。引入jar包的方法十分繁琐,而maven就是用来管理这种依赖关系的。

Maven是基于项目对象模型(POM project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具。通俗点讲,就是通过pom.xml文件的配置获取jar包,而不用手动去添加jar包。

  • 总之,maven可以帮你构建工程、管理jar包、编译代码、自动运行单元测试、打包、生成报表、部署项目。

怎么做

如果需要使用pom.xml来获取jar包,那么首先该项目就必须为maven项目,maven项目可以这样去想,就是在java项目和web项目的上面包裹了一层maven,本质上java项目还是java项目,web项目还是web项目,但是包裹了maven之后,就可以使用maven提供的一些功能了。
具体操作,看一、二、四、七就行了,其中七是基本操作

添加外部依赖

plugin插件

maven本质上是一个插件框架,它的核心并不是执行任何具体的构建任务,所有这些任务都交给插件来完成,例如编译源代码是由maven-complier-plugin完成的。进一步说,每个插件会有一个或者多个目标,例如maven-complier-plugin的complie目标用来编译位于src/main/java目录下的主源码,testComplier目标用来编译位于src/test/java目录下的测试源码。

plugins和pluginManagement的区别

plugins和pluginManagement的区别,和我们前面研究过的dependencies和dependencyManagement的区别是非常类似的。plugins下的plugin是真实使用的,而pluginManagement下的plugins下的plugin则仅仅是一种声明,子项目中可以对pluginManagement下的plugin进行信息的选择、集成和覆盖等。

dependencyManagement

  • 在父项目中统一管理依赖的版本号,可保证子模块中依赖的版本号一致,若子模块想不一致的话可以另外声明版本号
  • 只是管理而不会引入依赖,所以子模块还需写dependency

project下的属性

  • modelVersion:声明项目描述符遵循哪一个pom模型版本,通常是4.0.0。
  • name:项目的名称。maven产生的文档用。
  • url:项目主页的url,maven产生的文档用。
  • developers:项目开发者列表。

  • maven标签大全

dependency下的属性

  • groupId、artifactId、version是依赖的基本坐标,缺一不可。这三个可以不用讲,都知道。重要的是除了这三个之外的配置属性需要我们理解
  • type:依赖的类型,比如是jar包还是war包等。
    • 默认为jar,表示依赖的jar包
    • 注意:pom.lastUpdated 这个我们在上面添加servlet-jar的时候就遇到过,看到lastUpdated的意思是表示使用更新描述信息,占位符作用,通俗点讲,选择该类型,jar包不会被加载进来,只是将该jar包的一些描述信息加载进来,使别的jar包在引用他时,能够看到一些相关的提示信息,仅此而已,所以说他是个占位符,只要记住他的jar包不会被加载进来。
  • optional:标记依赖是否可选。
    • 默认值false
    • 比如struts2中内置了log4j这个记录日志的功能,就是将log4j内嵌入struts2的jar包中,而struts2有没有log4j这个东西都没关系,有它,提示的信息更多,没它,也能够运行,只是提示的信息就相对而言少一些,所以这个时候,就可以对它进行可选操作,想要它就要,不想要,就设置为false。
  • exclusions:排除传递依赖,解决jar冲突问题
    • 依赖传递的意思就是,A项目 依赖 B项目,B项目 依赖 C项目,当使用A项目时,就会把B也给加载进来,这是传递依赖,依次类推,C也会因此给加载进来。这个有依赖传递有好处,也有坏处,坏处就是jar包的冲突问题,比如,A 依赖 B(B的版本为1),C 依赖 B(B的版本为2),如果一个项目同时需要A和C,那么A,C都会传递依赖将B给加载进来,问题就在这里,两个B的版本不一样,将两个都加载进去就会引起冲突,这时候就需要使用exclusions这个属性配置了。maven也会有一个机制避免两个都加载进去,maven 默认配置在前面的优先使用,但是我们还是需要使用exclusions来配置更合理。
  • scope:依赖范围,意思就是通过pom.xml加载进来的jar包,来什么范围内使用生效,范围包括编译时,运行时,测试时。
    • compile:默认值,如果选择此值,表示编译、测试和运行都使用当前jar
    • test:表示只在测试时当前jar生效,在别的范围内就不能使用该jar包。例如:junit ,测试只能写在test/java包下。此处不写也不报错,因为默认是compile,compile包括了测试。
    • runtime,表示测试和运行时使用当前jar,编译时不用该jar包。例如:JDBC驱动。JDBC驱动,在编译时(也就是我们写代码的时候都是采用接口编程,压根就没使用到JDBC驱动包内任何东西,只有在运行时才用的到,所以这个是典型的使用runtime这个值的例子),此处不写也不报错,理由同上。
    • provided,表示编译和测试时使用当前jar,运行时不在使用该jar了。例如:servlet-api、jsp-api等。【必须填写】
    • import:搭配pom,表示引用外部的pom文件。由于maven的继承和java一样,无法实现多继承,如果一个父模块包含10个、20个甚至更多个子模块,那么这个父模块的pom的dependencyManagement会包含大量的依赖。为了将这些依赖分类以更清晰地管理,我们需要把dependencyManagement放到单独的专门用来管理依赖的pom汇总,然后在需要使用依赖的模块中通过import来引入。
    • 例如可以写一个管理依赖的pom
    <project>
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.test.sample</groupId>
        <!--官方的管理文档一般命名后缀为-bom(bill of material)-->
        <artifactId>base-parent1</artifactId> 
        <packaging>pom</packaging>
        <version>1.0.0-SNAPSHOT</version>

        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>junit</groupId>
                    <artifactid>junit</artifactId>
                    <version>4.8.2</version>
                </dependency>
                <dependency>
                    <groupId>log4j</groupId>
                    <artifactid>log4j</artifactId>
                    <version>1.2.16</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    </project>

- 然后通过非继承的方式来引入这段依赖管理配置


        <dependencyManagement>
            <dependencies>
                <dependency>
                    <!--下面三行是上面pom的信息-->
                    <groupId>com.test.sample</groupId>
                    <artifactid>base-parent1</artifactId>
                    <version>1.0.0-SNAPSHOT</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

        <!--不需要定义版本了-->
        <dependency>
            <groupId>junit</groupId>
            <artifactid>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactid>log4j</artifactId>
        </dependency>

repositories和repository

  • 构件可以分成两类,一类是被其他构件依赖的构件(dependencies),这也是maven库中的主要构件;另一种是插件(plugins),这是一种特殊的构件。由于插件的特殊性,插件库独立于依赖库,使用单独设置,但是和中的设置基本一致。

    <!--查找依赖和扩展的远程仓库列表;仅限本项目-->
    <repositories>
        <repository>
            <!--远程仓库唯一标识符。可以用来匹配在settings.xml文件里配置的远程仓库-->
            <id>spring-snapshot</id>
            <!--远程仓库名称-->
            <name>Spring Snapshot Repository</name>
            <!--远程仓库URL,按protocol://hostname/path形式-->
            <url>https://repo.spring.io/snapshot</url>
        </repository>
    </repositories>
    
  • maven中要配置库,可以直接在项目pom.xml中通过配置,但仅限于当前项目;也可以通过中的配置在特定环境下的库,这可以在pom.xml中,也可以在setting.xml中配置。如果哦都不设置的话则会到下面这个默认库区获取:

    <repository>
          <snapshots>
          <enabled>false</enabled>
          </snapshots>
          <id>central</id>
          <name>Maven Repository Switchboard</name>
          <url>http://repo1.maven.org/maven2</url>
     </repository>
    

packaging

  • 打包方式:pom/jar/war,默认是jar。父项目必须是pom,并且使用module指定子项目。

    <packaging>pom</packaging>  
    <modules>  
        <module>simple-weather</module>  //项目的子模块
        <module>simple-webapp</module>  
    </modules>
    
  • pom指的是项目不像之前的项目那样创建一个jar或war,它仅仅是引用其他maven项目的POM。
  • maven知道要去这些子模块的目录中寻找pom.xml文件。
  • 子模块中也需要配置父项目的关联

    <parent>
        <groupId>org.sonatype.mavenbook.ch06</groupId>
        <artifactId>simple-parent</artifactId>  //刚才的父项目名
        <version>1.0</version>
        <!-- <relativePath>../simple-parent/pom.xml</relativePath> --> //如果子项目不是标准地建在父项目的子模块里,而是跟父项目同级了的话,可以先到它们的上一级目录找到父项目,再找其pom文件;标准情况下不用该属性
    </parent>
    
  • 子模块的packaging默认是jar,可以改成war。
  • 这样,子模块会自动继承父模块的依赖。
  • 子项目的groupId和version如果和父项目的一样可以省略不写,会自动继承父项目的。
  • 但是子项目之间的互相引用必须注明版本号,比如dao模块引用实体类模块的依赖,必须指明实体类模块的版本号,尽管实体类自己没有显式声明自己的版本号,只是继承父类的。
  • 当maven执行一个带有子模块的项目时,maven首先载入父pom,然后定位所有子模块的pom(子模块的文件和父pom同级,也就是子pom必须在父pom的下一级文件中),随后将这些pom放入一个maven反应堆(reactor)的东西中,这个反应堆处理组件的排序,以确保相互独立的模块能以适当的顺序被编译和安装。

maven命令

  • mvn compile 编译,没什么用了,现在都是自动编译
  • mvn clean 清空target目录
  • mvn test 测试,测试依赖能否找到,单元测试成功与否
  • mvn package 打包,把项目打jar包或war包。
  • mvn install 把项目安装到本地仓库,运行之后本地仓库就会有这个依赖
  • 子模块编辑完后应该install安装到本地仓库,才能被其他模块引入。子模块修改了应该重新clean再install(除此之外不用其他操作),引入它的模块才能接收到更新。
  • 有的时候你中有我我中有你,一起更新之后要重新install会出错,最好就是先解开一方对另一方的新内容的引用,不然谁也install不了
  • 运行父模块发现总是失败可以重新install一下(先clean,否则异名旧包不会被清除),可能是最近的代码修改没有被识别到。

执行maven命令异常

  • Plugin org.apache.maven.plugins:maven-resources-plugin:3.0.2 or one of its dependencies…
  • 就是说它找不到这个依赖,在本地仓库下这个依赖下载失败,肯定有很多lastupdate文件在里面,而且这还不是个依赖,是个plugin,插件,一般来说我们创建maven项目都不会去添加什么插件的,但是有的时候他就是会自动添加插件,比如thz-parent项目的thz-manage-web模块下pom文件就有自动添加过插件

    <build>
        <!--war包名-->
        <finalName>thz-manager-web</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.20.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
    
  • 以上这些代码是某一次查看pom文件时突然多出来的,直接删去,再mvn test,问题解决
  • Failed to execute goal org.apache.maven.plugins maven-surefire-plugin
  • 看报错日志,可能是子模块忘记引入某个依赖了。

eclipse创建maven项目

多模块maven项目

  • 代码实例:ideaProjects/thz。
  • 运行子模块之前,先把父模块install一下。
  • 被其他模块引用的模块改动之后要重新install,引用它的模块才能检索到它的更新。
  • 创建单元测试时跨模块引用配置文件:
@ContextConfiguration(locations={"classpath*:config/applicationContext.xml"}) 
//classpath:在当前项目查找;classpath*:在所有依赖的jar包的classpath下找
  • 在父模块执行maven命令,将对所有的子模块起作用。
  • 如果是Spring的多模块maven项目,最好把配置文件集中写在dao模块,反正不要在web/controller这种被打包成war的模块中,否则在其他模块写单元测试时引入配置文件失败。原因是配置文件写在了war包中,而war包不能被当成依赖引入这个要建立单元测试的模块,因此配置文件无法引入,从而只能把测试类写在配置文件所在模块了。
  • 可参见代码示例service模块下测试和web模块下相同测试。
  • 如果同时有多个子模块都是web应用,它们需要同时运行协同测试,那么可以在debug configuration中添加两个maven命令,命令行都设为tomcat7:run,作用目录则分别选择相应的目录,然后两个都debug起来就行。
  • 若父模块的pom中依赖都写在dependencyManager中,则子模块依然需要引入依赖;若写在dependencies中,则子模块不需要再引入。

  • 父项目如果负责引入依赖而不只是管理依赖的话,那么子项目虽然继承了这些依赖,但是一开始写代码是检测不到这些依赖的,有时alt+enter可以直接把依赖add to classpath,但有时代码却只是飘下划线而不能直接add to classpath,此时可以手动import,就能在飘红的包名那里alt+enter把依赖add to classpath了。

  • 网上推荐把.iml文件删除,然后点击以下图标重新生成,如此几次即可:
  • 参考文章
  • 代码示例:ideaProjects/shiro-chapter23/shiro-chapter23-client/ClirntShiroFilterFactoryBean/setApplicationContext(){this.applicationContext},这一段在未添加spring-beans依赖并将其add to classpath时飘红线。