Maven 的拆分和聚合
# 110.Maven 的拆分和聚合
使用拆分和聚合,可以更好的解耦
# 之前项目的问题
之前我们用 Maven 完成了一个表的 CRUD,其实目前项目还是有一些问题的,我们考虑这样的场景:
- 我们目前的项目是分三层架构的:dao,service,controller
- 假设目前做的是一个电商项目,用户有买家和买家
- 目前有一个查看订单的功能,买家要看订单,需要查数据库,这是 dao 层的任务;
- 卖家要查订单,也要查数据库,也是 dao 层的任务
- 而此时卖家和买家用的不是同一个系统(例如某宝,卖家用的是一个后台系统),那么就会重复的代码
此时我们遇到的和问题和之前很类似:
- 在使用 Maven 之前,十个 SSM 项目有 10 套重复的依赖;使用 Maven 后,jar 包就不会重复了
- 而我们可以将我们的 dao 层也看成是一个 jar 包!他们和 Maven 的依赖没有什么不同,这样我们就可以在多个项目都使用 dao 层打成的 jar 包,减少重复!这样如果要修改 dao,只需修改一份就可以
Maven 是这样解决的:Maven 把一个完整的项目,分成不同的独立模块,这些模块都有各自独立的坐标。哪个地方需要其中某个模块,就直接引用该模块的坐标即可。有点类似乐高的拼图,我们可以使用拼图完成不同的模型
今后如果开发一个新项目,我们先考虑问题不是 dao,service,utils,domain 如何编写,我们要考虑的是这些模块是否已经存在,如果存在直接引用。以上说的就是 Maven 拆分的思想。
我们可以把拆分的模块聚合到一起编写一个完整的项目,这就是 Maven 聚合思想。此时该项目可以看成是一个父工程,其下有很多个子工程(模块)
# 父子工程的创建
我们首先创建父工程,父工程只需有一个 pom.xml 就可以了,我们在 IDEA 中创建 project 或 module 都可以。
创建后,我们可以直接将 src 目录删掉,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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
2
3
4
5
6
7
8
9
# 创建子模块
这里我们创建 3 个模块 dao,service,controller 即可,实际开发中可能有很多个模块,但创建起来都是一样的
我们在项目上右键--选择新建--选择新模块
先创建 dao 模块:这里我们不使用骨架,因此选择第一个“新建模块”,然后名称我们加个 dao
然后我们打开 dao 模块的 pom.xml(注意不是父工程的 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>
<parent>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>LearnJavaMaven_dao</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
可以看到其多了一个 parent 标签。之前我们说过每个 Maven 项目都有自己的坐标,而我们这个子模块好像没有定义?这是因为子模块和父工程共用 groupId 和 version,子模块只需定义自己的 artifactId 即可
我们再来看父工程的 pom.xml:可以看到其多了一个 modules 标签,表明子模块。
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>LearnJavaMaven_dao</module>
</modules>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
我们按照上述的方法,创建 service 和 controller 层。注意创建 controller 层的时候,我们选择骨架 webapp。
service 模块的 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>
<parent>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>LearnJavaMaven_service</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
由于我们 controller 层用了骨架,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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>LearnJavaMaven_web</artifactId>
<packaging>war</packaging>
</project>
2
3
4
5
6
7
8
9
10
11
12
此时父工程就有 3 个 module 了:
<modules>
<module>LearnJavaMaven_dao</module>
<module>LearnJavaMaven_service</module>
<module>LearnJavaMaven_web</module>
</modules>
2
3
4
5
至此,父子工程创建完成
# 工程和模块,继承和依赖
刚刚我们创建了一个 Maven 工程,和几个 Maven 模块,那么工程和模块有什么区别呢?
工程不等于完整的项目,模块也不等于完整的项目,一个完整的项目看的是代码,代码完整,就可以说这是一个完整的项目,和此项目是工程和模块没有关系。
工程天生只能使用自己内部资源,工程天生是独立的,后天可以和其他工程或模块建立关联关系。
模块天生不是独立的,模块天生是属于父工程的,模块一旦创建,所有父工程的资源都可以使用。举个生活中的例子,工程类似一个班级,而模块就是每个班级的学生。学生可以使用班级里的公共资源(例如黑板等)
父子工程之间:子模块天生集成父工程,可以使用父工程所有资源。子模块之间天生是没有任何关系的,但子模块之间可以建立关系,例如 dao 可以给其他模块调用,只需通过引用坐标的方式来使用。
父子工程之间不用建立关系,继承关系是先天的,不需要手动建立。
平级直接的引用叫依赖,依赖不是先天的,依赖是需要后天建立的。
# 拆分模块
接下来我们将前面搭建的 Maven 的 SSM 工程,分成几个模块。
首先我们将依赖,全部导入到父工程的 pom.xml 中。
然后我们在 web 模块中引用 service:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>LearnJavaMaven_web</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven_service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
同理,service 层引用 dao 层的:
<?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>
<parent>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>LearnJavaMaven_service</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.peterjxl</groupId>
<artifactId>LearnJavaMaven_dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 依赖的传递性
假设我们现在工程里有个依赖是 Junit,其作用域 scope 配置的是 test,那么模块能使用这个依赖吗?
其实模块也是依赖于父工程的(因为要模块需要父工程定义的依赖),那么 dao 模块是否能用 Junit?
答案是不能的,这是 Maven 的规定。那么怎么样的依赖能被模块使用呢?我们可以看一张图:
首先我们的 dao 模块是直接依赖于 parent 的,其作用域是 compile:
然后 Junit 是传递依赖,我们定位到 compile 行和 test 列:
如果对应上-,就表示传递丢失了,也就是 dao 不能使用 Junit。
实际开发中,如果传递依赖丢失,表现形式就是 jar 包的坐标导不进去,我们的做法就是直接再导入一次。
除此之外,在依赖标签里面还有一个标签属性 optional:该标签默认值为 false,指的是父子项目之间的是否传递(之前在看 Mybatis 源代码的时候,下载下来后的 pom.xml 里面就会有这个标签),如果父项目引入一个依赖并且 optional 标签设置为 true 的话,那么子项目打包的时候也会打包进去,如果设置为 false 的话,那么就不会打包进去。
本小节的知识点用的较少,大家了解即可。
# 填充 SSM 的代码
接下来我们将之前搭建的 SSM-Maven 项目,填充到这次拆分了几个模块的项目中。
# dao 模块
我们首先填充下 dao 模块,我们将 ItemsDao 接口,Items 类复制进来;然后将 ItemsDao.xml 也复制进来。
然后我们新建一个 resources/spring/applicationContext-dao.xml,用来配置 dao 的 bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///learnmaven"/>
<property name="username" value="LearnMavenUser"/>
<property name="password" value="LearnMavenUser@Password"/>
</bean>
<!-- 配置生成 SqlSession 对象的工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--扫描pojo包,给包下所有pojo对象起别名-->
<property name="typeAliasesPackage" value="com.peterjxl.domain"/>
</bean>
<!-- 扫描接口包路径,并生成所有接口的代理对象,放到 Spring 容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.peterjxl.dao"/>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
此时的 dao 模块目录结构为:
├── LearnJavaMaven_dao
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── peterjxl
│ │ │ │ ├── dao
│ │ │ │ │ └── ItemsDao.java
│ │ │ │ └── domain
│ │ │ │ └── Items.java
│ │ │ └── resources
│ │ │ ├── com
│ │ │ │ └── peterjxl
│ │ │ │ └── dao
│ │ │ │ └── ItemsDao.xml
│ │ │ └── spring
│ │ │ └── applicationContext-dao.xml
│ │ └── test
│ │ └── java
│ └── pom.xml
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# service 模块
同理,复制 service 层的代码,然后新建 resources/spring/applicationContext-service.xml 文件,将 service 的 bean 配置挪过来:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描service包下所有使用注解的类型,并交给 Spring 管理 -->
<context:component-scan base-package="com.peterjxl.service"/>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务的通知 -->
<tx:advice id="active">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入(切面) -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.peterjxl.service.impl.*.*(..))"/>
<aop:advisor advice-ref="active" pointcut-ref="pointcut"/>
</aop:config>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
此时目录结构如下:
├── LearnJavaMaven_service
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── peterjxl
│ │ │ │ └── service
│ │ │ │ ├── impl
│ │ │ │ │ └── ItemsServiceImpl.java
│ │ │ │ └── ItemsService.java
│ │ │ └── resources
│ │ │ └── spring
│ │ │ └── applicationContext-service.xml
│ │ └── test
│ │ └── java
│ └── pom.xml
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# web 模块
同理,引入 controller 的代码,JSP 页面,然后是 log4j.properties,springmvc.xml 到 resources 目录下
然后是 web.xml,此时我们会发现我们没有 applicationContext.xml
;我们在 resources 目录下新建,并将两个模块的配置文件引入:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<import resource="classpath:spring/applicationContext-dao.xml"/>
<import resource="classpath:spring/applicationContext-service.xml"/>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
此时目录结构如下
├── LearnJavaMaven_web
│ ├── src
│ │ └── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── peterjxl
│ │ │ └── controller
│ │ │ └── ItemsController.java
│ │ ├── resources
│ │ │ ├── applicationContext.xml
│ │ │ ├── log4j.properties
│ │ │ └── springmvc.xml
│ │ └── webapp
│ │ ├── WEB-INF
│ │ │ ├── pages
│ │ │ │ └── itemDetail.jsp
│ │ │ └── web.xml
│ │ └── index.jsp
│ └── pom.xml
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 启动项目的 3 种方式
# 第一种模式:启动父工程
我们可以在 Maven 面板中,找到 Tomcat7 插件然后双击运行:(其实在命令行中输入 mvn tomcat7: run 也是一样的)
访问项目地址:
# 第二种方式:启动 web 模块
之前我们说过看一个项目是否完整,得看它代码是否完整,而不是看它是模块还是工程。
注意,我们先要将 service 层的模块,和 dao 层的模块都安装到本地仓库中,否则会找不到依赖。我们可以直接 install 父工程:可以在命令行里使用 mvn install 命令,或者通过可视化界面来安装
然后我们启动:
再次访问,也是可以看到数据的。
# 第三种方式:使用本地 Tomcat
我们新建一个运行配置:
然后找到 Tomcat,并改个名字,最后添加一个 war 包
我们选第一个即可:
此时的运行结果是一样的:
# 源码
本项目已将源码上传到 Gitee (opens new window) 和 GitHub (opens new window) 上。并且创建了分支 demo7,读者可以通过切换分支来查看本文的示例代码。
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02