Apache Maven
Apache Maven 项目管理工具
Apache Maven,是一个软件(特别是Java软件)项目管理及自动构建工具,由Apache软件基金会所提供。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。
Maven也可被用于构建和管理各种项目,例如C#,Ruby,Scala和其他语言编写的项目。Maven曾是Jakarta项目的子项目,现为由Apache软件基金会主持的独立Apache项目。
Maven这个单词来自于意第绪语(犹太语),意为知识的积累,最初在Jakata Turbine项目中用来简化构建过程。当时有一些项目(有各自Ant build文件),仅有细微的差别,而JAR文件都由CVS来维护。于是希望有一种标准化的方式构建项目,一个清晰的方式定义项目的组成,一个容易的方式发布项目的信息,以及一种简单的方式在多个项目中共享JARs。
Maven入门
Maven是Apache下的一个纯java开发的开源项目,它是一个项目管理工具,使用Maven对Java项目进行构建、依赖管理。
项目构建
什么是项目构建
项目构建是一个项目从编写源代码到编译、测试、运行、打包、部署到运行的一系列过程
传统的项目构建过程
传统项目构建过程如下
- 在Eclipse中创建一个Java Web工程
- 在工程中编写源代码及配置文件等
- 对源代码进行编译(.java --> .class)
- 进行JUnit单元测试
- 将工程打包成war包部署到Tomcat运行
Maven项目构建过程
Maven将项目构建的过程进行标准化,每个阶段使用一个命令来完成,如下图
上图中部分阶段对应命令如下
- clean:清理阶段的命令,清理输出的class文件
- compile:编译阶段的命令,将Java代码编译成class文件
- package:打包阶段的命令,将Java工程打成jar包,Java Web工程打成war包
运行一个Maven工程(Web工程)需要使用一个 tomcat:run 命令
Maven工程构建的优点
一个命令完成构建、运行,方便快捷
Maven对每个构建阶段进行规范,有利于大型团队协作开发
依赖管理
什么是依赖管理
什么是依赖?一个java项目可能要使用一些第三方的jar包才可以运行,那么我们说这个java项目依赖了这些第三方的jar包。
举个例子,crm系统,它的架构是SsH框架,该crm项目依赖SSH框架,具体它依赖的Hibernate、Spring、Struts2。
什么是依赖管理?就是对项目所有依赖的jar包进行规范化管理。
简言之,依赖管理就是jar包的管理
传统项目的依赖管理
传统的项目工程要管理所依赖的jar包完全靠人工手动进行,程序员从网上下载jar包添加到项目工程中,如下图:程序员手工将 Hibernate、Struts2、Spring的jar添加到工程中的WEB-INF/lib目录下。
传统的依赖管理方式的缺陷
- 没有对jar包的版本统一管理,容易导致版本冲突
- 从网上手动寻找jar包不方便,效率低下
- jar包被添加到工程中导致工程变得庞大
Maven项目的依赖管理
Maven项目管理所依赖的jar包不需要手动向工程添加jar包,只需要在pom.xml文件(maven工程的配置文件)中添加jar包的坐标,自动从Maven仓库中下载jar包、运行,如下图:
使用Maven进行依赖管理的优点
- 通过pom.xml文件对jar包的版本进行统一管理,可以避免版本冲突
- Maven团队维护了一个非常全的Maven仓库,包括了当前使用的jar包,Maven工程可以通过坐标自动从Maven仓库中下载jar包,方便快捷
- Maven项目中没有jar包文件实体,体积相对小很多
使用Maven的优势
通过上边介绍传统项目和Maven项目在项目构建及依赖管理方面的区域,Maven有如下的好处
- 一步构建(一键构建):Maven对项目构建的过程进行标准化,通过一个命令即可完成构建过程
- 有项目源码和Maven直接可以通过tomcat:rum命令构建这个Web项目,毋需提供其他jar/Tomcat环境等
- 依赖管理:Maven工程不用手动导jar包,通过在pom.xml中定义坐标从Maven仓库自动下载,方便且不易出错,节省空间
- Maven可跨平台,可在Window、Linux上使用
- Maven遵循规范开发有利于提高大型团队的开发效率,降低项目的维护成本,大公司都会考虑使用Maven来构建项目
Maven的安装
略,见附件pdf
Maven仓库
Maven工作时需要从仓库下载相应的jar包,如下图,本地项目A和项目B第一次通过Maven运行时都会从远程仓库(互联网上的长裤)下载jar包并保存在本地仓库(即本地文件夹),当第二次需要这些jar包时不再从远程仓库下载,而是从本地仓库中获取,而远程仓库中没有的jar包将从中央仓库获取,并下载到远程仓库中,类似多级缓存。
Maven仓库类型
- 本地仓库:由自己维护
- 远程仓库:由公司维护
- 中央仓库:由Maven团队维护
本地仓库
用于存储从远程仓库或中央仓库下载的插件和jar包,项目使用到的jar包和插件会优先从本地仓库中查找
默认本地仓库位置位于${user.dir}/.m2/repository下,${user.dir}表示Windows用户目录
配置本地仓库
在MAVEN_HOME/conf/settings.xml文件中配置本地仓库的位置
本文是D:/apache-maven-3.5.4/conf/settings.xml
远程仓库
如果本地需要的插件或者jar包,本地仓库没有,则默认到远程仓库中下载,远程仓库可以在互联网中也可以在局域网中
一般公司自己会搭建一个远程仓库作为私服
中央仓库
在Maven软件中内置一个远程仓库地址http://repo1.maven.org/maven2,即中央仓库,服务整个互联网,由Maven团队自己维护,里面存储了非常全的jar包,包含世界上大部分流行的开源项目构件
Maven的基本使用
Maven项目的目录结构
Maven的常用命令
执行下列命令时命令行需要进入到项目目录中
- clean:清理编译后的文件
- compile:编译主目录的源代码文件
- test:编译并运行test目录的代码
- package:打包这个项目
- 隐含了编译操作
- 打包后项目包的位置和名称由配置文件pom.xml决定,例如版本名称
- install:将项目打包并发布到本地仓库
- 同上,配置文件可以配置输出属性(位置,名称等)
- tomcat:run:一键搭建Tomcat服务器并运行这个项目
- 注意:Tomcat版本需要与Maven使用的jdk版本(环境变量配置的JAVA_HOME)兼容,否则启动后不能访问
*Maven命令的生命周期
Maven对项目构建过程分为三套相互独立的生命周期
- clean生命周期
- clean命令
- default生命周期
- compile命令
- test命令
- package命令
- install命令
- site生命周期
- site命令
生命周期的阶段
每个Maven命令对应生命周期的某个阶段,例如:mvn clean命令对应clean生命周期的clean阶段,mvn test命令对应default生命周期的test阶段。
执行命令会将该命令再生命周期当中之前的阶段自动执行,如执行mvn clean命令会自动执行pre-clean和clean两个阶段,mvn test命令会执行validate、compile、test等阶段。
注意:执行某个生命周期的某个阶段不会影响其他生命周期
如果要同时执行多个生命周期的阶段可在命令行输入多个命令,中间以空格隔开,如:clean package,将执行clean生命周期的clean阶段和default生命周期的package阶段。
*使用IntelliJ IDEA创建基本的Maven项目
如何添加jar包
手动添加
修改pom.xml,添加对应的依赖
如:添加servlet-api,jsp-api,junit等
<?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.zella</groupId> <artifactId>mavenDemo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> </dependencies> </project>
点选添加
在Windows环境在pom.xml使用
在pom.xml中的dependencies标签内使用快捷键alt+g呼出Generate菜单,点选dependency即可呼出搜索框直接一键导入依赖
使用Maven自带Tomcat
注意pom.xml必须配置packaging属性为war
然后可以通过手动输入命令的方式运行
或配置运行方式,以后可以点选运行
整合Struts2
在pom.xml中只要编写Struts2核心依赖,Maven会自动将框架所依赖的其他jar包(即org.apache.struts.xwork依赖的jar包)导入到项目中
只添加了struts2-core依赖,发现项目中出现更多的jar包,这种情况称为依赖传递
整合过程
- 编辑pom.xml文件,添加Struts2的核心jar包
- 注意要修改jar的scope(依赖范围/作用域),与Tomcat冲突的jar需要改为provided
- 编写一个简单的Action
- 在src/resource目录下创建并编写struts.xml配置文件
- 编辑web.xml配置核心过滤器
- 访问网页测试是否整合成功
Maven的依赖范围
- compile(默认):编译时、测试时、运行时和打包时均依赖该jar包(如struts-core)
- provided:编译时和测试时依赖,运行时和打包时不依赖(如上文的servlet-api和jsp-api)
- runtime:编译时不依赖,测试时、运行时和打包时依赖(如数据库驱动包(一般数据库驱动包由反射方式引入,不会报错))
- test:编译时不依赖,测试时依赖,运行时和打包时都不依赖(如上文的junit)
- system:不推荐使用
-
打包时依赖的jar包将会被打包进jar文件内部,不依赖则不打包该jar包,这就是产生冲突的原因
另外,依赖范围决定了在哪个目录的文件可以使用这个jar包,如只有在test目录下才可以使用junit这个jar
依赖范围对依赖传递的影响
Maven插件的添加
默认的普通的tomcat:run命令运行时使用的Tomcat版本是Tomcat6,默认端口是8080,可以添加插件使其使用Tomcat7并修改其默认端口
添加方法
编辑pom.xml,添加build->plugins->plugin标签
其中version不能自动补全,可以使用导入dependency的方法搜索该插件查看其对应的version版本号手动填写
配置好后出现新的可执行命令,使用tomcat7:run可以使用上面配置的端口和访问路径用Tomcat7部署这个项目
访问
在Maven窗口右键这个命令可以使用debug模式执行这个命令
整合SSH
整合Spring
在上文整合Struts2的基础上,添加struts2-spring-plugin:2.3.24插件jar包,依赖传递如下
再添加spring-context4.2.4,如下
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.zella</groupId> <artifactId>mavenDemo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies> </project>
此时引入的jar大纲如下,可以发现引入了低版本(3.0.5)的spring-beans包
此时就发生了版本冲突问题
处理版本冲突有多种方法,见下文,处理妥当后继续下面的操作
导入其余jar包
其实pom文件网络上有很多现成的,稍作修改就可以作为己用,如下
pom文件
<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>cn.itcast.maven-crm</groupId> <artifactId>maven-crm</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>maven-crm</name> <description>maven-crm项目描述</description> <!-- 属性 --> <properties> <spring.version>4.2.4.RELEASE</spring.version> <hibernate.version>5.0.7.Final</hibernate.version> <struts.version>2.3.24</struts.version> </properties> <!-- 锁定版本,struts2-2.3.24、spring4.2.4、hibernate5.0.7 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>${struts.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>${struts.version}</version> </dependency> </dependencies> </dependencyManagement> <!-- 依赖管理 --> <dependencies> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <!-- hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <!-- c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- 导入 struts2 --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> </dependency> <!-- servlet jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.2</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <plugins> <!-- 设置编译版本为1.7 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- maven内置 的tomcat6插件 --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <!-- 可以灵活配置工程路径 --> <path>/ssh</path> <!-- 可以灵活配置端口号 --> <port>8080</port> </configuration> </plugin> </plugins> </build> </project>
然后可以开始配置dao层的环境了
在mysql创建一个新的数据库,名称为maven,新建一个表用于存储数据
Hibernate
- 创建实体类(bean)用于接收数据条目
- 引入Hibernate主配置文件hibernate.cfg.xml
- 引入hbm映射文件
- 注意,hbm文件等配置文件应该放置到resource文件下,且目录结构应该与main目录下Customer实体的目录结构相同,如下
- 最终都会被打包到同一个目录中
导入Hibernate之后再导入Spring的配置文件(applicationContext文件)
*解决版本冲突
有多种方法解决版本冲突
根据Maven处理冲突的原则解决
Maven在处理版本冲突的时候遵循以下原则
- 第一声明者优先:在pom文件中定义依赖,采用先声明的依赖
- 运用这个原则:上文可以在pom文件中先声明spring-context以导入4.2.4版本的spring-beans,如下图
- 路径近者优先:简单说就是在pom文件中声明需要的版本,直接引入这个版本的依赖,不通过依赖传递来引入jar包
排除依赖
上述问题也可以通过排除依赖的方法来辅助依赖调解,在pom文件声明exclusions标签定义需要排除的依赖
锁定版本
面对众多的依赖,有一种方法不需要考虑依赖路径、声明优化等因素,可以使用直接锁定版本的方法确定依赖构件的版本,版本锁定后则不考虑依赖的声明顺序或路径,以锁定的版本为准添加到项目中,企业开发常用。
这种方法类似添加依赖,但是两者不同,锁定版本不代表添加依赖
- 版本锁定声明在添加依赖前,相当于先告知Maven需要的版本,Maven根据锁定声明来引入对应版本的jar
- 添加依赖是在dependencies标签内声明,而锁定版本是在dependencyManagement标签中声明,如下
pom文件如下:锁定了4.2.4的spring-beans版本,若依赖传递涉及spring-beans这个jar包,将不受依赖声明顺序的影响,直接引入该版本jar
<?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.zella</groupId> <artifactId>mavenDemo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies> </project>
*pom文件中的properties标签
可以在pom文件中的properties标签定义引用,以使用${xxx}的格式来引用这些属性
Maven的分模块开发
概述
我们的项目分为Web层、Service层、Dao层和Entity层
- 创建父工程
- 父工程的作用
- 统筹调度作用
- 配置主要依赖
- 创建首个子模块
- 基本
- 子模块不需要再配置父类已配置的依赖,可以直接继承父类配置好的依赖
- 子模块的配置文件有时候需要根据模块名称来命名(如applicationContext-dao.xml),以区分各模块之间的配置文件
- 一般来说Web层模块打包方式为war包,其他为jar包
- 创建其余子模块
分模块开发最终得到的是Web层模块打包成的war包,内含其他模块的jar包,Tomcat运行的是这个war包
分模块开发中依赖范围对依赖传递的影响
*使用Intellij IDEA创建多模块的Maven工程
可以参考
Maven的概念模型
Maven包含了一个项目对象模型(Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。
下图为Maven的概念模型图
- 项目对象模型(Project Object Model)
- 一个Maven工程都有一个pom.xml文件,通过pom.xml文件定义项目的坐标、项目依赖、项目信息、插件目标等
- 依赖管理系统(Dependency Management System)
- 一个项目的生命周期(Project Lifecycle)
- 使用Maven完成项目的构建,项目构建包括:清理、编译、测试、部署等过程,Maven将这些过程规范为一个生命周期,如下图
- Maven通过执行一些简单命令即可实现上边生命周期的各个过程,如:mvn compile命令执行编译
- 一组标准集合
- Maven将整个项目管理过程定义一组标准,比如:通过Maven构建工程有标准的目录结构,有标准的生命周期阶段、依赖管理和标准的坐标定义
- 插件(plugin)目标(goal)
- Maven管理项目生命周期过程是基于插件完成的
私服-Nexus
详见附件实战篇
(建议复习)
JUnit测试
*IDEA快速生成测试类的方法
附件列表