Maven原理与实践

一.maven简介

  1. 何为maven

Maven是一个采用纯Java编写的开源项目管理工具。Maven采用了一种被称之为project object model (POM)概念来管理项目,所有的项目配置信息都被定义在一个叫做POM.xml的文件中,通过该文件,Maven可以管理项目的整个声明周期,包括编译,构建,测试,发布,报告等等。目前Apache下绝大多数项目都已经采用Maven进行管理。而Maven本身还支持多种插件,可以方便更灵活的控制项目。

Maven是面向技术层面,针对Java开发项目管理工具,它提供了构建工具所提供功能的超集,除了构建功能之外,Maven还可以管理项目结构、管理依赖关系、生成报告、生成Web站点、有助于团队成员之间的交流与协作。

1.2maven的优势

  • 指导开发:提供了Java项目的最佳开发实践,自由开发项目骨架而可自动生成项目结构。
  • 自动编译:不仅仅只像Ant自动编译,还包括测试,打包,发布,文档生成,项目站点生成……
  • 依赖管理:Maven可以方便地管理应用程序依赖,例如第三方依赖、模型依赖
  • 无限扩展:插件模式可以无限增强Maven功能,例如通过TomcatJetty插件可以自由控制其服务器。
  • 持续集成:鼓励开发者积极提交代码,更早地发现程序错误,在并行开发中稳妥推进。
  • 开发协作:更简单和谐的团队协作

1.3maven名词解释

  • Project任何您想build的事物,Maven都可以认为它们是工程。这些工程被定义为工程对象模型(POM,Poject Object Model)。一个工程可以依赖其它的工程;一个工程也可以由多个子工程构成。
  • POMPOM(pom.xml)是Maven的核心文件,它是指示Maven如何工作的元数据文件,类似于Ant中的build.xml文件。POM文件位于每个工程的根目录中。
  • GroupIdgroupId是一个工程的在全局中唯一的标识符,一般地,它就是工程名。groupId有利于使用一个完全的包名,将一个工程从其它有类似名称的工程里区别出来。
  • Artifactartifact 是工程将要产生或需要使用的文件,它可以是jar文件,源文件,二进制文件,war文件,甚至是pom文件。每个artifact都由groupId和 artifactId组合的标识符唯一识别。需要被使用(依赖)的artifact都要放在仓库(见Repository)中,否则Maven无法找到 (识别)它们。
  • Dependency为了能够build或运行,一个典型的Java工程会依赖其它的包。在Maven中,这些被依赖的包就被称为dependency。dependency一般是其它工程的artifact。
  • Plug-in:Maven是由插件组织的,它的每一个功能都是由插件提供的。插件提供goal(类似于Ant中的target),并根据在POM中找到的元数据去完成工作。主要的Maven插件要是由Java写成的,但它也支持用Beanshell或Ant脚本写成的插件。
  • Repository仓库。

 

二.maven原理简介

Maven的主要组件如下图所示:

1.2.1项目对象模型(POM)

项目对象模型(Project Object Model,POM)描述项目的各个方面。尽管对于 POM 的物理表示没有内在的限制,但 Maven 开发人员通常使用一个 XML 项目文件(project.xml)。该 XML 文件格式由位于 Maven 安装目录中的 XML 模式(maven-project.xsd)定义。

通常,pom.xml 文件由三个主要部分组成:

  • 项目管理部分包括项目的组织、开发人员名单、源代码位置和错误跟踪系统 URL 等信息。
  • 项目相关性部分包括关于项目相关性的信息。当前 Maven 实现(1.0 beta 测试版 8)仅支持 JAR 文件相关性。
  • 项目构建和报告部分包含项目构建信息(如源代码目录、单元测试用例目录)和要在构建中生成的报告。

清单 1. 主 project.xml 框架

2.1.1项目管理部分

项目管理部分(如清单 2 所示)主要包括可选项。在此部分中指定开发人员名单(带有正确的标识),当您希望获得更改日志(Change Log)报告和开发活动(Development Activity)报告时尤其要这么做。


清单 2. 项目管理部分

  • 一个Java构件的五大坐标元素:
  • groupId:组ID
  • artifactId:实际项目的ID
  • version:版本
  • package:包类型,如JAR、EAR、POM…
  • classifier:分类,如二进制包,源、文档

通过这种规则就可以定位到世界上任何一个构件

1.2.1.2项目相关性部分

我们项目中用到的jar包可以通过依赖的方式引入,构建项目的时候从Maven仓库下载即可。


清单 3. 项目相关性部分

groupId 

告诉 Maven 资源库内哪个子目录中包含相关性文件。

artifactId 

告诉 Maven 该构件的唯一标识。

version 

表示相关性的版本号。

jar 

(可选的)表示相关性的 JAR 文件。在绝大多数情况下,可以从相关性的 <artifactId> 和 <version> 构造 JAR 文件的名称。

type 

(可选的)相关性的类型;如 jar 和分发版等。缺省值是 jar。

url 

scope

(可选的)相关性项目的 URL,在相关性是在因特网上找到的第三方库时有用。

(可选的)依赖的范围,下面会进行详解

exclusions

(可选的)用来排除传递性依赖,下面会进行详解

1.依赖范围

Maven有以下几种依赖范围: 

compile: 编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。

test: 测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候才需要。 

provided: 已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。

runtime: 运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主 代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。

system: 系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围依赖时必须通过 systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此 应该谨慎使用。systemPath元素可以引用环境变量。

2. 传递性依赖

 我们可以看到此项目引入依赖junit和spring-core,我们可以在Maven仓库中查找spring-core构件,如图:

点击POM我们会看到该文件包含了一个commons-logging依赖:

那么该依赖会传递到当前项目中,这就是依赖的传递性,打开项目查看Maven dependencies:

3. 可选依赖

 有时候我们不想让依赖传递,那么可配置该依赖为可选依赖,将元素optional设置为true即可,例如:

那么依赖该项目的另一项目将不会得到此依赖的传递

4.依赖调解

第一原则:路径最短者优先,A -> B -> C ->X(1.0), A -> D -> X(2.0),此时A只会依赖X(2.0)

第二原则:第一声明者优先,当第一原则无法解决依赖冲突时,则按在pom中依赖声明的顺序决定

5. 排除依赖

  当我们引入第三方jar包的时候,难免会引入传递性依赖,有些时候这是好事,然而有些时候我们不需要其中的一些传递性依赖

比如上例中的项目,我们不想引入传递性依赖commons-logging,我们可以使用exclusions元素声明排除依 赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖。需要注意的是,声明exclusions 的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖 图中的某个依赖。换句话说,Maven解析后的依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。

 如下是一个排除依赖的例子: 

6. 依赖归类

 如果我们项目中用到很多关于Spring Framework的依赖,它们分别是org.springframework:spring-core:2.5.6, org.springframework:spring-beans:2.5.6,org.springframework:spring- context:2.5.6,它们都是来自同一项目的不同模块。因此,所有这些依赖的版本都是相同的,而且可以预见,如果将来需要升级Spring Framework,这些依赖的版本会一起升级。因此,我们应该在一个唯一的地方定义版本,并且在dependency声明引用这一版本,这一在 Spring Framework升级的时候只需要修改一处即可。

1.2.1.3项目构建部分

项目构建和报告部分(如清单 4 所示)包含用于配置某些 Maven 插件的重要构建和报告信息。例如,可以配置 Maven 在站点文档生成时包含还是排除某些报告。

清单 4. 项目构建部分

1.编译源代码: mvn compile

2. 编译测试代码:mvn test-compile   

3. 运行测试:mvn test  

4. 产生site:mvn site  

5. 打包:mvn package  

6. 在本地Repository中安装jar:mvn install

7. 清除产生的项目:mvn clean  

8. 生成eclipse项目:mvn eclipse:eclipse

9. 生成idea项目:mvn idea:idea

10. 组合使用goal命令,如只打包不测试:mvn -Dtest package  

11. 编译测试的内容:mvn test-compile

12. 只打jar包: mvn jar:jar

13. 只测试而不编译,也不测试编译:mvn test -skipping compile -skipping test-compile

      ( -skipping 的灵活运用,当然也可以用于其他组合命令)

14. 清除eclipse的一些系统设置:mvn eclipse:clean  

三.资源库

在以前使用Ant的时候,我们会建立一个lib目录在存放我们的jar包,比如项目所依赖的第三方包,每建立一个项目都要建立一个lib,不停的做copy工作,不仅是对于磁盘的浪费,而且也造成了版本管理上的麻烦。而且我们还需要通过提交到svn上来对lib进行管理,但是svn对于这种二进制文件的管理并不出色。

Maven仓库的初衷就是为了解决这个问题,是所有常用的第三方包的集中营。这样所有的Maven项目就可以从这个仓库中获取所需要的资源,Maven仓库中对jar通过Group Id, Atifact Id, version 来管理,所以Maven项目可以很方便的进行依赖管理。你不需要自己来管理这个庞大的资源仓库,当然你可以创建一个公司层面的仓库管理器,这个我在这个章节的后面会介绍。

Maven 仓库的两个概念:本地仓库和远程仓库

本地仓库是远程仓库的一个缓冲和子集,当你构建Maven项目的时候,首先会从本地仓库查找资源,如果没有,那么Maven会从远程仓库下载到你本地仓库。这样在你下次使用的时候就不需要从远程下载了。如果你所需要的jar包版本在本地仓库没有,而且也不存在于远程仓库,Maven在构建的时候会报错,这种情况可能发生在有些jar包的新版本没有在Maven仓库中及时更新。

Maven缺省的本地仓库地址为${user.home}/.m2/repository 。也就是说,一个用户会对应的拥有一个本地仓库。当然你可以通过修改${user.home}/.m2/settings.xml 配置这个地址:

如果你想让所有的用户使用统一的配置那么你可以修改Maven主目录下的setting.xml:
${M2_HOME}/conf/setting.xml

Settings.xml配置介绍

localRepository: 自定义本地库路径,默认在$user.home/.m2中

interactiveMode:

offline:是否每次编译都去查找远程中心库

pluginGroups:插件组,例如org.mortbay.jetty

proxies:通过代理访问外部库

servers:集成认证服务,例如集成Tomcat

mirrors:镜像库,可以指定内部中心库

profiles:个性配置,需要在Activation标签中激活

activeProfiles:表示激活的profile

 POM 中配置远程仓库 

下面我介绍下如何在pom.xml里面配置远程仓库,我们需要在什么时候配置远程仓库呢?当你连接中央仓库的速度比较慢时,或者你为你的公司搭建了 自己的仓库,比如Nexus仓库管理(后面我会介绍),又或者你苏需要的jar存在另外一个公共仓库,比如我们配置一个国内的镜像地址:

配置镜像 

如果你想覆盖中央仓库的默认地址,那么这里我们就会使用的镜像了,还在settings.xml里面配置:

内部中心仓库也称私有共享仓库(私服)。一般是由公司自己设立的,只为本公司内部共享使用。它既可以作为公司内部构件协作和存档,也可作为公用类库镜像缓存,减少在外部访问和下载的频率。

Nexus和Artifactory均可搭建仓库服务器。但后者支持LDAP认证,这样就可以将私有仓库的认证集成到公司已经有的LDAP认证服务器。

内部中心库又可以连接第三方库,例如Jboss中心库、Spring中心库,以随时获得最新版本的第三方构件。

四.Eclipse+tomcat+maven+Spring mvc实践

4.1基础环境(eclipse+tomcat+jdk+maven

1.安装jdk:

下载并安装jdk,(过程比较简单就略过了)。

配置环境变量:按照java 默认安装路径需要配置为:

JAVA_HOME: C:\Program Files\Java\jdk1.6.0_07 (注意JAVA_HOME 路径后面不能有分号。)

path:%JAVA_HOME%\bin;

classpath: %JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;

在控制台下输入javac 有结果就表示安装成功了。

2.安装eclipse:

此次采用的是eclipse 4.2,代号Juno。(免安装版,其他版本慎用,测试过helios版本的,照着这个文档配置会有错误)。

只要解压到指定目录即可。

3.安装tomcat

先安装插件:下载地址:http://www.eclipsetotale.com/tomcatPlugin.html#A3。我用的是tomcatPluginV33.zip。直接把插件解压放到eclipse 安装目录-plugins 目录下。

再把tomcat 文件放到c 盘根目录(自选)。

4.关联

eclipse 关联jdktomcat:

preferences-java- installed jres add- jre home 中选择jdk 安装目录。(特别注意:不是jre 文件,是jdk. 如果原来是jre 要换成jdk,如下图)

 

eclipse preferences-server-runtime environmen ,add-选择与之相关版本的tomcat(这里选择7.0)browse-选择tomcat 根目录,jre 选择安装过的jre

再在tomcat 选项选择你安装的tomcat 版本,选择tomcat 根目录,ok。浏览器中输入:http://localhost:8080/就可以检查是否成功安装了tomcat

5.安装maven:

先安装插件:在eclipse marcketplace 输入maven查找,选择maven integration for eclipse,按照向导安装,重启eclipse。在你的新建项目以及prefrences 就有maven这一项了。

再把maven 文件解压到c 盘根目录(自选)。(maven 文件请自行到网站下载,链接:http://www.apache.org/dyn/closer.cgi/maven/maven-3/3.0.4/binaries/apachemaven-3.0.4-bin.zip

最后关联操作:在preferences maven - user settings 中选择maven文件目录目录conf 文件-- settings.xmlok.

4.2:使用并配置环境

1.新建maven项目:

File-new-other-maven-maven project---直接下一步--filte 中输入webapp(这里代表建立web 项目):选择第二个,如图:

 

下一步后。如下图:

Group ID 一般写大项目名称。Artifact ID 是子项目名称。(我这里分别写IFLYTEKsms,点击finishMaven web 项目就建好了。

创建好的maven 项目:

2.构建Maven常规目录结构。

Maven 管理的项目中,文件目录一般都很固定,我们下面就建立一般的web maven

目目录结构。构建并运行一个hello 的示例demo

1)建立source folder 文件夹:

Workspace-项目名-src-main 下新建一个文件夹:java。同样的在上级目录src 目录中新建test 文件夹,在test 下新建文件夹:java。在eclipse 右击刷新,目录就出来了。(如果直接新建source folder 会出现问题)。

2)新建spring 配置文件:(mvc-dispatcher-servlet.xml

在项目树中找到src-main-webapp-WEB-INF,在这个目录中新建一个文件夹命名为:

pages,用于以后存放jsp 文件(这样会让项目看起来更清晰一点)。同时在WEB-INF

录中新建文件,命名为:mvc-dispatcher-servlet.xml

编辑mvc-dispatcher-servlet.xml文件:

<beans xmlns ="http://www.springframework.org/schema/beans"

xmlns:context ="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc"

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-3.0.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.0.xsd

http://www.springframework.org/schema/mvc

http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<context:component-scan base-package="com.sms"/>

<mvc:annotation-driven/>

<bean

class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix">

<value>/WEB-INF/pages/</value>

</property>

<property name="suffix">

<value>.jsp</value>

</property>

</bean>

</beans>

其中<context:component-scan base-package="com"/>代表编译时读取的包(在classpath下新建的包,在文档下几页中我是新建com.sms.controller所以写这个,扫描com目录下的所有文件)<mvc:annotation-driven />代表注释驱动,<value>/WEB-INF/pages/</value>代表前端控制器寻找jsp 的路径。

3)编辑web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/webapp_

2_4.xsd" id="WebApp_ID" version="2.4">

<display-name>Spring Web MVC Application</display-name>

<servlet>

<servlet-name>mvc-dispatcher</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>mvc-dispatcher</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>

</ context-param>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</ listener>

</web-app>

其中重要的配置是<context-param>中的<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>,它指明了spring 配置文件的目录。(当然也可以用classpath:xxx的方式进行配置, 多个配置文件也可以用“,”隔开)

<servlet-mapping>中的<url-pattern>/</url-pattern>,它代表前端拦截器将拦截所有文件。

这里要特别注意的是<servlet-name>mvc-dispatcher,因为要对应<param-value>/WEB-INF/mvc-dispatcher-servlet.xml。比如,如果<servlet-name>mydispatcher,那么对应的<param-value>必须为mydispatcher- servlet.xml

4)配置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/mavenv4_

0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>IFLYTEK</groupId>

<artifactId>sms</artifactId>

<packaging>war</packaging>

<version>0.0.1-SNAPSHOT</version>

<name>sms Maven Webapp</name>

<url>http://maven.apache.org</ url>

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>

</dependencies>

<build>

<finalName>sms</finalName>

<plugins>

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>tomcat-maven-plugin</artifactId>

<version>1.1</version>

</plugin>  

</plugins>

</build>

</project>

maven 通过pom.xml 管理项目依赖的jar 包,通过groupIDartifactId 以及版本号可以唯一确定一个jar 包。这样可以防止老式Web 项目中WEB-INF/lib jar 包不一致的问题。

并且maven 还会自动下载添加进的jar 包所依赖的jar 包。

 

Run asmaven install,就会去自动下载包了。

至此,项目文件结构如图:

(5)Controller :

src/main/java 下新建包:com.sms.controller,在这个包中新建controller

:HelloController。编辑如下

packagecom.sms.controller;

import org.springframework.stereotype.Controller;

import org.springframework.ui.ModelMap;

import org.springframework.web.bind.annotation.RequestMapping;

@Controller

public classHelloController {

@RequestMapping("/hello.do")

public String index_jsp(ModelMap model){

System.out.println("hello");

model.addAttribute("yan", "yan你好");

return "hello";

}

}

6)在pages 目录下新建文件hello.jsp,编辑如下:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8" %>

<%

String path = request.getContextPath();

String basePath =

request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+ "/";

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<base href="<%=basePath%>">

<title>My JSP 'Login.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">

<meta http-equiv="cache-control" content="no-cache">

<meta http-equiv="expires" content="0">

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">

<meta http-equiv="description" content="This is my page">

<!--

<link rel=" stylesheet" type="text/css" href="styles.css">

-->

</head>

<body>

${yan}</br>

</body>

</html>

下载完毕后,启动tomcat(build 之前都要先启动tomcat,不然会报build failed 错误)run as- maven build双击新建一个maven build—在Goals 中输入package tomcat:redeploy(关键)(如下图):

点击刚才配置好的,点击菜单栏上的绿色大按钮run,再稍等片刻,等待包的下载。

 

 

 

 

 

 

 

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐