我们先通过企业开发中的实际需求来看一看哪些方面是我们现有技术的不足。
1. 第三方Jar包添加
在今天的JavaEE开发领域,有大量的第三方框架和工具可以供我们使用。要使用这些jar包最简单的方法就是复制粘贴到WEB-INF目录下的lib目录下。但是这会导致每次创建一个新的工程就需要将jar包重复复制到lib目录下,从而造成工作区中存在大量重复的文件。
而使用Maven后每个jar包只在本地仓库中保存一份,需要jar包的工程只需要维护一个文本形式的jar包的引用——我们称之为“坐标”。不仅极大的节约了存储空间,更避免了重复文件太多而造成的混乱。
2. 第三方Jar包获取
JavaEE开发中需要使用到的jar包种类繁多,几乎每个jar包在其本身的官网上的获取方式都不尽相同。为了查找一个jar包找遍互联网,身心俱疲,没有经历过的人或许体会不到这种折磨。不仅如此,费劲心血找的jar包里有的时候并没有你需要的那个类,又或者有同名的类没有你要的方法——以不规范的方式获取的jar包也往往是不规范的。
使用Maven我们可以享受到一个完全统一规范的jar包管理体系。你只需要在你的项目中以坐标的方式依赖一个jar包,Maven就会自动从中央仓库进行下载,并同时下载这个jar包所依赖的其他jar包——规范、完整、准确!一次性解决所有问题!
3. Jar包之间的依赖关系
jar包往往不是孤立存在的,很多jar包都需要在其他jar包的支持下才能够正常工作,我们称之为jar包之间的依赖关系。最典型的例子是:commons-fileupload-1.3.jar依赖于commons-io-2.0.1.jar,如果没有IO包,FileUpload包就不能正常工作。
那么问题来了,你知道你所使用的所有jar包的依赖关系吗?当你拿到一个新的从未使用过的jar包,你如何得知他需要哪些jar包的支持呢?如果不了解这个情况,导入的jar包不够,那么现有的程序将不能正常工作。再进一步,当你的项目中需要用到上百个jar包时,你还会人为的,手工的逐一确认它们依赖的其他jar包吗?这简直是不可想象的。
而引入Maven后,Maven就可以替我们自动的将当前jar包所依赖的其他所有jar包全部导入进来,无需人工参与,节约了我们大量的时间和精力。用实际例子来说明就是:通过Maven导入commons-fileupload-1.3.jar后,commons-io-2.0.1.jar会被自动导入,程序员不必了解这个依赖关系。
4. Jar包之间的冲突处理
上一点说的是jar包不足项目无法正常工作,但其实有的时候jar包多了项目仍然无法正常工作,这就是jar包之间的冲突。
举个例子:我们现在有三个工程MakeFriend、HelloFriend和Hello。MakeFriend依赖HelloFriend,HelloFriend依赖Hello。而Hello依赖log4j.1.2.17.jar,HelloFriend依赖log4j.1.2.14.jar。
如下图所示:
那么MakeFriend工程的运行时环境中该导入log4j.1.2.14.jar呢还是log4j.1.2.17.jar呢?
这样的问题一个两个还可以手工解决,但如果系统中存在几十上百的jar包,他们之间的依赖关系会非常复杂,几乎不可能手工实现依赖关系的梳理。
使用Maven就可以自动的处理jar包之间的冲突问题。因为Maven中内置了两条依赖原则:最短路径者优先和先声明者优先,上述问题MakeFriend工程会自动使用log4j.1.2.14.jar。
5. 将项目拆分成多个工程模块
随着JavaEE项目的规模越来越庞大,开发团队的规模也与日俱增。一个项目上千人的团队持续开发很多年对于JavaEE项目来说再正常不过。那么我们想象一下:几百上千的人开发的项目是同一个Web工程。那么架构师、项目经理该如何划分项目的模块、如何分工呢?这么大的项目已经不可能通过package结构来划分模块,必须将项目拆分成多个工程协同开发。多个模块工程中有的是Java工程,有的是Web工程。
那么工程拆分后又如何进行互相调用和访问呢?这就需要用到Maven的依赖管理机制。
例如:某项目拆分的情况如下:
上层模块依赖下层,所以下层模块中定义的API都可以为上层所调用和访问。
6. 实现项目的分布式部署
在实际生产环境中,项目规模增加到一定程度后,可能每个模块都需要运行在独立的服务器上,我们称之为分布式部署,这里同样需要用到Maven。
Maven是一款自动化构建工具,专注服务于Java平台的项目构建和依赖管理。在JavaEE开发的历史上构建工具的发展也经历了一系列的演化和变迁:Make→Ant→Maven→Gradle→其他……
1. 构建的概念
构建并不是创建,创建一个工程并不等于构建一个项目。要了解构建的含义我们应该由浅入深的从以下三个层面来看:
1)纯Java代码
大家都知道,我们Java是一门编译型语言,.java扩展名的源文件需要编译成.class扩展名的字节码文件才能够执行。所以编写任何Java代码想要执行的话就必须经过编译得到对应的.class文件。
2)Web工程
当我们需要通过浏览器访问Java程序时就必须将包含Java程序的Web工程编译的结果“拿”到服务器上的指定目录下,并启动服务器才行。这个“拿”的过程我们叫部署。
我们可以将未编译的Web工程比喻为一只生的鸡,编译好的Web工程是一只煮熟的鸡,编译部署的过程就是将鸡炖熟。
Web工程和其编译结果的目录结构对比见下图:
3)实际项目
在实际项目中整合第三方框架,Web工程中除了Java程序和JSP页面、图片等静态资源之外,还包括第三方框架的jar包以及各种各样的配置文件。所有这些资源都必须按照正确的目录结构部署到服务器上,项目才可以运行。
所以综上所述:构建就是以我们编写的Java代码、框架配置文件、国际化等其他资源文件、JSP页面和图片等静态资源作为“原材料”,去“生产”出一个可以运行的项目的过程。
那么项目构建的全过程中都包含哪些环节呢?
2. 构建环节
1)清理:删除以前的编译结果,为重新编译做好准备。
2)编译:将Java源程序编译为字节码文件。
3)测试:针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性。
4)报告:在每一次测试后以标准的格式记录和展示测试结果。
5)打包:将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。Java工程对应jar包,Web工程对应war包。
6)安装:在Maven环境下特指将打包的结果——jar包或war包安装到本地仓库中。
7)部署:将打包的结果部署到远程仓库或将war包部署到服务器上运行。
3. 自动化构建
其实上述环节我们在IDEA中都可以找到对应的操作,只是不太标准。那么既然IDE已经可以进行构建了我们为什么还要使用Maven这样的构建工具呢?我们来看一个小故事:
这是阳光明媚的一天。托马斯向往常一样早早的来到了公司,冲好一杯咖啡,进入了自己的邮箱——很不幸,QA小组发来了一封邮件,报告了他昨天提交的模块的测试结果——有BUG。“好吧,反正也不是第一次”,托马斯摇摇头,进入IDE,运行自己的程序,编译、打包、部署到服务器上,然后按照邮件中的操作路径进行测试。“嗯,没错,这个地方确实有问题”,托马斯说道。于是托马斯开始尝试修复这个BUG,当他差不多有眉目的时候已经到了午饭时间。
下午继续工作。BUG很快被修正了,接着托马斯对模块重新进行了编译、打包、部署,测试之后确认没有问题了,回复了QA小组的邮件。
一天就这样过去了,明媚的阳光化作了美丽的晚霞,托马斯却觉得生活并不像晚霞那样美好啊。
让我们来梳理一下托马斯这一天中的工作内容:
从中我们发现,托马斯的很大一部分时间花在了“编译、打包、部署、测试”这些程式化的工作上面,而真正需要由“人”的智慧实现的分析问题和编码却只占了很少一部分。
能否将这些程式化的工作交给机器自动完成呢?——当然可以!这就是自动化构建。
那么Maven又是如何实现自动化构建的呢?简单的说来就是它可以自动的从构建过程的起点一直执行到终点:
仓库分类:
1)本地仓库:为当前本机电脑上的所有Maven工程服务。
2)远程仓库,包含以下:
a)私服:架设在当前局域网环境下,为当前局域网范围内的所有Maven工程服务。
b)中央仓库:架设在Internet上,为全世界所有Maven工程服务。
c)中央仓库的镜像:架设在各个大洲,为中央仓库分担流量。减轻中央仓库的压力,同时更快的响应用户请求。
仓库中的文件:
1)Maven的插件。
2)我们自己开发的项目的模块。
3)第三方框架或工具的jar包。
※不管是什么样的jar包,在仓库中都是按照坐标生成目录结构,所以可以通过统一的方式查询或依赖。
二、Maven安装与配置 1、安装Maven核心程序1)检查JAVA_HOME环境变量
Maven是使用Java开发的,所以必须知道当前系统环境中JDK的安装目录:
C:\Windows\System32>echo %JAVA_HOME%
D:\Java\jdk1.8.0_111
2)解压Maven的核心程序
将apache-maven-3.2.2-bin.zip解压到一个非中文无空格的目录下。例如:
D:\apache-maven-3.2.2
3)配置环境变量
4) 在系统变量里面创建M2_HOME变量并赋值
变量:M2_HOME
值:D:\apache-maven-3.2.2
5)在Path变量中添加maven环境变量
变量:Path
值:%M2_HOME%\bin或D:\apache-maven-3.2.2\bin
6)查看Maven版本信息验证安装是否正确
1. 按Win +r,进入电脑运行模式;
2. 在打开里面输入:cmd;
3. 在管理员窗口输入;
C:\Users\Administrator>mvn -v

1)Maven默认的本地仓库
~\.m2\repository目录
说明:~表示当前用户的家目录。
2)Maven的核心配置文件位置
解压目录D:\apache-maven-3.2.2\conf\settings.xml
3)本地仓库地址更改到E:\LocalRepository
默认在C:\Users\Administrator\.m2\repository。
E:\LocalRepository
4)配置阿里云镜像(下载速度快)
nexus-aliyun
central
Nexus aliyun
http://maven.aliyun.com/nexus/content/groups/public
3、在Idea中配置Maven
1)close project所有项目后,回到如下页面,点击右下角的Configure=》点击setting
2)设置Maven的安装目录及本地仓库
说明:
- Maven home directory:可以指定本地 Maven 的安装目录所在,因为我已经配置了M2_HOME系统参数,所以直接这样配置IntelliJ IDEA是可以找到的。但是假如你没有配置的话,这里可以选择你的Maven安装目录。此外,这里不建议使用IDEA默认的。
- User settings file / Local repository:我们还可以指定 Maven 的 settings.xml 位置和本地仓库位置。
3)配置Maven自动导入依赖的jar包
说明:
- Import Maven projects automatically:表示 IntelliJ IDEA 会实时监控项目的 pom.xml 文件,进行项目变动设置,勾选上。
- Automatically download:在 Maven 导入依赖包的时候是否自动下载源码和文档。默认是没有勾选的,也不建议勾选,原因是这样可以加快项目从外网导入依赖包的速度,如果我们需要源码和文档的时候我们到时候再针对某个依赖包进行联网下载即可。IntelliJ IDEA 支持直接从公网下载源码和文档的。
- VM options for importer:可以设置导入的VM参数。一般这个都不需要主动改,除非项目真的导入太慢了我们再增大此参数。
1)创建Project
2)创建一个空的Project
3)创建一个module
4)右键→new Module→Maven
5)点击Next,配置坐标
6)点击Next,给Module命名
目录结构及说明:
说明:
- main目录用于存放主程序。
- java目录用于存放源代码文件。
- resources目录用于存放配置文件和资源文件。
- test目录用于存放测试程序。
7)配置Maven的核心配置文件pom.xml
4.0.0
com.atguigu.maven
Hello
1.0-SNAPSHOT
junit
junit
4.0
test
8)编写主代码
在src/main/java目录下新建文件Hello.java:
public class Hello {
public String sayHello(String name){
return "Hello "+name+"!";
}
}
9)编写测试代码
在/src/test/java目录下新建测试文件HelloTest.java:
import org.junit.Test;
public class HelloTest {
@Test
public void testHello(){
Hello hello = new Hello();
String maven = hello.sayHello("Maven");
System.out.println(maven);
}
}
10)使用Maven的方式运行Maven工程
来到BigData项目的根目录(例如E:\ideaProject1\BigData):
1. compile命令
查看target目录的变化,编译前=》
编译后=》
2. clean命令
然后再次查看根目录变化,clean前=》
clean后=》
3. test命令
查看target目录变化:
4. package命令
查看target目录变化:
5. install命令
查看本地仓库的目录变化:
执行install命令前:
执行命令后:
1)创建简单的Maven工程,打包方式为war包
com.atguigu.maven
MavenWeb
war
1.0-SNAPSHOT
2)点击Project Structure
3)选择对应的Module,添加web目录
4)设置目录名称
5)在web目录下创建index.jsp页面
部署到Tomcat上运行即可。
3、目录结构1. POM
Project Object Model:项目对象模型。
Java工程的相关信息封装为对象作为便于操作和管理的模型。Maven工程的核心配置。可以说学习Maven就是学习pom.xml文件中的配置。
2. 约定的目录结构
现在JavaEE开发领域普遍认同一个观点:约定>配置>编码。意思就是能用配置解决的问题就不编码,能基于约定的就不进行配置。而Maven正是因为指定了特定文件保存的目录才能够对我们的Java工程进行自动化构建。
a-maven-project
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ └── resources
│ └── test
│ ├── java
│ └── resources
└── target
项目的根目录a-maven-project是项目名,它有一个项目描述文件pom.xml,存放Java源码的目录是src/main/java,存放资源文件的目录是src/main/resources,存放测试源码的目录是src/test/java,存放测试资源的目录是src/test/resources,最后,所有编译、打包生成的文件都放在target目录里。这些就是一个Maven项目的标准目录结构。
所有的目录结构都是约定好的标准结构,我们千万不要随意修改目录结构。使用标准结构不需要做任何配置,Maven就可以正常使用。
4、插件和目标Maven的核心仅仅定义了抽象的生命周期,具体的任务都是交由插件完成的。每个插件都能实现多个功能,每个功能就是一个插件目标,Maven的生命周期与插件目标相互绑定,但是真正实现流程的工程是由插件来完成的。
我们也可以说 Maven 是一个执行插件的框架,每一个任务实际上都是有插件来完成。进一步说每个任务对应了一个插件目标(goal),每个插件会有一个或者多个目标,例如maven-compiler-plugin的compile目标用来编译位于src/main/java/目录下的主源码,testCompile目标用来编译位于src/test/java/目录下的测试源码。
1. 配置编译插件
一般我们创建一个 Maven 工程,就算指定了 JDK 的版本,但是你执行 update project 操作,一般 Maven 工程会自动恢复到默认的 JDK 版本,有可能是1.4,有可能是1.5(和 Maven 版本有关)。
那么我们如何指定其 JDK 版本呢?在 pom.xml 中添加如下代码:
org.apache.maven.plugins
maven-compiler-plugin
1.7
1.7
UTF-8
下面我们来添加一个 tomcat 插件,首先我们要知道如何创建 Maven Web 工程。
2. 创建 Maven Web 工程
第一步:New maven project,注意打包方式为 war.
第二步:右击项目名,选择 properties,选择Project Facets
第三步:将 Dynamic Web Module 取消,点击 Apply
第四步:将 Dynamic Web Module 重新勾选,点击 下方生成的超链接
第五步:点击超链接,修改目录结构,然后点击 OK,创建 Maven Web 工程完成
创建的 Web 工程目录结构如下:
3. 添加 tomcat 插件
我们在上面创建的 web 工程,可以输入 tomcat:run 来使用默认的 tomcat 插件去启动 web 工程,但是默认的插件版本有点低,我们可以手动添加插件。
org.apache.tomcat.maven
tomcat7-maven-plugin
8080
/
执行命令是输入:
tomcat7:run
1. 几何中的坐标
1)在一个平面中使用x、y两个向量可以唯一的确定平面中的一个点。
2)在空间中使用x、y、z三个向量可以唯一的确定空间中的一个点。
2. Maven的坐标
使用如下三个向量在Maven的仓库中唯一的确定一个唯一的jar。
1)groupId:公司或组织的域名倒序+当前项目名称;
2)artifactId:当前项目的模块名称;
3)version:当前模块的版本;
在项目的pom.xml文件中存储坐标:
4.0.0
com.atguigu.maven
Hello
1.0-SNAPSHOT
3. 如何通过坐标到仓库中查找jar包
1)将gav三个向量连起来
com.atguigu.maven + Hello + 1.0-SNAPSHOT
2)以连起来的字符串作为目录结构到仓库中查找
com/atguigu/maven/Hello/1.0-SNAPSHOT/Hello-1.0-SNAPSHOT.jar
※注意:我们自己的Maven工程必须执行安装操作才会进入仓库。
安装的命令是:
mvn install
1)创建HelloFriend Module
2)在pom.xml配置文件中配置当前工程依赖Hello
4.0.0
com.atguigu.maven
HelloFriend
1.0-SNAPSHOT
com.atguigu.maven
Hello
1.0-SNAPSHOT
junit
junit
4.0
test
3)主程序
在src/main/java目录下新建文件HelloFriend.java:
public class HelloFriend {
public String sayHelloToFriend(String name){
Hello hello = new Hello();
String str = hello.sayHello(name)+" I am "+this.getMyName();
return str;
}
public String getMyName(){
return "Idea";
}
}
4)测试程序
在/src/test/java目录下新建测试文件HelloFriendTest.java:
import org.junit.Test;
public class HelloFriendTest {
@Test
public void testHelloFriend(){
HelloFriend helloFriend = new HelloFriend();
String results = helloFriend.sayHelloToFriend("Maven");
System.out.println(results);
}
}
5)关键:对Hello的依赖
这里Hello就是我们的第一个Maven工程,现在HelloFriend对它有依赖。那么这个依赖能否成功呢?更进一步的问题是:HelloFriend工程会到哪里去找Hello呢?
答案是:本地仓库。任何一个Maven工程会根据坐标到本地仓库中去查找它所依赖的jar包。如果能够找到则可以正常工作,否则就不行。
3、依赖管理1)基本概念
当A jar包需要用到B jar包中的类时,我们就说A对B有依赖。例如:HelloFriend-1.0-SNAPSHOT.jar依赖于Hello-1.0-SNAPSHOT.jar。
通过第二个Maven工程我们已经看到,当前工程会到本地仓库中根据坐标查找它所依赖的jar包。
配置的基本形式是使用dependency标签指定目标jar包的坐标。
例如:
com.atguigu.maven
Hello
1.0-SNAPSHOT
junit
junit
4.0
test
2)直接依赖和间接依赖
如果A依赖B,B依赖C,那么A→B和B→C都是直接依赖,而A→C是间接依赖。
1. 依赖的范围
1)compile(默认就是这个范围)
(1)main目录下的Java代码可以访问这个范围的依赖。
(2)test目录下的Java代码可以访问这个范围的依赖。
(3)部署到Tomcat服务器上运行时要放在WEB-INF的lib目录下。
例如:对Hello的依赖。主程序、测试程序和服务器运行时都需要用到。
2)test
(1)main目录下的Java代码不能访问这个范围的依赖。
(2)test目录下的Java代码可以访问这个范围的依赖。
(3)部署到Tomcat服务器上运行时不会放在WEB-INF的lib目录下。
例如:对junit的依赖。仅仅是测试程序部分需要。
3)provided
(1)main目录下的Java代码可以访问这个范围的依赖。
(2)test目录下的Java代码可以访问这个范围的依赖。
(3)部署到Tomcat服务器上运行时不会放在WEB-INF的lib目录下。
例如:servlet-api在服务器上运行时,Servlet容器会提供相关API,所以部署的时候不需要。
4)其他:runtime、import、system等。
各个依赖范围的作用可以概括为下图:
2. 依赖的传递性
当存在间接依赖的情况时,主工程对间接依赖的jar可以访问吗?这要看间接依赖的jar包引入时的依赖范围——只有依赖范围为compile时可以访问。
例如:
3. 依赖的原则:解决jar包冲突
1)路径最短者优先
2)路径相同时先声明者优先
这里“声明”的先后顺序指的是dependency标签配置的先后顺序。
4. 依赖的排除
有的时候为了确保程序正确可以将有可能重复的间接依赖排除。请看如下的例子:
假设当前工程为MakeFriend,直接依赖OurFriends。
OurFriends依赖commons-logging的1.1.1对于MakeFriend来说是间接依赖。
当前工程MakeFriend直接依赖commons-logging的1.1.2
加入exclusions配置后可以在依赖OurFriends的时候排除版本为1.1.1的commons-logging的间接依赖:
com.atguigu.maven
OurFriends
1.0-SNAPSHOT
commons-logging
commons-logging
commons-logging
commons-logging
1.1.2
5. 统一管理目标Jar包的版本
以对Spring的jar包依赖为例:Spring的每一个版本中都包含spring-context,springmvc等jar包。我们应该导入版本一致的Spring jar包,而不是使用4.0.0的spring-context的同时使用4.1.1的springmvc。
org.springframework
spring-context
4.0.0.RELEASE
org.springframework
spring-webmvc
4.0.0.RELEASE
org.springframework
spring-jdbc
4.0.0.RELEASE
org.springframework
spring-orm
4.0.0.RELEASE
问题是如果我们想要将这些jar包的版本统一升级为4.1.1,是不是要手动一个个修改呢?显然,我们有统一配置的方式:
4.0.0.RELEASE
……
org.springframework
spring-context
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-orm
${spring.version}
这样一来,进行版本调整的时候只改一改地方就行了。
五、Maven生命周期 1、Maven生命周期简介使用Maven时,我们首先要了解什么是Maven的生命周期(lifecycle),Maven的生命周期由一系列阶段(phase)构成,定义了各个构建环节的执行顺序,有了这个清单,Maven就可以自动化的执行构建命令了。
Maven有三套相互独立的生命周期,分别是:
- Clean Lifecycle在进行真正的构建之前进行一些清理工作。
- Default Lifecycle构建的核心部分,编译,测试,打包,安装,部署等等。
- Site Lifecycle生成项目报告,站点,发布站点。
再次强调一下它们是相互独立的,你可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点。当然你也可以直接运行 mvn clean install site 运行所有这三套生命周期。
每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应于一个特定的阶段。比如,运行mvn clean,这个clean是Clean生命周期的一个阶段。有Clean生命周期,也有clean阶段。
2、clean生命周期Clean生命周期一共包含了三个阶段:
- pre-clean 执行一些需要在clean之前完成的工作。
- clean 移除所有上一次构建生成的文件。
- post-clean 执行一些需要在clean之后立刻完成的工作。
- pre-site执行一些需要在生成站点文档之前完成的工作。
- site生成项目的站点文档。
- post-site执行一些需要在生成站点文档之后完成的工作,并且为部署做准备。
- site-deploy将生成的站点文档部署到特定的服务器上。
这里经常用到的是site阶段和site-deploy阶段,用以生成和发布Maven站点,这可是Maven相当强大的功能,Manager比较喜欢,文档及统计数据自动生成,很好看。
4、Default生命周期Default生命周期是Maven生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里,只解释一些比较重要和常用的阶段:
validate 验证工程是否正确,所需的信息是否完整
initialize 初始化构建平台,例如:设置properties或创建目录
generate-sources
process-sources
generate-resources
process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目的源代码。
process-classes 源码编译后的后期处理,比如java字节码的增强(优化?)
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码(默认是test目录下)
process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package
package 接受编译好的代码,打包成可发布的格式,如JAR,WAR等
pre-integration-test
integration-test 集成测试
post-integration-test
verify 检查package是否有效、符合标准
install 将包安装至本地仓库,以让其它项目依赖。
deploy 将最终的包复制到远程的仓库,以让其它开发人员与项目共享或部署到服务器上运行。
如果我们运行mvn package,Maven就会执行default生命周期,它会从开始一直运行到package这个phase为止:
validate
...
package
5、生命周期与自动化构建
运行任何一个阶段的时候,它前面的所有阶段都会被运行,例如我们运行mvn install 的时候,代码会被编译,测试,打包,这就是Maven为什么能够自动执行构建过程的各个环节的原因。
此外,Maven的插件机制是完全依赖Maven的生命周期的,因此理解生命周期至关重要。
六、Maven继承有三个 Maven 工程,每个工程都依赖某个 jar 包,比如 Junit,由于 test 范围的依赖不能传递,它必然会分散在每个工程中,而且每个工程的jar 包版本可能不一致,那么如何管理各个工程中对于某个 jar 包的版本呢?
1、继承机制由于非compile范围的依赖信息是不能在“依赖链”中传递的,所以有需要的工程只能单独配置。例如:
Hello
junit junit 4.0 test
HelloFriend
junit junit 4.9 test
MakeFriend
junit junit 4.10 test
此时如果项目需要将各个模块的junit版本统一为4.9,那么到各个工程中手动修改无疑是非常不可取的。
使用继承机制就可以将 jar 包版本统一提取到 “父" 工程中,在子工程中声明依赖时不指定版本,以父工程中统一设定的为准,同时也便于修改。
2、管理依赖1. 创建父工程
父工程的打包方式为pom:
com.atguigu.maven
Parent
pom
1.0-SNAPSHOT
父工程只需要保留pom.xml文件即可。
2. 在子工程中引用父工程
...
...
...
..
例如:
com.atguigu.maven
Parent
1.0-SNAPSHOT
../Parent/pom.xml
此时如果子工程的groupId和version如果和父工程重复则可以删除。
3. 在父工程中管理依赖
将Parent项目中的dependencies标签,用dependencyManagement标签括起来:
junit
junit
4.0
test
在子项目中重新指定需要的依赖,删除范围和版本号:
junit
junit
七、Maven聚合
在真实项目中,一个项目有表现层、业务层、持久层等。我们在用Maven 管理项目的时候,通常为创建多个 Maven 工程,也就是一个项目的多个模块。但是这样分成多个模块了,当我们进行项目打包发布的时候,那么要每一个模块都执行打包操作吗?这种重复的操作我们怎么才能避免呢?
1、聚合机制将多个工程拆分为模块后,需要手动逐个安装到仓库后依赖才能够生效。修改源码后也需要逐个手动进行clean操作。而使用了聚合之后就可以批量进行Maven工程的安装、清理工作。
2、如何配置聚合在总的聚合工程中使用modules/module标签组合,指定模块工程的相对路径即可。
../MakeFriend
../OurFriends
../HelloFriend
../Hello
Maven可以根据各个模块的继承和依赖关系自动选择安装的顺序。
八、项目模块管理在软件开发中,把一个大项目分拆为多个模块是降低软件复杂度的有效方法:
┌ ─ ─ ─ ─ ─ ─ ┐
┌─────────┐
│ │Module A │ │
└─────────┘
┌──────────────┐ split │ ┌─────────┐ │
│Single Project│───────> │Module B │
└──────────────┘ │ └─────────┘ │
┌─────────┐
│ │Module C │ │
└─────────┘
└ ─ ─ ─ ─ ─ ─ ┘
对于Maven工程来说,原来是一个大项目:
single-project
├── pom.xml
└── src
现在可以分拆成3个模块:
mutiple-project
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
Maven可以有效地管理多个模块,我们只需要把每个模块当作一个独立的Maven项目,它们有各自独立的pom.xml。
1、提取共同部分作parentparent的是pom而不是jar,因为parent本身不含任何Java代码。编写parent的pom.xml只是为了在各个模块中减少重复的配置。现在我们的整个工程结构如下:
multiple-project
├── pom.xml (我在idea中不需要)
├── parent
│ └── pom.xml
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
Maven支持模块化管理,可以把一个大项目拆成几个模块:
- 可以通过继承在parent的pom.xml统一定义重复配置;
- 可以通过编译多个模块;
在Maven中定义了超级pom.xml,任何没有申明自己父pom.xml的pom.xml都将默认继承自这个超级pom.xml。文件为pom-4.0.0.xml,后文pom之间的关系里有说明。
超级pom.xml的位置:
Maven 2.xxx版本:在maven安装目录下的maven2/lib/maven-xxx-uber.jar中,org.apache.maven.project下会有pom-4.0.0.xml的文件。
Maven 3.xxx版本: 在maven安装目录下的maven3/lib/maven-model-builder-xxx.jar中, \org\apache\maven\mode目录中的pom-4.0.0.xml。
先来看一下这个超级pom.xml的定义:
4.0.0
central
Central Repository
https://repo.maven.apache.org/maven2
default
false
central
Central Repository
https://repo.maven.apache.org/maven2
default
false
never
${project.basedir}/target
${project.build.directory}/classes
${project.artifactId}-${project.version}
${project.build.directory}/test-classes
${project.basedir}/src/main/java
${project.basedir}/src/main/scripts
${project.basedir}/src/test/java
${project.basedir}/src/main/resources
${project.basedir}/src/test/resources
maven-antrun-plugin
1.3
maven-assembly-plugin
2.2-beta-5
maven-dependency-plugin
2.8
maven-release-plugin
2.3.2
${project.build.directory}/site
release-profile
performRelease
true
true
maven-source-plugin
attach-sources
jar
true
maven-javadoc-plugin
attach-javadocs
jar
true
maven-deploy-plugin
true
前面说到一个pom.xml来说有几个元素是必须定义的,一个是project根元素,然后就是它里面的modelVersion、groupId、artifactId和version。由上面的超级pom.xml的内容我们可以看到pom.xml中没有groupId、artifactId和version的定义,所以我们在建立自己的pom.xml的时候就需要定义这三个元素。和java里面的继承类似,子pom.xml会完全继承父pom.xml中所有的元素,而且对于相同的元素,一般子pom.xml中的会覆盖父pom.xml中的元素,但是有几个特殊的元素它们会进行合并而不是覆盖。这些特殊的元素是:
- dependencies
- developers 开发者,和功能无关
- contributors 贡献者,和功能无关
- build下的plugin列表,包括plugin下面的reports
- build下的resources
下面示例是maven项目中的pom.xml文件。注意,其中的modelVersion是4.0.0,这是当前仅有可以被Maven2&3同时支持的POM版本。
4.0.0
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
一个最简单的pom.xml的定义必须包含modelVersion、groupId、artifactId和version这四个元素,当然这其中的元素也是可以从它的父项目中继承的。
4.0.0
otowa.user.dao
user-dao
0.0.1-SNAPSHOT
war
maven
碰上非开源包,maven支持不了这个包,那么则有有三种 方法处理:
- 本地安装这个插件install plugin
- 创建自己的repositories并且部署这个包。
- 设置scope为system,并且指定系统路径。
对于某个依赖,Maven只需要3个变量即可唯一确定某个jar包:
- groupId:属于组织的名称,类似Java的包名;
- artifactId:该jar包自身的名称,类似Java的类名;
- version:该jar包的版本。
通过上述3个变量,即可唯一确定某个jar包。Maven通过对jar包进行PGP签名确保任何一个jar包一经发布就无法修改。修改已发布jar包的唯一方法是发布一个新版本。
因此,某个jar包一旦被Maven下载过,即可永久地安全缓存在本地。
注:只有以-SNAPSHOT
结尾的版本号会被Maven视为开发版本,开发版本每次都会重复下载,这种SNAPSHOT版本只能用于内部私有的Maven repo,公开发布的版本不允许出现SNAPSHOT。
通过idea的多模块管理进行测验:
建立empty project,在project中再建立maven module。
父工程的packaging的标签值必须为pom。
父工程下的目录结构只有一个pom.xml文件(idea自己的配置文件不算)
父工程module-p的pom.xml定义如下:
4.0.0
com.cijian.maven
module-p
1.0.0
pom
子工程maven-01,需要继承父工程module-p,子工程必须指明父工程的groupId、artifactId和version。
可以这样定义maven-01的pom.xml文件。
com.cijian.maven
module-p
1.0.0
../projectA/pom.xml
4.0.0
maven-01
jar
1)pom中dependency设置说明
依赖关系列表(dependency list)是POM的重要部分。
项目之间的依赖是通过pom.xml文件里面的dependencies元素下面的dependency元素进行的。一个dependency元素定义一个依赖关系。在dependency元素中我们主要通过依赖项目的groupId、artifactId和version来定义所依赖的项目。
先来看一个简单的项目依赖的示例吧,假设我现在有一个项目projectA,然后它里面有对junit的依赖,那么它的pom.xml就类似以下这个样子:
4.0.0
com.tiantian.mavenTest
projectB
1.0-SNAPSHOT
jar
junit
junit
3.8.1
test
true
groupId, artifactId, version:描述了依赖的项目唯一标志。
type:对应于依赖项目的packaging类型,默认是jar。
scope:用于限制相应的依赖范围、传播范围。scope的主要取值范围如下(还有一个是在Maven2.0.9以后版本才支持的import,关于import作用域将在后文《Dependency介绍》中做介绍):
-
test:在测试范围有效,它在执行命令test的时候才执行,并且它不会传播给其他模块进行引入,比如 junit,dbunit 等测试框架。
-
compile(default 默认):这是它的默认值,这种类型很容易让人产生误解,以为只有在编译的时候才是需要的,其实这种类型表示所有的情况都是有用的,包括编译和运行时。而且这种类型的依赖性是可以传递的。
-
runtime:在程序运行的时候依赖,在编译的时候不依赖,比如mysql的jdbc。
-
provided:这个跟compile很类似,但是它表示你期望这个依赖项目在运行时由JDK或者容器来提供。这种类型表示该依赖只有在测试和编译的情况下才有效,在运行时将由JDK或者容器提供。这种类型的依赖性是不可传递的。比如 javaee:
- eclipse开发web环境中是没有javaee必须要手动添加。
- myeclipse新建web项目会有JavaEE(servlet-api.jar,jsp-api.jar...)web容器依赖的jar包,一般都是做开发的时候才使用。但是myeclipse不会把这些 jar包发布的,lib下你是找不到javaee引入的jar包,因为myeclipse发布项目的时候会忽略它。为什么?因为tomcat容器bin/lib已经存在了这个jar包了。
-
system:这种类型跟provided类似,唯一不同的就是这种类型的依赖我们要自己提供jar包,这需要与另一个元素systemPath来结合使用。systemPath将指向我们系统上的jar包的路径,而且必须是给定的绝对路径。
-
systemPath:上面已经说过了这个元素是在scope的值为system的时候用于指定依赖的jar包在系统上的位置的,而且是绝对路径。该元素必须在依赖的 jar包的scope为system时才能使用,否则Maven将报错。
-
optional:当该项目本身作为其他项目的一个依赖时标记该依赖为可选项。假设现在projectA有一个依赖性projectB,我们把projectB这个依赖项设为optional,这表示projectB在projectA的运行时不一定会用到。这个时候如果我们有另一个项目projectC,它依赖于projectA,那么这个时候因为projectB对于projectA是可选的,所以Maven在建立projectC的时候就不会安装projectB,这个时候如果projectC确实需要使用到projectB,那么它就可以定义自己对projectB的依赖。当一个依赖是可选的时候,我们把optional元素的值设为true,否则就不设置optional元素。
-
exclusions:考虑这样一种情况,我们的projectA依赖于projectB,然后projectB又依赖于projectC,但是在projectA里面我们不需要projectB依赖的projectC,那么这个时候我们就可以在依赖projectB的时候使用exclusions元素下面的exclusion排除projectC。这个时候我们可以这样定义projectA对projectB的依赖:
-
com.tiantian.mavenTest
projectB
1.0-SNAPSHOT
com.tiantian.mavenTest
projectC
2)pom中build设置说明
defaultGoal:默认的目标,必须跟命令行上的参数相同,如:jar:jar,或者与时期parse相同,例如install。
directory:指定build target目标的目录,默认为$(basedir}/target,即项目根目录下的target
finalName:指定去掉后缀的工程名字,例如:默认为artifactId−artifactId−{version}
filters:用于定义指定filter属性的位置,例如filter元素赋值filters/filter1.properties,那么这个文件里面就可以定义name=value对,这个name=value对的值就可以在工程pom中通过name引用,默认的filter目录是name引用,默认的filter目录是{basedir}/src/main/fiters/ 。
resources:描述工程中资源的位置。
META-INF/plexus
false
${basedir}/src/main/plexus
configuration.xml
**/*.properties
- targetPath:指定build资源到哪个目录,默认是base directory
- filtering:指定是否将filter文件(即上面说的filters里定义的*.property文件)的变量值在这个resource文件有效,例如上面就指定那些变量值在configuration文件无效。
- directory:指定属性文件的目录,build的过程需要找到它,并且将其放到targetPath下,默认的directory是${basedir}/src/main/resources
- includes:指定包含文件的patterns,符合样式并且在directory目录下的文件将会包含进project的资源文件。
- excludes:指定不包含在内的patterns,如果inclues与excludes有冲突,那么excludes胜利,那些符合冲突的样式的文件是不会包含进来的。
- testResources:这个模块包含测试资源元素,其内容定义与resources类似,不同的一点是默认的测试资源路径是${basedir}/src/test/resources,测试资源是不部署的。
plugins配置:
org.apache.maven.plugins
maven-jar-plugin
2.0
false
true
test
...
...
pluginManagement:与并列,两者之间的关系类似于与之间的关系。中也配置,其配置参数与中的完全一致。
只是,往往出现在父项目中,其中配置的往往通用于子项目。子项目中只要在中以声明该插件,该插件的具体配置参数则继承自父项目中对该插件的配置,从而避免在子项目中进行重复配置。
3)pom中其它参数设置说明
dependencyManagement:是用于帮助管理chidren的dependencies的。例如如果parent使用dependencyManagement定义了一个dependencyon junit:junit4.0,那么 它的children就可以只引用 groupId和artifactId,而version就可以通过parent来设置,这样的好处就是可以集中管理 依赖的详情
modules:对于多模块的project,outer-module没有必需考虑inner-module的dependencies,当列出modules的时候,modules的顺序是不重要的,因为maven会自动根据依赖关系来拓扑排序。
properties:是用来定义一些配置属性,例如设置编码,版本号等。
reporting设置:reporting包含site生成阶段的一些元素,某些maven plugin可以生成reports并且在reporting下配置。例如javadoc,maven site等,在reporting下配置的report plugin的方法与build几乎一样,最不同的是build的plugin goals在executions下设置,而reporting的configures goals在reporttest。
name:项目除了artifactId外,可以定义多个名称 description: 项目描述 url:项目url inceptionYear:创始年份
等略...
4)pom引用属性
在pom.xml文件中我们可以使用propertyName的形式引用属性。是值的占位符,类似EL,类似ant的属性,比如propertyName的形式引用属性。是值的占位符,类似EL,类似ant的属性,比如{X},可用于pom文件任何赋值的位置。有以下分类:
- env.propertyName:这种形式表示引用的是环境变量,比如我们需要引用当前系统的环境变量PATH的时候,就可以使用${env.PATH}。
- project.propertyName:这种形式表示引用的是当前这个pom.xml中project根元素下面的子元素的值。比如我们需要引用当前project下面的version的时候,就可以使用${project.version}。
- settings.propertyName:这种形式引用的是Maven本地配置文件settings.xml或本地Maven安装目录下的settings.xml文件根元素settings下的元素。比如我们需要引用settings下的本地仓库localRepository元素的值时,我们可以用${settings.localRepository}
- Java System Properties:java的系统属性,所有在java中使用java.lang.System.getProperties()能够获取到的属性都可以在pom.xml中引用,比如${java.home}。
- 自定义:pom.xml中properties元素下面的子元素作为属性。假如在pom.xml中有如下一段代码helloWorld,那么我们就可以使用${hello.world}引用到对应的helloWorld。
实现 SSM 工程构建,规范依赖管理。场景:根据 id 展示商品信息。
1、创建一个 maven 工程新建一个 ssm_maven 项目,使用下图选中的骨架:
填写坐标:
查看是否使用的自己的私服:
在 main 目录下新建 java 和 resources 文件夹:
把 java 和 resources 文件夹转成 source root:
修改编译版本,在 pom.xml 文件中添加:
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
1.8
UTF-8
先添加 springmvc 的核心依赖的坐标:
org.springframework
spring-webmvc
4.2.4.RELEASE
会发现出现除了 spring-webmvc 以外的其他 jar。因为我们的项目依赖 spring-webmv.jar,而 spring-webmv.jar 会依赖 spring-beans.jar 等等,所以 spring-beans.jar 这些 jar 包也出现在了我 们的 maven 工程中,这种现象我们称为依赖传递。
从下图中可看到他们的关系:(请注意 spring-beans 的版本)。
接着添加一个依赖:
我们会发现这两个 jar 包同时都依赖了 spring-beans。
但是,spring-webmvc 依赖 spirng-beans-4.2.4,spring-context 依赖 spring-beans-5.0.2,但是发现 spirng-beans-4.2.4 加入到工程中。
而我们希望 spring-beans-5.0.2 加入工程。这就造成了依赖冲突。
解决依赖冲突有以下原则:
maven自动按照下边的原则调解:
1. 第一声明者优先原则
在 pom 文件定义依赖,先声明的依赖为准。
测试:
如果将上边 spring-webmvc 和 spring-context 顺序颠倒,系统将导入 spring-beans-5.0.2。 分析: 由于 spring-webmvc 在前边以 spring-webmvc 依赖的 spring-beans-5.0.2 为准,所以最终spring-beans-5.0.2 添加到了工程中。
2. 路径近者优先原则
例如:还是上述情况,spring-contex 和 spring-webmvc 都会传递过来 spirng-beans,那 如果直接把 spring-beans 的依赖直接写到 pom 文件中,那么项目就不会再使用其他依赖传 递来的 spring-beans,因为自己直接在 pom 中定义 spring-beans 要比其他依赖传递过来的路径要近。
在本工程中的 pom 中加入 spirng-beans-5.0.2 的依赖,根据路径近者优先原则,系统将导入spirng-beans-5.0.2:
上边的问题也可以通过排除依赖方法辅助依赖调解,如下:
比如在依赖 spring-webmvc 的设置中添加排除依赖,排除 spring-beans, 下边的配置表示:依赖 spring-webmvc,但排除 spring-webmvc 所依赖的 spring-beans。
面对众多的依赖,有一种方法不用考虑依赖路径、声明优化等因素可以采用直接锁定版 本的方法确定依赖构件的版本,版本锁定后则不考虑依赖的声明顺序或依赖的路径,以锁定 的版本的为准添加到工程中,此方法在企业开发中常用。
如下的配置是锁定了 spring-beans 和 spring-context 的版本:
还可以把版本号提取出来,使用标签设置成变量。
注意:在工程中锁定依赖的版本并不代表在工程中添加了依赖,如果工程需要添加锁定版本 的依赖则需要单独添加标签,如下:
上边添加的依赖并没有指定版本,原因是已在中锁定了版本, 所以在下不需要再指定版本。
4、定义 pom.xmlmaven 工程首先要识别依赖,web 工程实现 SSM 整合,需要依赖 spring-webmvc5.0.2、 spring5.0.2、mybatis3.4.5 等,在 pom.xml 添加工程如下依赖:
(在实际企业开发中会有架构师专门来编写 pom.xml)
分两步:
1. 锁定依赖版本
2. 添加依赖
4.0.0
cn.itcast.ssm_maven
ssm_maven
1.0-SNAPSHOT
war
5.0.2.RELEASE
5.0.2.RELEASE
3.4.5
org.mybatis
mybatis
${mybatis.version}
org.springframework
spring-webmvc
${springmvc.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-core
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-expression
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-tx
${spring.version}
org.mybatis
mybatis
org.mybatis
mybatis-spring
1.3.1
mysql
mysql-connector-java
5.1.32
com.alibaba
druid
1.0.9
org.springframework
spring-webmvc
org.springframework
spring-context
org.springframework
spring-core
org.springframework
spring-aop
org.springframework
spring-web
org.springframework
spring-expression
org.springframework
spring-beans
org.springframework
spring-aspects
org.springframework
spring-context-support
org.springframework
spring-test
org.springframework
spring-jdbc
org.springframework
spring-tx
junit
junit
4.12
javax.servlet
servlet-api
2.5
provided
javax.servlet
jsp-api
2.0
provided
javax.servlet
jstl
1.2
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
1.8
UTF-8
org.apache.tomcat.maven
tomcat7-maven-plugin
2.2
/
8080
5、Dao 层
在 src/main/java 中定义 dao 接口,实现根据 id 查询商品信息。
1. pojo 模型类
在 src/main/java 创建模型类:
public class Items {
private Integer id;
private String name;
private Float price;
private String pic;
private Date createtime;
private String detail;
………
}
2. dao层代码
3. 配置文件
注意配置文件的位置:
内容如下:
select * from items where id=#{id}
在 src/main/resources 创建 applicationContext.xml:
在 src/main/resources 配置 log4j.properties:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5 p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
#在开发阶段日志级别使用 debug
log4j.rootLogger=debug, stdout
### 在日志中输出 sql 的输入参数 ###
log4j.logger.org.hibernate.type=TRACE
4. 单元测试
在 src/test/java 创建单元测试类:
public class ItemsMapperTest {
@Test
public void testFindItemsById() {
//获取 spring 容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
//获取 Mapper
ItemsMapper itemsMapper = applicationContext.getBean(ItemsMapper.class);
//调用 Mapper 方法
Items items = itemsMapper.findById(1); System.out.println(items);
}
}
6、Service 层
@Service
@Transactional
public class ItemsServiceImpl implements ItemsService {
@Autowired
private ItemsMapper itemsMapper;
@Override
public Items findById(int itemId) { return itemsMapper.findById(itemId);
}
在 applicationContext.xml 中配置 service:
7、Web层
@Controller
@RequestMapping("/items/")
public class ItemsController {
@Autowired
private ItemsService itemsService ;
// 展示商品信息页面
@RequestMapping("/showItem")
public String showItem(int id,Model model){ Items items = itemsService.findById(id);
model.addAttribute("item", items);
return "viewItem";
}
在springmvc.xml中进行配置:
Web.xml中加载 spring 容器,配置 springmvc 前端控制器:
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc.xml
springmvc
*.action
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
classpath*:applicationContext*.xml
8、JSP
/WEB-INF/jsp/viewItem.jsp配置,如下:
商品信息
商品名称
${item.name }
商品价格
${item.price }
生成日期
商品简介
${item.detail}
9、运行与调试
添加 tomcat7 插件,双击右侧 tomcat7 运行:
运行结果如下:
分为以下三个模块:
ssm_dao
ssm_service
ssm_web
1. 创建maven-parent 父模块
选择骨架创建父工程:
填写坐标:
确认使用的是本地仓库:
注意代码所在的路径(默认):
设置项目的打包方式:
在父工程的 pom.xml 中抽取一些重复的配置的,比如:锁定 jar 包的版本、设置编译版本等。
5.0.2.RELEASE
5.0.4.RELEASE
3.4.5
org.mybatis
mybatis
${mybatis.version}
org.springframework
spring-webmvc
${springmvc.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-core
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-expression
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-tx
${spring.version}
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
1.8
2. 将父工程发布至仓库
父工程创建完成执行 install 将父工程发布到仓库方便子工程继承:
3. 创建dao子模块
在父工程上右击创建maven模块:
选择“跳过骨架选择”:
填写模块名称:
下一步,确定项目的目录:
打包方式是 jar:
只添加到层的 pom,mybatis 和 spring 的整合相关依赖:
org.mybatis
mybatis
org.mybatis
mybatis-spring
1.3.1
mysql
mysql-connector-java
5.1.32
com.alibaba
druid
1.0.9
org.springframework
spring-context
org.springframework
spring-core
org.springframework
spring-aop
org.springframework
spring-web
org.springframework
spring-expression
org.springframework
spring-beans
org.springframework
spring-aspects
org.springframework
spring-context-support
org.springframework
spring-test
org.springframework
spring-jdbc
org.springframework
spring-tx
把文件夹转成 sources root:
将 ssm_maven 工程中的 dao 接口、映射文件及 pojo 类拷贝到 src/main/java 中:
将 applicationContext.xml 拆分出一个 applicationContext-dao.xml,此文件中只配置 dao 相关:
在 dao 模块的 pom.xml 添加 junit 的依赖,添加时 Scope 选择 test:
编写 junit 测试代码 :
public class ItemsMapperTest {
@Test
public void testFindItemsById() {
//获取 spring 容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext-dao.xml");
//获取 Mapper
ItemsMapper itemsMapper = applicationContext.getBean(ItemsMapper.class);
//调用 Mapper 方法
Items items = itemsMapper.findById(1); System.out.println(items);
}
}
4. 把 dao 模块 install 到本地仓库。
调过测试,install到本地仓库:
5. 创建service子模块
方法同 ssm_dao 模块创建方法,模块名称为 ssm_service。
ssm_service 模块的 pom.xml 文件中需要继承父模块,ssm_service 依赖 ssm_dao 模块,添加 spring 相关的依赖:
org.springframework
spring-jdbc
org.springframework
spring-tx
cn.itcast.ssm
ssm_dao
1.0-SNAPSHOT
将 ssm_maven 工程中的 service 接口拷贝到 src/main/java 中:
创建 applicationContext-service.xml,此文件中定义的 service:
当在写junit测试时发现,代码报出没有找不到类的错误信息:
是因为没有 junit.jar 引起的,为什么会这样呢?我们 ssm_dao 模块中有 junit 依赖而ssm_service 模块依赖了 ssm_dao,难道 junit 不应该传递过来吗?
6. 依赖范围对传递依赖的影响
是因为依赖会有依赖范围,依赖范围对传递依赖也有影响,例如有 A、B、C,A 依赖 B、B 依赖 C,C 可能是 A 的传递依赖,如下图:
最左边一列为直接依赖,理解为 A 依赖 B 的范围,最顶层一行为传递依赖,理解为B依赖C的范围,行与列的交叉即为 A 传递依赖C的范围。
举例 1:
比如 A 对 B 有 compile 依赖,B 对 C 有 runtime 依赖,那么根据表格所示 A 对 C 有 runtime 依赖。
ssm_dao 依赖 junit,scop 为 test ssm_service 依赖 ssm_dao. 查看下图红色框内所示传递依赖范围:
所以 ssm_dao 工程所依赖的 junit 的 jar 没有加入到 ssm_service 工程。
举例 2:如果修改 ssm_dao 工程依赖 junit 的 scop 为 compile,ssm_dao 工程所依赖的 junit 的 jar 包会加入到 ssm_service 工程中,符合上边表格所示,查看下图红色框内所示:
遇到依赖没有传递过来的问题我们通常的解决方案是在本工程中直接添加依赖。
把如下依赖添加到 ssm_service 的工程中:
再看测试代码也不报错了:
Install 到本地仓库:
7. 创建 web 子模块
选择骨架创建 web 子模块:
确认使用自己的本地仓库:
填写模块名称:
使用骨架创建 web 项目会花费些时间,请耐心等待。
创建 java 和 resources 文件夹,转成 source root:
添加打包方式 war:
ssm_web 模块的 pom.xml 文件中需要继承父模块,ssm_web 依赖 ssm_service 模块,和 springmvc 的依赖:
ssm_parent
cn.itcast.ssm
repositories 菜单,可以看到该路径。
DrepositoryId 服务器的表示 id,在 nexus 的 configuration 可以看到。
Dversion 表示版本信息,
关于 jar 包准确的版本: 包的名字上一般会带版本号,如果没有那可以解压该包,会发现一个叫 MANIFEST.MF 的文件,
这个文件就有描述该包的版本信息。
比如 Specification-Version: 2.2 可以知道该包的版本了。 上传成功后,在 nexus 界面点击 3rd party 仓库可以看到这包。