一个对 Maven 比较正式的定义是这么说的: Maven 是一个项目管理工具,它包含了一个项目对象模型 (POM: Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。
maven 能解决什么问题?
试想,如果现在有一种工具,可以把你从上面的繁琐工作中解放出来,能帮你构建工程,管理 jar 包,编译代码,还能帮你自动运行单元测试,打包,生成报表,甚至能帮你部署项目,生成 Web 站点,你会心动吗? Maven 就可以解决上面所提到的这些问题。
前面我们通过 Web 阶段项目,要能够将项目运行起来,就必须将该项目所依赖的一些 jar 包添加到工程中,否则项目就不能运行。 试想如果具有相同架构的项目有十个,那么我们就需要将这一份 jar 包复制到十个不同的工程中。 我们一起来看一个 CRM 项目的工程大小。
使用传统 Web 项目构建的 CRM 项目如下:
原因主要是因为上面的 WEB 程序要运行,我们必须将项目运行所需的 Jar 包复制到工程目录中,从而导致了工程很大。
同样的项目,如果我们使用 Maven 工程来构建,会发现总体上工程的大小会少很多。如下图:
小结: 可以初步推断它里面一定没有 jar 包,继续思考,没有 jar 包的项目怎么可能运行呢?
Maven 的一个核心特性就是依赖管理。当我们涉及到多模块的项目(包含成百个模块或者子项目),管理依赖就变成一项困难的任务。 Maven 展示出了它对处理这种情形的高度控制。
传统的 WEB 项目中,我们必须将工程所依赖的 jar 包复制到工程中,导致了工程的变得很大。那么 maven 工程是如何使得工程变得很少呢?分析如下:
通过分析发现:maven 工程中不直接将 jar 包导入到工程中,而是通过在 pom.xml
文件中添加所需 jar 包的坐标,这样就很好的避免了 jar 直接引入进来,在需要用到 jar 包的时候,只要查找 pom.xml
文件,再通过 pom.xml
文件中的坐标,到一个专门用于 ”存放 jar 包的仓库”(maven 仓库)中根据坐标从而找到这些 jar 包,再把这些 jar 包拿去运行。
那么问题来了:
第一:”存放 jar 包的仓库” 长什么样?
第二:通过读取 pom.xml
文件中的坐标,再到仓库中找到 jar 包,会不会很慢?从而导致这种方式不可行!
第一个问题:存放 jar 包的仓库长什么样,这一点我们后期会分析仓库的分类,也会带大家去看我们的本地的仓库长什么样。
第二个问题:通过 pom.xml
文件配置要引入的 jar 包的坐标,再读取坐标并到仓库中加载 jar 包,这样我们就可以直接使用 jar 包了,为了解决这个过程中速度慢的问题, maven 中也有索引的概念,通过建立索引,可以大大提高加载 jar 包的速度,使得我们认为 jar 包基本跟放在本地的工程文件中再读取出来的速度是一样的。这个过程就好比我们查阅字典时,为了能够加快查找到内容,书前面的目录就好比是索引,有了这个目录我们就可以方便找到内容了,一样的在 maven 仓库中有了索引我们就可以认为可以快速找到 jar 包。
我们的项目,往往都要经历编译、 测试、 运行、 打包、 安装 ,部署等一系列过程。
什么是构建?指的是项目从编译、测试、运行、打包、安装 ,部署整个过程都交给 maven 进行管理,这个过程称为构建。
一键构建:指的是整个构建过程,使用 maven 一个命令可以轻松完成整个工作。
Maven 规范化构建流程如下:
Apache-maven-3.5.2 下载地址: http://archive.apache.org/dist/maven/maven-3/
Maven 下载后,将 Maven 解压到一个没有中文没有空格的路径下,比如 D:\software\maven
下面。
maven 的工作需要从仓库下载一些 jar 包,如下图所示,本地的项目 A、项目 B 等都会通过 maven 软件从远程仓库(可以理解为互联网上的仓库)下载 jar 包并存在本地仓库,本地仓库就是本地文件夹,当第二次需要此 jar 包时则不再从远程仓库下载,因为本地仓库已经存在了,可以将本地仓库理解为缓存,有了本地仓库就不用每次从远程仓库下载了。
下图描述了 maven 中仓库的类型:
本地仓库:用来存储从远程仓库或中央仓库下载的插件和 jar 包,项目使用一些插件或 jar 包,优先从本地仓库查找
默认本地仓库位置在 ${user.dir}/.m2/repository
, ${user.dir}
表示 windows 用户目录。
自定义本地仓库的位置:
在 MAVE_HOME/conf/settings.xml
文件中配置本地仓库位置(maven 的安装目录下):
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>G:\maven_repositorylocalRepository>
远程仓库:如果本地需要插件或者 jar 包,本地仓库没有, 默认去远程仓库下载。远程仓库可以在互联网内也可以在局域网内。
中央仓库:在 maven 软件中内置一个远程仓库地址 https://repo1.maven.org/maven2 ,它是中央仓库,服务于整个互联网,它是由 Maven 团队自己维护,里面存储了非常全的 jar 包,它包含了世界上大部分流行的开源项目构件。
maven 仓库地址、私服等配置信息需要在 setting.xml
文件中配置,分为全局配置和用户配置。
在 maven 安装目录下的有 conf/setting.xml
文件,此 setting.xml
文件用于 maven 的所有 project 项目,它作为 maven 的全局配置。
如需要个性配置则需要在用户配置中设置,用户配置的 setting.xml
文件默认的位置在: ${user.dir} /.m2/settings.xml
目录中,${user.dir}
指 windows 中的用户目录。
maven 会先找用户配置,如果找到则以用户配置文件为准,否则使用全局配置文件。
作为一个 maven 工程,它的 src
目录和 pom.xml
是必备的。
进入 src 目录后,我们发现它里面的目录结构如下:
注意:如果是普通的 java 项目,那么就没有 webapp 目录。
我们可以在 cmd 中通过一系列的 maven 命令来对我们的 maven-helloworld 工程进行编译、测试、运行、打包、安装、部署。
clean
是 maven 工程的清理命令,执行 clean
会删除 target 目录及内容。
compile
是 maven 工程的编译命令,作用是将 src/main/java
下的文件编译为 class 文件输出到 target
目录下。
test
是 maven 工程的测试命令 mvn test
,会执行 src/test/java
下的单元测试类。
不仅仅会编译 src/test/java
下的代码,同样会编译 src/main/java
下的代码。
package
是 maven 工程的打包命令,对于 java 工程执行 package
打成 jar 包,对于 web 工程打成 war 包(在 target 目录下)。
install
是 maven 工程的安装命令,执行 install
将 maven 打成 jar 包或 war 包发布到本地仓库。
从运行结果中,可以看出:
当后面的命令执行时,前面的操作过程也都会自动执行。
maven 对项目构建过程分为三套相互独立的生命周期,请注意这里说的是 “三套”,而且 “相互独立”,这三套生命周期分别是:
Maven 包含了一个项目对象模型(Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。
项目对象模型(Project Object Model)
一个 maven 工程都有一个 pom.xml
文件,通过 pom.xml
文件定义项目的坐标、项目依赖、项目信息、插件目标等。
依赖管理系统(Dependency Management System)
通过 maven 的依赖管理对项目所依赖的 jar 包进行统一管理。
比如:项目依赖 junit4.9,通过在 pom.xml
中定义 junit4.9 的依赖即使用 junit4.9,如下所示是 junit4.9 的依赖定义:
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.9version>
<scope>testscope>
dependency>
dependencies>
一个项目生命周期(Project Lifecycle)
使用 maven 完成项目的构建,项目构建包括:清理、编译、测试、部署等过程, maven 将这些过程规范为一个生命周期。
maven 通过执行一些简单命令即可实现上边生命周期的各个过程,比如执行 mvn compile
执行编译、执行 mvn clean
执行清理。
一组标准集合
maven 将整个项目管理过程定义一组标准,比如:通过 maven 构建工程有标准的目录结构,有标准的生命周期阶段、依赖管理有标准的坐标定义等。
插件(plugin)目标(goal)
maven 管理项目生命周期过程都是基于插件完成的。
点击下一步:
创建完之后的 java 工程:
目录并不完整,需要我们手动添加资源目录:
创建 java 工程后的目录结构:
点击下一步:
创建 web 工程后的目录结构:
目录结构仍然不完整,需要我们手动添加:
在 pom.xml 中导入项目运行所需要的 jar 包:
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
dependency>
新建一个 Servlet 取名为 MyServlet:
添加依赖需要指定依赖 jar 包的坐标,但是很多情况我们是不知道 jar 包的的坐标,可以通过如下方式查询:
https://mvnrepository.com/
MySevlet 代码为:
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("hello.jsp").forward(request, response);
}
}
A 依赖 B,需要在 A 的 pom.xml
文件中添加 B 的坐标,添加坐标时需要指定依赖范围,依赖范围包括:
在 maven-web 工程中测试各各 scop。测试总结:
依赖范围由强到弱的顺序是: compile > provided > runtime > test
只在运行时起作用:
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
<scope>providedscope>
dependency>
只在测试时起作用:
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat9-maven-pluginartifactId>
<version>2.2version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
我们作为示例,取 192.168.1.1 数据库服务器 db4 数据库 user 表中的 id 和 username 字段。
我们需要数据库连接的 jar 包和单元测试的 jar 包。
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.15version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
配置完 pom.xml 一定要记得点击 “刷新”。
package com.li;
public class Items {
private Integer id;
private String username;
public Integer getId() {
return id;
}
public String getUsername() {
return username;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "Items{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
package com.Dao;
import com.li.Items;
import java.util.List;
public interface ItemsDao {
public List<Items> findAll();
}
接着创建一个实现类:
package com.Dao.impl;
import com.Dao.ItemsDao;
import com.li.Items;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class ItemsDaoImpl implements ItemsDao {
@Override
public List<Items> findAll() throws SQLException {
//1. 先获取connection对象
Connection connection = null;
//2. 获取真正操作数据的对象
PreparedStatement preparedStatement = null;
//3. 执行数据库操作
ResultSet resultSet = null;
List<Items> list = new ArrayList<>();
try{
//加载驱动类
Class.forName("com.mysql.jdbc.Driver");
//1. 先获取connection对象
connection = DriverManager.getConnection("jdbc:mysql://192.168.1.11/db4", "root", "Opfordream@0518");
//2. 获取真正操作数据的对象
preparedStatement = connection.prepareStatement("select id, username from user");
//3. 执行数据库操作
resultSet = preparedStatement.executeQuery();
//4. 把数据库结果集转成java的List集合
while (resultSet.next()){
Items items = new Items();
items.setId(resultSet.getInt("id"));
items.setUsername(resultSet.getString("username"));
list.add(items);
}
}catch (Exception e){
e.printStackTrace();
}finally {
connection.close();
preparedStatement.close();
resultSet.close();
}
return list;
}
}
package com.li.test;
import com.Dao.ItemsDao;
import com.Dao.impl.ItemsDaoImpl;
import com.li.Items;
import org.junit.Test;
import java.sql.SQLException;
import java.util.List;
public class ItemsTest {
@Test
public void findAll() throws SQLException {
ItemsDao itemsDao = new ItemsDaoImpl();
List<Items> itemsList = itemsDao.findAll();
for (Items items:
itemsList) {
System.out.println(items);
}
}
}
Items{id=1, username='zhangsan'}
Items{id=2, username='lisi'}