Maven高级
学习目标
- 了解搭建私服的使用
- 理解传递依赖
- 掌握如何解决依赖版本冲突问题
- 能够使用maven构建SSM工程
- 学习使用maven分模块方式构建工程
1. 私服:私服的介绍
目标
理解私服的作用
仓库的介绍
仓库的概念
中央仓库
目前来说: https://repo1.maven.org/maven2/是真正的Maven中央仓库的地址,该地址内置在Maven的源码中其他的都是镜像。
什么是仓库镜像
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像,使用镜像可以提高项目构建效率。
仓库的索引
中央仓库带有索引文件以方便用户对其进行搜索,索引每周更新一次,中央仓库的索引有几十M。
黑名单
如果某个IP地址恶意的下载中央仓库内容,例如全公司100台机器使用同一个IP反复下载,这个IP(甚至是IP段)会进入黑名单,因此稍有规模的使用Maven时,应该用Nexus架设私服。
私服
有些公司不提供外网给项目组人员,因此就不能使用maven访问远程的仓库地址,所以很有必要在局域网里找一台有外网权限的机器,搭建nexus私服,然后开发人员连到这台私服上,这样的话就可以通过这台搭建了nexus私服的电脑访问maven的远程仓库。
2. 私服:私服的安装和启动
下载
Nexus的官网 https://www.sonatype.com/
- 选择导航菜单Products
- 选择OSS Edition
注:资料中已经下载latest-win64直接使用即可。
默认是使用jetty做为web容器
安装
- 在任意位置创建目录:nexus
- 解压到nexus目录下会出现两个目录,两个目录都有用
- 安装路径下etc目录中nexus-default.properties文件保存有nexus基础配置信息,例如默认访问端口,nexus默认的端口号是8081
- 安装路径下bin目录中nexus.vmoptions文件保存有nexus服务器启动对应的配置信息,例如默认占用内存空间
启动
注:要保证JDK环境配置正确
- 进入以下目录
- 输入cmd进入命令行
- 可以输入nexus /?查看所有的参数,输入 /run在窗口下运行服务器
- 如果弹出以下窗口,点允许访问
- 启动成功,默认是8081,不要关闭这个窗口,不然服务器就退出了
访问
- 当Nexus启动后,可以在浏览器中输入:http://localhost:8081/可以看到以下页面
- 点击右上角登录
- 弹出以下对话框
- 输入默认的帐号:admin,密码:在admin.password文件中找
- 登录成功以后出现配置向导,直接点next
- 修改密码,输入两次,建议都使用admin,不要忘记了
- 允许匿名的账户登录
- 配置完成
3. 私服:仓库类别和配置
类别介绍
proxy:是远程仓库的代理。比如说在nexus中配置了一个central仓库的proxy,当用户向这个proxy请求一个组件,这个proxy就会先在本地查找,如果找不到的话,就会从远程仓库下载,然后返回给用户,相当于起到一个中转的作用。
Hosted:是宿主仓库,用户可以把自己的一些构件,deploy到hosted中,也可以手工上传构件到hosted里。比如说oracle的驱动程序,在central repository是获取不到的,就需要手动上传到hosted里。
Group:是仓库组,在maven里没有这个概念,是nexus特有的。目的是将上述多个仓库聚合,对用户暴露统一的地址,这样用户就不需要在pom中配置多个地址,只要统一配置group的地址就可以了。
注:公司内部通常只需关注hosted宿主仓库就可以了
修改配置
中央仓库的地址
Nexus的中央仓库,默认配置的是maven的中央仓库:https://repo1.maven.org/maven2/,为了提高速度可以配置成阿里云的中央仓库https://maven.aliyun.com/repository/central
- 选择中央仓库
- 修改地址:Remote storage的位置
- 记得点最下面的保存按钮
修改RELEASES为可以重复部署
因为下面要上传jar包到这个仓库RELEASES下
- 仓库选择Releases
- 在Access Settings中选择Allow Redeploy,设置为可以重复部署
- 点保存按钮
4. 私服:上传项目到私服
- 配置本地仓库访问私服的权限,修改maven的conf/settings.xml配置文件,用户名和密码,复制如下片段到相应的位置,大约在111行。注意:密码要与你设置的密码相同
<server>
<id>releases</id>
<username>admin</username>
<password>admin</password>
</server>
<server>
<id>snapshots</id>
<username>admin</username>
<password>admin</password>
</server>
- 重新创建一个新的模块,配置要上传项目中的发布管理,添加如下代码段:
坐标如下:
- 在pom.xml文件中添加以下代码段
<distributionManagement>
<repository>
<id>releases</id>
<url>http://localhost:8081/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
上面的url即仓库地址
- 执行deploy命令,上传项目到私服。
- 上传日志结果
- 去私服查看snapshots上传的结果
- 修改当前模块的坐标为RELEASE
- 再次上传到私服
- 查看上传的日志
- 查看RELEASE仓库上传的结果
5. 私服:从私服下载项目
目标
从私服下载资源
私服下载的配置
注:项目A必须是RELEASE版本才可以使用
- 假设上面的模块是A,创建另一个模块B
<groupId>com.itheima</groupId>
<artifactId>heima-deploy-b</artifactId>
<version>1.0-RELEASE</version>
- 依赖于上面的模块A
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>heima-deploy</artifactId>
<version>1.0-RELEASE</version>
</dependency>
</dependencies>
- 删除本地仓库中A模块的jar包
- 修改%MAVEN_HOME%\conf\setttings.xml文件,项目B是依赖于私服仓库中模块A
注:后面还要再改回来,以后还是使用阿里云的镜像<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/repository/maven-public/</url>
</mirror>
- clean清除项目B,再complie编译一次
- 可以看到日志信息是从本地仓库中下载
改回阿里云远程仓库
注:接下来的学习还是要使用原来的配置,上面只是临时配置一下。
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
6. 回顾:依赖范围
目标
- 依赖的作用
- 依赖范围
依赖范围
在maven中三种类路径
classpath范围 | 理解为 |
---|---|
编译类路径 | 在main目录下起作用 |
测试类路径 | 在test目录下起作用 |
运行时类路径 | 在运行的过程中起作用,在target目录下 |
依赖范围
依赖范围 | 编译类路径 | 测试类路径 | 运行时类路径 |
---|---|---|---|
compile (默认) | Y | Y | Y |
provided | Y | Y | (不会打包到项目中) |
runtime | Y | Y | |
test | Y |
7. 依赖:依赖传递
什么是依赖传递
概念:某个项目依赖了另一个项目,另一个项目中jar包会不会传递到这个项目中来
面试题
- 项目A依赖junit,依赖范围是test,项目B依赖项目A,问: 项目B对junit是否有依赖?
- 项目A依赖junit,依赖范围是compile,项目B依赖项目A,问: 项目B对junit是否有依赖?
- 项目A依赖junit,依赖范围是provided,项目B依赖项目A,问: 项目B对junit是否有依赖?
- 项目A依赖junit,依赖范围是runtime,项目B依赖项目A,问: 项目B对junit是否有依赖?
依赖传递
实验设计:两个项目
创建项目:maven_a
创建项目:maven_b
演示
- 项目A依赖junit,依赖范围是test,项目B依赖项目A,问: 项目B对junit是否有依赖?
没有
- 项目A依赖junit,依赖范围是compile,项目B依赖项目A,问: 项目B对junit是否有依赖?
有,产生依赖传递
- 项目A依赖junit,依赖范围是provided,项目B依赖项目A,问: 项目B对junit是否有依赖?
没有
- 项目A依赖junit,依赖范围是runtime,项目B依赖项目A,问: 项目B对junit是否有依赖?
有,产生依赖传递
小结
哪两种依赖范围会产生传递依赖?
compile, runtime 在运行时中起作用中范围会产生依赖传递
8. 依赖:依赖可选和依赖排除【重点】
目标
- 什么是可选依赖和依赖排除
- 分别如何配置
依赖可选
前提是会产生依赖传递的两种情况
什么是可选依赖
- A项目依赖了junit
- B项目依赖了A项目
- 根据依赖传递特性,默认情况下B项目会依赖junit
- 我们可以在A项目通过可选依赖,让B项目不能依赖junit
配置方式
在A项目的依赖配置中,通过optional子标签进行配置。它有两个取值:
- true:是可选依赖,不进行依赖传递
- false:默认值,进行依赖传递。
我是A项目,由我决定要不要给你,我的地盘我做主
步骤
- 创建A项目,依赖于junit
- 创建B项目,依赖于项目A
- 在项目A中指定optional为true
- 查看项目B的依赖情况
依赖排除
什么是依赖排除
- A项目依赖了junit
- B项目依赖了A项目
- 根据依赖传递特性,默认情况下B项目会依赖junit
- 我们可以在B项目通过排除依赖,配置B项目不依赖junit
配置步骤
通过在B项目中配置 exclusions标签进行配置
- 在依赖A项目下的version标签下编写exclusions
- exclusions中每个exclusion是一个要排除的jar包
- 只需指定groupId和artifactId就可以了,不用指定version版本
你给我就要,我不是很没面子?我来决定要不要
代码
项目A的pom.xml
<?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>com.itheima</groupId>
<artifactId>day51_02_A</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
<!-- 依赖可选,取值是true或false,这个包不会传递下去,我要决定给不给 -->
<!--<optional>true</optional>-->
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</project>
项目B的pom.xml
<?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>com.itheima</groupId>
<artifactId>day51_03_B</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--依赖于项目A-->
<dependency>
<groupId>com.itheima</groupId>
<artifactId>day51_02_A</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 依赖排除,我来决定要不要 -->
<exclusions>
<!-- 可以设置多个 -->
<exclusion>
<!-- 要写排除的坐标,不用写version -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
小结
依赖配置 | 配置元素 |
---|---|
可选 | <optional>true</optional> |
排除 | <exclusions><exclusion>坐标没有version</exclusion></exclusions> |
9. 依赖:依赖冲突【重点】
目标
- 依赖冲突的三种情况
- 如何解决依赖冲突
情况1:两个直接依赖
问题
一个项目中同时配置了2份junit4.12和junit4.11不同的版本,项目最终会使用哪个junit的版本?
配置文件pom.xml
<dependencies>
<!--默认是compile,会产生依赖传递-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
效果
结论
如果两个直接依赖:以下面配置的为准
情况2:一个直接依赖一个间接依赖
问题
A项目依赖junit4.12,B项目依赖junit4.11,B项目依赖于A项目,B项目使用哪个版本的junit?
配置文件
项目A的pom.xml
<?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>com.itheima</groupId>
<artifactId>day51_02_A</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
项目B的pom.xml (junit4.12是间隔依赖,junit4.11是直接依赖)
<?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>com.itheima</groupId>
<artifactId>day51_03_B</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 直接依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!--依赖于项目A-->
<dependency>
<groupId>com.itheima</groupId>
<artifactId>day51_02_A</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
效果
点这个按钮可看包的依赖情况
结论
如果一个间接依赖,一个直接依赖:以直接依赖为准
情况3:两个间接依赖
问题
spring-core包到底使用哪个?
- spring-aop 5.2.0间接依赖了spring-core和spring-beans
- spring-tx 4.2.8间接依赖了spring-core和spring-beans
如果版本不同使用哪个jar包
场景
在project_a 中,同时配置spring-aop 5.2.0和spring-tx 4.2.8
配置文件
spring-aop 5.2.0在上面效果
spring-tx 4.2.8在上面的效果
代码
<!-- 两个间接依赖spring-core包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.2.8.RELEASE</version>
</dependency>
结论
如果2个间接依赖:以上面配置为准
小结
依赖冲突的三种情况 | 依赖如何处理的 | |
---|---|---|
1 | 两个直接依赖 | 以下面配置的为准 |
2 | 一个直接依赖一个间接依赖 | 以直接为准 |
3 | 两个间接依赖 | 以上面配置的为准 |
10. 多环境配置
多环境介绍
maven提供配置多种环境的设定,帮助开发者使用过程中快速切换环境
步骤
- 创建的maven工程,复制jdbc.properties到resources目录下
- 修改jdbc.properties中的jdbc.url=${jdbc.url}
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=${jdbc.url}
jdbc.username=root
jdbc.password=root
- 在pom.xml中profiles下配置三个不同的环境:开发,生产,测试
- 在profile的properties中分别指定不同的jdbc.url属性值
- 分别选择不同的profile
指定resource的目录地址和开启替换的参数 ```xml <?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">
4.0.0 com.itheima multi-env 1.0-SNAPSHOT env_dev jdbc:mysql://127.0.0.1:3306/ssm true env_pro jdbc:mysql://127.2.2.2:3306/ssm env_test jdbc:mysql://127.3.3.3:3306/ssm ${basedir}/src/main/resources true
7. 运行package命令,查看target中的jar包中jdbc.properties中的url是否不同 ![](assets/image-20210917110405664.png#crop=0&crop=0&crop=1&crop=1&id=M32Qe&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
8. 也可以使用命令行方式执行
mvn 指令 –P 环境定义id
<br />![](assets/image-20210917110636412.png#crop=0&crop=0&crop=1&crop=1&id=PQBE4&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="3acccac8"></a>
# 11. 继承与聚合:介绍
<a name="73e82552-5"></a>
## 目标
1. 分模块创建项目的好处
2. 理解继承与聚合的概念
<a name="ef636c63"></a>
## 三种不同的实现方式
<a name="bc32a18c"></a>
### 方式一:传统开发
项目中包含企业所有业务的统一系统, 如下:
![](assets/image-20200224151215253.png#crop=0&crop=0&crop=1&crop=1&id=QTtBq&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)<br />特点:传统开发方式是将这些业务集中在1个web项目中, 缺点是代码量大, 业务插拔不方便 。
<a name="fdb67ce7"></a>
### 方式二:模块开发
特点: 模块化开发业务清晰, 维护方便, 业务插拔较简单, 缺点是各模块间 仍然存在依赖 。
![](assets/image-20200603162301006.png#crop=0&crop=0&crop=1&crop=1&id=p4sZN&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="667285e3"></a>
### 方式三:微服务
特点:将各个业务拆分成多个独立的系统,如下:
![](assets/image-20200224152214033.png#crop=0&crop=0&crop=1&crop=1&id=DVD8P&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
注:今天使用第二种方式
<a name="7ef1bb26"></a>
## 传统项目与分模块构件项目区别
![](assets/image-20200629192858681.png#crop=0&crop=0&crop=1&crop=1&id=fKBCG&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="3cdd7893"></a>
## 分模块创建项目好处
1. 项目架构一目了然,更清晰
2. 有利于团队合作开发,不同的成员开发一个模块
3. 代码的可维护性更强
4. 更方便重用,例如:所有的工具类都写到一个utils工程中,不仅当前项目可以用,其他项目也可以用。
<a name="4df71bef"></a>
## 继承的作用
1. 父项目:
1. 抽取所有项目公用的配置和插件
1. 锁定当前项目中各个子项目依赖的版本
2. 子项目:<br />继承父项目,父项目的公用的配置就可以直接使用了
<a name="e00e4808"></a>
## 聚合的作用
主要是为了聚合各个子模块,作为一个整体运行,这样就实现了分模块构建项目而运行的时候是一个整体项目。
**聚合与继承通常是一起使用的**
<a name="d7df58c7"></a>
## 小结:聚合与继承的作用
![](assets/image-20200904172432469.png#crop=0&crop=0&crop=1&crop=1&id=I3QKa&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="675500c5"></a>
# 12. 继承与聚合:案例演示【重点】
<a name="73e82552-6"></a>
## 目标
演示项目的继承与聚合
1. 创建父项目project_parent
2. 创建子项目project_entity
<a name="f610ae8c"></a>
## 实现步骤
<a name="0879c556"></a>
### 第1步:创建父项目project_parent
1. 创建父模块,指定父模块的坐标 ![](assets/image-20200629213025855.png#crop=0&crop=0&crop=1&crop=1&id=CrOGM&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
2. 指定模块的名字,模块根目录,模块文件所在位置 ![](assets/image-20200629213112393.png#crop=0&crop=0&crop=1&crop=1&id=qGsl9&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
3. 因为父项目中不需要编写Java代码,所以src目录可以删除,只保留pom.xml配置文件。<br />![](assets/image-20200629213406375.png#crop=0&crop=0&crop=1&crop=1&id=PQfJS&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
4. 在父项目的pom.xml中添加junit的依赖包
```xml
<!--添加junit的依赖包-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
第2步:创建子项目project_child
- 在project_parent模块下点右键,创建子模块
- 指定子模块的名字,注:模块根目录和位置都在父模块的下一级目录
父项目的pom.xml文件变化:可以看到子模块的聚合信息
- 父模块或聚合模块的打包类型是pom
- 可以通过modules看出当前是一个聚合项目,它聚合了哪些模块
```xml
<?xml version=”1.0” encoding=”UTF-8”?>
4.0.0
com.itheima day51_04_parent 1.0-SNAPSHOT pom day51_05_child <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
```
子项目的pom.xml文件:可以看到继承于父模块的信息
- 包含parent标签:指定父模块的坐标
- 包含当前模块的坐标信息
```xml
<?xml version=”1.0” encoding=”UTF-8”?>
com.itheima day51_04_parent 1.0-SNAPSHOT
4.0.0 day51_05_child
5. 子模块中继承了父模块中的依赖包 ![](assets/image-20200629215923959.png#crop=0&crop=0&crop=1&crop=1&id=iWUA2&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="76711877"></a>
# 13. 继承与聚合:父模块版本锁定【重点】
<a name="69fa3701"></a>
## 作用
版本锁定,统一维护组件的版本
<a name="1bc21f7e"></a>
## dependencyManagement元素
> 作用:一旦在父项目中使用了版本锁定,就是统一控制各个组件的版本。
>
> 如果子项目继承父项目,父项目中版本锁定的包子项目是**不能**直接使用的。
>
> 如果要使用,需要在子项目添加依赖,但不需要指定版本号。
>
> 注:父项目中版本锁定的jar包,子项目中可以使用,也可以不使用
<a name="52b36576-2"></a>
## 步骤
1. 在父项目中pom.xml
1. 在dependencies的上一级配置dependencyManagement
1. 配置依赖于junit
1. 配置依赖spring-context
2. 此时在子项目pom.xml中并没有依赖任何包
1. 在子项目需要再配置一次依赖spring-context,子项目中不需要指定版本version,这时子项目中就有了依赖包了
1. 因为没有配置junit,所以junit的包并没有继承下来
<a name="06e004ef-2"></a>
## 代码
父项目的pom.xml
```xml
<!--使用版本锁定-->
<dependencyManagement>
<!--添加junit的依赖包-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 依赖spring的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
子项目的pom.xml
<dependencies>
<!-- 不需要指定版本,因为版本已经锁定 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
效果
小结
- 父项目如果进行了版本锁定,子项目可以有选择的使用哪些包,可以使用父项目中的包,也可以不使用
- 统一整个项目的版本
14. 分模块构建项目1:父项目和聚合项目【重点】
目标
使用分模块方式构建SSM项目
分析
需求
实现查询所有的用户和他的订单信息
无条件查询
条件查询
准备数据库环境
代码
CREATE DATABASE maven_adv IF NOT EXISTS;
USE maven_adv;
-- 用户名
CREATE TABLE `user` (
uid INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL,
sex CHAR(1) DEFAULT '男',
phone VARCHAR(20)
);
INSERT INTO `user` (`name`,sex,phone) VALUES ('张小敏','女','13923454567'), ('李强','男','18712321234'),('邹家俊','男','16233425678');
SELECT * FROM `user`;
-- 订单表
CREATE TABLE `order` (
oid INT PRIMARY KEY AUTO_INCREMENT,
address VARCHAR(100) COMMENT '订单地址',
total DOUBLE COMMENT '金额',
uid INT,
FOREIGN KEY(uid) REFERENCES `user`(uid)
);
-- 一个用户下多个订单
INSERT INTO `order` (address,total,uid) VALUES ('广东省汕头市', 3200,1),('湖南省株洲市', 250,1),('广西省柳州市', 1200,2),('广州市海珠区', 78,2),('海南省海口市', 605,3);
-- 查询所有订单
SELECT * FROM `order`;
-- 查询用户和订单
SELECT * FROM `user` u INNER JOIN `order` o ON u.uid = o.uid;
查询结果
表关系如下
创建父项目
- 创建项目ssm_parent
- 删除src目录
- pom.xml文件如下
<!--统一管理依赖包版本-->
<properties>
<!-- 指定编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<!--spring 版本-->
<spring.version>5.2.10.RELEASE</spring.version>
<!-- log4j日志包版本 -->
<slf4j.version>1.7.7</slf4j.version>
<!-- jstl标签版本 -->
<jstl.version>1.2</jstl.version>
<!--servlet版本-->
<servlet.version>3.1.0</servlet.version>
<!--jsp版本-->
<jsp.version>2.0</jsp.version>
<!-- mybatis版本号 -->
<mybatis.version>3.5.0</mybatis.version>
<!--mysql驱动版本-->
<mysql.version>5.1.30</mysql.version>
<!-- mybatis-spring整合包版本 -->
<mybatis.spring.version>1.3.1</mybatis.spring.version>
<!--druid版本-->
<druid.version>1.0.29</druid.version>
<!-- aspectj版本号 -->
<aspectj.version>1.6.12</aspectj.version>
<!--json-->
<jackson.version>2.9.8</jackson.version>
<!--lombok-->
<lombok.version>1.18.6</lombok.version>
<!-- junit -->
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<!--spring依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring mvc包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring jdbc包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--aspectj包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis-spring整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!-- mysql数据库依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--数据库连接池druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- log4j包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!--json包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--spring-test测试包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
15. 分模块构建项目2:实体类,持久层【重点】
目标
- 创建实体类子模块
- 持久层子模块
- 业务层子模块
创建父工程
- 创建实体类ssm_entity工程
- 结构如下
- 创建用户实体类
package com.itheima.entity;
import lombok.Data;
@Data
public class User {
private Integer uid;
private String name;
private char sex;
private String phone;
}
- 创建订单实体类,订单类中包含了用户类
package com.itheima.entity;
import lombok.Data;
/**
* 一个订单对应一个用户
*/
@Data
public class Order {
private Integer oid;
private String address;
private Double total;
private User user; //包含了用户对象,查询订单对象,可以获取到用户对象的属性
}
持久层步骤
- 创建ssm_dao项目
- 添加依赖: ssm_entity项目
- 编写dao接口
- 编写查询用户和订单的方法
- 方法提供address地址做为参数
- 使用XML的配置方式
- 创建实体类映射配置文件
注:在resources下创建目录名要使用/,而不是点号- 使用表连接查询用户表和订单表的信息
- 创建resultMap,指定id和type属性。
注:需要在applicationContext.xml中SqlSessionFactoryBean配置别名typeAliasesPackage - 创建子元素result,指定属性:user.uid,映射的列是uid
- 创建子元素result,指定属性:user.name,映射的列是name
- 创建子元素result,指定属性:user.sex,映射的列是sex
- 创建子元素result,指定属性:user.phone,映射的列是phone
- 查询的结果指定为上面的resultMap
实现
- 创建ssm_dao项目
添加依赖: ssm_entity
<?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">
<parent>
<artifactId>ssm_parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm_dao</artifactId>
<!-- 依赖于entity -->
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>ssm_entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 编写dao接口,查询所有的用户和订单信息。注:Order不要导错了 ```java package com.itheima.dao;
import com.itheima.entity.Order; import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface OrderDao { /**
* 查询所有的订单和用户
*/
List<Order> findUserAndOrder(@Param("address") String address);
}
4. 编写XML配置文件,查询所有的用户和订单
1. 配置映射对象
1. 将order对象中user属性创建映射关系
1. 查询订单所有的用户和订单
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.OrderDao">
<!-- 创建一个实体映射关系 -->
<resultMap id="orderMap" type="com.itheima.entity.Order">
<result property="user.uid" column="uid"/>
<result property="user.name" column="name"/>
<result property="user.sex" column="sex"/>
<result property="user.phone" column="phone"/>
</resultMap>
<!-- 查询用户和订单 -->
<select id="findUserAndOrder" resultMap="orderMap">
SELECT * FROM `user` u INNER JOIN `order` o ON u.uid = o.uid
</select>
</mapper>
16. 分模块构建项目2:业务层
步骤
- 创建项目:ssm_service
- 添加dao层的依赖:ssm_dao
- 编写service接口:查询所有用户和订单
- 编写service实现类:调用DAO查询
实现
- 创建项目:ssm_service
结构
添加dao层的依赖:ssm_dao ```xml <?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">
<artifactId>ssm_parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
4.0.0 ssm_service com.itheima ssm_dao 1.0-SNAPSHOT
3. 编写配置类<br />JdbcConfig
```java
package com.itheima.config;
@PropertySource("classpath:jdbc.properties") //加载属性文件
public class JdbcConfig {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.driverClassName}")
private String driverClassName;
/**
* 创建数据源,放到容器中
*/
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setUrl(url);
dataSource.setDriverClassName(driverClassName);
return dataSource;
}
/**
* 创建事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
MybatisConfig
jdbc.properties
log4j.properties
SpringConfig
- 编写service接口:查询所有用户和订单
方法的查询参数是字符串的地址 ```java package com.itheima.service;
import com.itheima.entity.Order;
import java.util.List;
public interface OrderService { /**
* 查询所有用户的订单
*/
List<Order> findUserAndOrder(String address);
}
5. 编写service接口实现:调用DAO查询
1. 添加@Service注解,添加到Spring容器
1. 使用@Autowared注解,从容器中注入Dao的实现类
1. 在方法中调用DAO对象查询记录
1. 使用注解式事务
```java
package com.itheima.service.impl;
import com.itheima.dao.OrderDao;
import com.itheima.entity.Order;
import com.itheima.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
/**
* 查询所有用户的订单
* @param address
*/
@Override
public List<Order> findUserAndOrder(String address) {
return orderDao.findUserAndOrder(address);
}
}
- 测试类 ```java package com.itheima.test;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class OrderTest {
@Autowired
private OrderService orderService;
@Test
public void testFindOrder() {
List<Order> orders = orderService.findUserAndOrder("");
orders.forEach(System.out::println);
}
}
<a name="844393f7-1"></a>
## 查询结果
![](assets/image-20211107210429401.png#crop=0&crop=0&crop=1&crop=1&id=tcjaF&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="e3f5fa89"></a>
# 17. 分模块构建项目3:表现层子模块【重点】
<a name="73e82552-9"></a>
## 目标
创建web层子模块
![](assets/image-20211107175856160.png#crop=0&crop=0&crop=1&crop=1&id=uQLwt&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="52b36576-4"></a>
## 步骤
1. 创建maven模块:ssm_web<br />![](assets/image-20211107210705096.png#crop=0&crop=0&crop=1&crop=1&id=G0HNc&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
2. 修改pom.xml中包的类型为war,并且刷新![](assets/image-20211107210734166.png#crop=0&crop=0&crop=1&crop=1&id=v1TQm&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
```xml
<?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">
<parent>
<artifactId>ssm-parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm-web</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>ssm-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 将java工程转成web工程
为OK以后
点Yes
- 选择上面的添加web.xml
- 选择正确的目录结构:
scr\main\webapp\WEB-INF\web.xml
- 结构如下,完成
实现
- SpringMvcConfig ```java package com.itheima.config;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration @ComponentScan({“com.itheima.controller”, “com.itheima.config”}) //扫描控制器 @EnableWebMvc //开启json转换 public class SpringMvcConfig { }
5. 启动时加载的配置类
```java
package com.itheima.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* tomcat启动的时候自动加载的配置类
*/
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 加载Spring的配置类,父容器
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 加载SpringMVC的配置类,子容器
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
18. SSM案例:显示所有订单
目标
静态资源
- 复制静态资源
- 静态资源放行
package com.itheima.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
/**
* 把静态资源放行
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
System.out.println("静态资源放行");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/**").addResourceLocations("/");
}
}
- 建议部署到tomcat中运行,注:是部署ssm_web项目到tomcat中,而不是部署ssm_parent项目
注:分模块构建中使用tomcat插件运行有时会出现找不到jar包的情况
代码
如果部署运行出现找不到类,则在父工程上打包一次
控制器代码
package com.itheima.controller;
import com.itheima.entity.Order;
import com.itheima.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 查询用户和订单
*/
@RequestMapping("/find")
public List<Order> find(String address) {
return orderService.findUserAndOrder(address);
}
}
页面测试
页面代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>查询所有联系人</title>
<!-- 1. 导入CSS的全局样式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 2. jQuery导入,建议使用1.9以上的版本 -->
<script src="js/jquery-3.3.1.js"></script>
<!-- 3. 导入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
<style type="text/css">
tr, th {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4">
<h3>用户和订单列表</h3>
</div>
<div class="col-md-8">
<div class="input-group" style="padding-top: 15px">
<div class="input-group">
<input type="text" id="address" name="address" class="form-control" placeholder="输入查询的地址">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" onclick="findAll()">查询订单</button>
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table border="1" class="table table-striped table-bordered table-hover table-condensed">
<tr class="info">
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>电话</th>
<th>地址</th>
<th>金额</th>
</tr>
<tbody id="orderBody">
<tr v-for="(order,index) in orderData">
<td>{{index+1}}</td>
<td>{{order.user.name}}</td>
<td>{{order.user.sex}}</td>
<td>{{order.user.phone}}</td>
<td>{{order.address}}</td>
<td>{{order.total}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript">
new Vue({
el: ".container",
data: {
orderData: []
},
methods: {
//查询所有订单
findAll() {
axios.get("/order/find").then(resp => {
this.orderData = resp.data;
});
}
},
created() {
this.findAll();
}
});
</script>
</html>
19. SSM案例:完成订单的模糊查询
目标
步骤
修改OrderDao.xml中的配置
- 判断address如果不为空,而且也不为空串,则做为where条件拼接在查询语句的后面
- 模糊查询使用
"%"#{address}"%"
注:mybatis中如果使用if等表达式时,方法上的参数要使用@Param(“address”)注解,否则会出现异常。
代码
OrderDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.OrderDao">
<!-- 创建一个实体映射关系 -->
<resultMap id="orderMap" type="com.itheima.entity.Order">
<result property="user.uid" column="uid"/>
<result property="user.name" column="name"/>
<result property="user.sex" column="sex"/>
<result property="user.phone" column="phone"/>
</resultMap>
<!-- 查询用户和订单 -->
<select id="findUserAndOrder" resultMap="orderMap">
SELECT * FROM `user` u INNER JOIN `order` o ON u.uid = o.uid
<if test="address!=null and address!=''">
where address like "%"#{address}"%"
</if>
</select>
</mapper>
页面代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>查询所有联系人</title>
<!-- 1. 导入CSS的全局样式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 2. jQuery导入,建议使用1.9以上的版本 -->
<script src="js/jquery-3.3.1.js"></script>
<!-- 3. 导入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
<style type="text/css">
tr, th {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4">
<h3>用户和订单列表</h3>
</div>
<div class="col-md-8">
<div class="input-group" style="padding-top: 15px">
<div class="input-group">
<input type="text" v-model="address" class="form-control" placeholder="输入查询的地址">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" @click="findAll()">查询订单</button>
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table border="1" class="table table-striped table-bordered table-hover table-condensed">
<tr class="info">
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>电话</th>
<th>地址</th>
<th>金额</th>
</tr>
<tbody id="orderBody">
<tr v-for="(order,index) in orderData">
<td>{{index+1}}</td>
<td>{{order.user.name}}</td>
<td>{{order.user.sex}}</td>
<td>{{order.user.phone}}</td>
<td>{{order.address}}</td>
<td>{{order.total}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript">
new Vue({
el: ".container",
data: {
orderData: [],
address: ''
},
methods: {
//查询所有订单
findAll() {
axios.get("/order/find?address=" + this.address).then(resp => {
this.orderData = resp.data;
});
}
},
created() {
this.findAll();
}
});
</script>
</html>