【题目】使用ivy构建jar包的依赖关系
【提要】
1 要解决的问题
2 关于网络上的repository介绍
3 Maven和Ivy的工作原理和比较
4 ivy运行环节的配置
5 配置中的细节问题
【内容】
1 要解决的问题
使用java架构的开源框架最为头痛的事情就是要面对一大堆相互之间存在依赖关系的jar包。有以下几个原因造成了它们之间关系的复杂性:
首先,jar包之间的依赖关系是有传递性的,即A.jar依赖B.jar而B.jar又依赖C.ja,以此类推……,这样要使用A.jar就必须找到所有与之有直接或间接的依赖关系的jar包;
其次,是依赖关系与jar包的不同版本密切相关,例如hibernate-3.2.6.ga.jar依赖asm-1.5.3.jar,而spring-2.0.6.jar依赖asm-2.2.3.jar,必须解决两个不同版本的asm之间的版本冲突;
其三是一个开源框架往往是一组jar包的组合,而框架间的依赖关系分为许多不同情况下的依赖,如:编译时依赖、运行时依赖、测试依赖等,不同状态下需要取用不同的jar包文件;
其四是jar包的下载、升级和管理,一个框架中jar包间达成的依赖关系可能是非常复杂的!管理这些jar包不胜其烦,一个jar包的升级可能导致必须升级更替一系列的jar包文件。
2 关于网络上的repository介绍
maven2出现部分地解决了上述问题,它的思想就是自动解决jar的依赖关系问题,它和IVY一样是一个关于项目构建和测试的管理工具,理解这类工具的工作原理,首先要了解网上开源社区里面的所谓repository(仓库)系统,它和maven是配套使用的,粗略看起来,可以理解为一个在远程服务器上的一个大型的、包括了各种各样的jar文件的下载服务器,但是它绝不像下载服务器那样简单,我们可以实际看看它的结构:
2.1 可供下载jar包的地址(即:maven.repo.remote)的几个镜像站点
http://www.apache.org/dist/java-repository/
http://apache.binarycompass.org/java-repository/
http://www.bluesunrise.com/maven/
http://fisheye5.cenqua.com/browse/glassfish/repo/
http://dist.codehaus.org/
http://source.concord.org/repository/
http://www.ganet.org/maven/
http://mirrors.ibiblio.org/pub/mirrors/maven/
http://www.ibiblio.org/maven/
http://apache.linuxforum.net/dist/java-repository/
http://repo1.maven.org/maven/
http://repo1.maven.org/maven/maven/
http://public.planetmirror.com/pub/maven/
http://mirrors.sunsite.dk/maven/
http://dist.sourcelabs.com/sash/repo/
http://dist.sourcelabs.com/sash/repo/javax/activation/activation/1.0.2/
和普通的下载服务器不同的是:它们除了提供一个jar文件的下载地址之外,还使用了规定格式的目录结构存放这些jar,并且提供了一个xml用以描述jar包之间的依赖关系。
2.2 目录格式为:repository的根/组织或公司名/模块名/版本号/jars/模块名-版本号.jar,并且对每一个jar提供了一个扩展名为pom的xml文件,用以描述该jar文件所依赖的其他jar文件及其版本。
这样就提供了一种可能,让我们可以用非手工的方式,自动化地、从根本上解决jar包的依赖问题。
3 Maven和Ivy的工作原理和比较
让我们想象一下Maven和Ivy的一个理想化的工作流程,享受一下开发自动化给我们带来的乐趣:
首先,在我们构建的项目中先编写一个执行脚本(ant脚本),解决jar包依赖性问题,作为项目构建的第一步放在执行脚本第一个要执行的任务中(ivy任务)。
其次,使用一个xml文件(ivy.xml),用以描述本项目要依赖的jar包(当然是直接依赖)
然后,使用另一个xml文件(ivysetting),用以告知ivy需要从什么地方下载jar包,及描述其依赖关系的描述文件的url地址;发生版本冲突时的解决方案等信息。
具体实例如下:
3.1 【ant脚本文件】
<target name="ivy">
<echo message="构建类库……" />
<taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpath="${ant.home}/lib/ivy-2.0.0-rc2.jar" />
<delete includeEmptyDirs="true" failonerror="false" dir="${docs.dir}/ivy" />
<mkdir dir="${docs.dir}/ivy" />
<delete includeEmptyDirs="true" failonerror="false" dir="${libaray.dir}" />
<mkdir dir="${libaray.dir}" />
<property name="ivy.lib.dir" value="${libaray.dir}" />
<ivy:settings id="settings" file="${repository.dir}/ivysettings.xml" />
<ivy:retrieve pattern="${ivy.lib.dir}/[artifact]-[revision].[ext]" settingsref="settings" />
<ivy:report todir="${docs.dir}/ivy" dot="true" settingsRef="settings" />
<apply executable="dot" dest="${docs.dir}/ivy" parallel="false" output="${docs.dir}/ivy/output.log" error="${docs.dir}/ivy/error.log">
<arg value="-Tpng" />
<arg value="-o" />
<targetfile />
<srcfile />
<fileset dir="${docs.dir}/ivy" includes="*.dot" />
<mapper type="glob" from="*.dot" to="*.png" />
</apply>
</target>
3.2 【ivy.xml文件】描述项目的直接依赖
<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="./config/ivy/ivy-doc.xsl"?>
<ivy-module version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="com.cn" module="project_090104" />
<dependencies>
<dependency org="bsh" name="bsh" rev="1.3.0" conf="default" />
<dependency org="commons-dbcp" name="commons-dbcp" rev="1.2.2" conf="default" />
<dependency org="commons-fileupload" name="commons-fileupload" rev="1.2.1" conf="default" />
<dependency org="commons-pool" name="commons-pool" rev="1.3" conf="default" />
<dependency org="commons-io" name="commons-io" rev="1.4" conf="default" />
<dependency org="commons-lang" name="commons-lang" rev="2.4" conf="default" />
<dependency org="commons-validator" name="commons-validator" rev="1.3.1" conf="default" />
<dependency org="commons-collections" name="commons-collections" rev="3.2" conf="default" />
<dependency org="com.bba96" name="bba96-core" rev="2.3.0" conf="default" />
<dependency org="com.enterprisedt" name="edtFTPj" rev="1.5.3" conf="default" />
<dependency org="javax.mail" name="mail" rev="1.4.1" conf="default" />
<dependency org="javax.resource" name="connector" rev="1.0" conf="default" />
<dependency org="javax.transaction" name="jta" rev="1.1" conf="default" />
<dependency org="jgroups" name="jgroups-all" rev="2.4.1" conf="default" />
<dependency org="jotm" name="jotm" rev="2.0.10" conf="default" />
<dependency org="junit" name="junit" rev="4.5" conf="default" />
<dependency org="mysql" name="mysql-connector-java" rev="5.1.6" conf="default" />
<dependency org="opensymphony" name="oscache" rev="2.4" conf="default" />
<dependency org="org.apache.activemq" name="activemq-all" rev="5.2.0" conf="default" />
<dependency org="org.apache.cxf" name="cxf-bundle-minimal" rev="2.1.3" conf="default" />
<dependency org="org.apache.xbean" name="xbean-spring" rev="3.4" conf="default" />
<dependency org="org.directwebremoting" name="dwr" rev="2.0.5" conf="default" />
<dependency org="org.hibernate" name="hibernate" rev="3.2.6.ga" conf="default" />
<dependency org="org.hibernate" name="hibernate-annotations" rev="3.4.0.GA" conf="default" />
<dependency org="org.hibernate" name="hibernate-entitymanager" rev="3.3.2.GA" conf="default" />
<dependency org="org.springframework" name="spring" rev="2.5.6" conf="default" />
<dependency org="org.apache.struts" name="struts2-core" rev="2.0.14" conf="default" />
<dependency org="org.compass-project" name="compass" rev="2.1.0" conf="default" />
<dependency org="org.springframework.security" name="spring-security-core" rev="2.0.4" conf="default" />
<dependency org="org.springframework" name="spring-test" rev="2.5.6" conf="default" />
<dependency org="org.slf4j" name="slf4j-api" rev="1.5.6" conf="default" />
<dependency org="org.slf4j" name="slf4j-jdk14" rev="1.5.6" conf="default" />
<dependency org="org.slf4j" name="slf4j-jcl" rev="1.5.6" conf="default" />
<dependency org="org.slf4j" name="log4j-over-slf4j" rev="1.5.6" conf="default" />
<!--注意:以下指定解决asm.jar包的版本冲突的方法:保留2.2.3和1.5.3两个版本的文件-->
<conflict org="asm" rev="1.5.3,2.2.3" />
</dependencies>
</ivy-module>
3.3 【ivysetting.xml文件】描述ivy的配置,包括:版本冲突管理,repository地址列表等
<ivysettings>
<!--注意:defaultConflictManager是默认的版本冲突解决方案,即只保留最新版本,替换老版本-->
<settings defaultResolver="libs.local" defaultConflictManager="latest-revision" />
<!--latest-revision-->
<resolvers>
<!--以下为ivy搜寻远程服务器或本地机器上jar文件库中jar文件的“搜索链”,ivy执行时将按照链的顺序在每个位置上寻找jar-->
<chain name="libs.local">
<filesystem name="repo.local" m2compatible="true">
<ivy pattern="${repository.dir}/[organisation]/[module]/ivy-[revision].xml" />
<artifact pattern="${repository.dir}/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]" />
<artifact pattern="${repository.dir}/[organisation]/[module]/[type]s/[artifact].[ext]" />
</filesystem>
<ibiblio name="repo1.maven.org" m2compatible="true" root="http://repo1.maven.org/maven2" />
<ibiblio name="repo2.maven.org" m2compatible="true" root="http://repo2.maven.org/maven2" />
<ibiblio name="repository.jboss.com" m2compatible="true" root="http://repository.jboss.com/maven2"/>
<ibiblio name="www.ibiblio.org" m2compatible="true" root="http://www.ibiblio.org/maven" />
<ibiblio name="mirrors.ibiblio.org" m2compatible="true" root="http://mirrors.ibiblio.org/pub/mirrors/maven2" />
<ibiblio name="dev.xiaonei.com" m2compatible="true" root="http://dev.xiaonei.com/apache-mirror" />
<ibiblio name="repo.compass-project.org" m2compatible="true" root="http://repo.compass-project.org" />
<ibiblio name="maven.atlassian.com" m2compatible="true" root="https://maven.atlassian.com/repository/public" />
</chain>
</resolvers>
<!--以下可以为特定的jar包定制搜索特定的地址-->
<modules>
<module organisation="org.compass-project" name="compass" resolver="repo.compass-project.org" />
<module organisation="javax.resource" name="connector" resolver="maven.atlassian.com" />
<module organisation="javax.jms" name="jms" resolver="maven.atlassian.com" />
</modules>
</ivysettings>
4 ivy运行环节的配置
4.1 首先,下载ivy和ant,解压缩于本地目录
Ivy下载地址:http://ant.apache.org/ivy/index.html
Ant下载地址:http://ant.apache.org/
4.2 其次,配置ant路径环境变量和java环境变量,JAVA_HOME以及ANT_HOME,将bin目录配置于path变量。
4.3 最后,拷贝ivy.jar文件到ant的lib目录,在build.xml所在目录下(一般为项目根目录),执行ant脚本中的ivy命令任务,即可
5 配置中的细节问题
5.1 定制的jar包下载地址:
虽然网络上有不少特定的依赖关系库,但是对于某些特定的jar而言,必须在指定的地址下载,在指定的地址找到解决依赖关系的xml描述文件,例如:对于compass来说,ivysetting.xml文件中的resolvers部分有这样的一个解决方案:
<ibiblio name="repo.compass-project.org" m2compatible="true" root="http://repo.compass-project.org" />
接着,在module部分里面有这样的指定:
<module organisation="org.compass-project" name="compass" resolver="repo.compass-project.org" />
两者结合起来,描述的是这样一个约定:对于org.compass-project组织里面的名为compass的模块,使用指定的解决方案。
5.2 最新版本的解决方案
在ivy.xml文件中的dependencies模块中有这样一行:
<dependency org="bsh" name="bsh" rev=" latest.revision" conf="default" />
其中rev=”revision.latest”表示取用最新版本,ant脚本运行时将自动搜寻依赖库中版本最高的一个依赖包。
5.3 版本冲突的解决实例1
对于名为asm的依赖包,系统要求必须同时存在两个不同版本,一个是1.5.3(被hibernate依赖),一个是2.2.3(被spring依赖),如果只是使用ivysetting中的默认设置:
<settings defaultResolver="libs.local" defaultConflictManager="latest-revision" />
那么2.2.3一定取代1.5.2,如何让两者同时存在呢?
在dependecies模块中使用conflict标记:<conflict org="asm" rev="1.5.3,2.2.3" />即可,不同版本号使用逗号间隔。
5.4 版本冲突的解决实例2
对于名为ant的依赖包,系统要求解决两个不同版本即:1.6.5和1.7.2之间的版本冲突,但是比较特殊的是这两个版本所属的org不同,即在公共依赖库中的目录不一致,1.6.2属于apache组织,在apache目录下。而1.7.2属于org.apache组织,在org.apache目录下(这显然是ant在开发升级过程中的一个调整的结果),如何解决两个所属org不同的依赖包之间的版本冲突呢?其实简单,两个依赖包有相同的名字:ant,还是用conflict标签解决问题:
<conflict module="ant" rev="1.7.1" />