转自 :http://blog.csdn.net/liupin_2011/article/details/40017837
1. 多模块拆分的必要性
使用Java技术开发的工程项目,无论是数据处理系统还是Web网站,随着项目的不断发展,需求的不断细化与添加,工程项目中的代码越来越多,包结构也越来越复杂这时候工程的进展就会遇到各种问题:
(1)不同方面的代码之间相互耦合,这时候一系统出现问题很难定位到问题的出现原因,即使定位到问题也很难修正问题,可能在修正问题的时候引入更多的问题。
(2)多方面的代码集中在一个整体结构中,新入的开发者很难对整体项目有直观的感受,增加了新手介入开发的成本,需要有一个熟悉整个项目的开发者维护整个项目的结构(通常在项目较大且开发时间较长时这是很难做到的)。
(3)开发者对自己或者他人负责的代码边界很模糊,这是复杂项目中最容易遇到的,导致的结果就是开发者很容易修改了他人负责的代码且代码负责人还不知道,责任追踪很麻烦。
将一个复杂项目拆分成多个模块是解决上述问题的一个重要方法,多模块的划分可以降低代码之间的耦合性(从类级别的耦合提升到jar包级别的耦合),每个模块都可以是自解释的(通过模块名或者模块文档),模块还规范了代码边界的划分,开发者很容易通过模块确定自己所负责的内容。
2. 使用maven构建模型进行多模块拆分
maven的多模块划分还是比较直观的,每个模块都是一个独立的maven项目,模块之间的相互引用和maven中对jar包依赖的解决是一致的,这使得物理(jar)和逻辑(dependency)上得以保持一致。我们先来看一个完整的多模块项目的组成(使用Maven The Definitive Guide上的例子):
(1)首先是整体的模块关系
引用书上对这些模块的介绍:
simple-model
该模块定义了一个简单的对象模型,对从Yashoo! Weather信息源返回的数据建模。该对象模型 包含了Weather,Condition,Atmosphere,Location,和Wind对象。当我们的应用程序解析Yahoo! Weather信息源的时候,simple-weather中定义的解析器会解析XML并创建供应用程序使用的Weather 对象。该项目还包含了使用hibernate 3标注符标注的模型对象,它们在simple-persist的逻辑中被用来 映射每个模型对象至关系数据库中对应的表。
simple-weather
该模块包含了所有用来从Yahoo! Weather数据源获取数据并解析结果XML的逻辑。从数据源返回的XML被转换成simple-model中定义的模型对象。simple-weather有一个对simple-model的依赖。simple-weather定义了一个WeatherService对象,该对象会被simple-command和simple-webapp项目引用。
simple-persist
该模块包含了一些数据访问对象(DAO),这些对象将Weather对象存储在一个内嵌数据库中。这 个多模块项目中的两个应用都会使用simple-persist中定义的DAO来将数据存储至内嵌数据库中。本项 目中定义的DAO能理解并返回simplemodel定义的模型对象。simple-persist有一个对simple-model的依 赖,它也依赖于模型对象上的Hibernate标注。
simple-webapp
这个web应用项目包含了两个spring MVC控制器实现,控制器使用了simpleweather中定义的WeatherService,以及simple-persist中定义的DAO。simplewebapp有对于simple-weather和simple-persist的直接依赖;还有一个对于simple-model的传递性依赖。
simple-command
该模块包含了一个用来查询Yahoo! Weather信息源的简单命令行工具。它包含了一个带有静态m ain()方法的类,与simple-weather中定义的WeatherService和simple-persist中定义的DAO交互。simpl e-command有对于simple-weather和simple-persist的直接依赖;还有一个对于simple-model的传递性 依赖。
(2)simple-parent项目
simple-parent是所有子模块的父类或者祖先模块,基本上父模块会被用来做为一个“抽象”模块,它不含有实际的可运行或被引用的逻辑代码,parent的pom文件中会声明一些公有的依赖还用一些构建配置例如使用的jdk版本,构建过程中的资源处理,通常在parent的pom中会声明子模块的引用,被引用的子模块在构建parent项目时也会被引用构建,子模块被构建的顺序和其在pom中的声明顺序关系不大,主要由子模块之间的依赖关系决定。maven使用一个被称为reactor(反应堆)的概念来描述这个子模块的构建顺序和依赖处理,记住循环依赖和相互引用在maven解析pom时就会被发现,这与其在parent的pom中声明的顺序是无关的。parent的pom结构如下:
其中的modules元素之间的内容声明了子模块列表,不在此列表中的子模块在parent构建过程中不会被加入reactor,也就不会被构建。还有编译器的目标配置成Java 5 JVM,构建parent的结果如下:
(3)simple-model模块
model模块是系统中最基础的模块,它建立了项目的对象模型,项目中的所有其他模块(parent除外)都依赖于model模块,在上面的构建结果中也能看出(model是第一个被构建的子模块)。在Java项目中不管是什么样的项目我们都需要将系统中的概念建模成对象模型,在Maven项目中,将这个对象模型分割成单独的项目以被广泛引用,是一种常用的实践。
(4)simple-weather模块
weather模块是一个服务模块,即为上层应用(web,桌面等)提供API接口。这个simple-weather模块包含了所有从Yahoo! Weather RSS数据源获取数据并解析的必要逻辑。虽然simple-weather只包含了三个Java类和一个JUnit测试,它还将展现为一个单独的组件,WeatherService,同时为简单web应用和简单命令行工具服务。
weather模块直接依赖于model模块,因而在reactor的构建顺序中weather在model之后
(5)simple-persist模块
persist模块提供了对数据库访问的操作,也即是我们通常所熟知的DAO
persist模块也是依赖于model模块,persist模块和weather模块在依赖链中的层次是一致的,reactor在处理处于同一依赖层次中的顺序与其在parent的pom文件中声明顺序相关。
(6)simple-webapp模块
webapp模块是系统中的最上层应用,它提供了使用http协议访问系统的入口
webapp模块依赖于persist和weather模块,因而其在reactor中的构建时间要晚于persist和weather。
(7)simple-command模块
通常一个复杂的项目不仅仅只有一个上层应用,面对不同的用户不同的环境可能需要提供更多的上层应用以应对复杂的生产需求。simple-command项目是simple-webapp的一个命令行版本。
webapp模块依赖于persist和weather模块,因而其在reactor中的构建时间要晚于persist和weather。
(8)多模块结构
在Eclipse中的结构如下在
文件系统中的结构
3. maven多模块拆分中的问题
(1)划分模块的方式
模块划分主要是根据程序的职责单一性和耦合性,如果是项目建立初期就使用多模块来规划整个项目,那么职责单一性原则应该是首要考虑的也就是通常意义上的按照层次划分(和上面的例子类似),如果是从一个已经十分复杂的项目开始拆分那么在划分模块的时候就不仅仅是考虑职责单一了,职责单一会造成大量的子模块产生导致pom文件臃肿且不容易识别,如果将耦合性考虑进去就应该将关系较为紧密的模块合并降低模块的数量提高实用性。
(2)公有依赖的抽象
parent中的依赖配置主要是一些公有的依赖,例如log,apache commons,spring等,怎么界定一个依赖是否属于公有呢?一般情况下如果这个依赖被超过2/3的子模块所依赖就可以将其认定为公有依赖,另外也可以将依赖的某些配置(例如版本号)以parent中pom的属性的形式加以声明,这样在升级某些依赖的时候就只改动一处就行了(很类似C语言中的#define)
(3)模块的存在与消亡
模块的划分并不是一成不变的,模块的存在就是为了方便维护和提高生产效率,如果某些模块不合理并且影响了开发效率,那么这些模块就需要再好好斟酌一下。一般情况下这种情况出现不是因为模块过于庞大就是因为模块的碎片化,对于前者需要拆分更多模块以提高复用和去除冗余,后者则需要酌情合并一些耦合性较高的模块。
(4)子模块的子模块
这种情况的出现往往就意味着这个项目本身就应该被分成多个项目,多个项目之间也可以继承同一个parent的pom.xml这主要是为了方便统一构建多个项目。所以子模块的子模块应该避免出现。