1·何为maven依赖?
何为依赖?我要和我的好朋友打电话,如果啥都没有,这事肯定办不了,所以,我需要借助于手机来帮助我完成此项任务。此时手机就充当了“依赖”的角色。
同理,maven依赖里,功能单一化的原则,迫使我们不得不站在巨人的肩膀上,借助第三方封装好的工具,来帮助我们完成想要完成的工作。很幸运的是,我们不用去各个官网下载我们需要的各个jar包,这一切,Maven先生看来眼里,怎舍得高薪资的程序猿浪费时间于查找jar上?so,Maven先生说了,只要你们声明你们想要的东西,我都会主动推送给你们。这个时候,我们只要进行 依赖配置,想要的自然会推送过来。
2·Maven依赖配置
junit
junit
3.8.1
...
test
...
...
...
3·maven依赖范围
依赖范围控制依赖与三种classpath(编译classpath,测试classpath,运行classpath)的关系。其中,依赖 范围通过scope标签来达到依赖范围的控制。该scope标签的可取值为:
·compile
·test
·provided
·runtime
·system
·import
值为compile时,指该jar包将影响项目的三种classpath路径,即在测试,编译,运行都有该jar包且可依赖。
值为test时,指该jar包将只影响测试classpath路径下的代码,什么意思呢?1·在编译情况下不识别:如在 maven默认的源代码路径下src/main/java,中使用@test注解,编译会报错(可自行尝试);2·在运行情况下不可用:如项目最后打好的war包并无junit的依赖。
值为provided时,指该jar包只作用于编译和测试classpath,在项目最后打成的war包不存在该类jar包。怎么讲?若项目部署再tomcat的web服务器上,tomcat本身可提供jsp-api.jar,servlet-api.jar两个jar构件,则在项目中可将这两个jar包都打上provided值的scoped标签,这样可减少重复jar包依赖以及减少依赖版本冲突。
值为runtime时,指该jar包只作用于测试和运行classpath,即编译情况下不需要该jar包的参与。如JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
system和import不常用,这里不做解释。
4·maven传递依赖机制
何为传递依赖:A直接依赖于B,B直接依赖于C,则C会被间接传递依赖到A中。
优点:考虑一个基于Spring Framework的项目中,如果不使用maven,那么在项目中就需要下载相关依赖,但又
由于Spring Framework会依赖于其它类库,所以,我们就要把和Spring Framework以及相关的所有jar包加载到我们的项目中,这个时候就会加大我们的管理成本,而Maven传递依赖机制的引入,就很好的解决了这个问题。Maven会解析各个直接依赖的POM,将那些有必要的间接依赖,以传递性依赖的形式引入到当前的项目中。
缺点:1·影响项目的稳定性:若项目中依赖于spring Framework,而Spring Framework又依赖于某个快照版本的jar包,此时,就会增加本身项目的不稳定因素。
2·不方便管理。和maven的最佳实践相矛盾。详情请看5最佳实践中的demo分析。
5·最佳实现
1·排除依赖
maven传递依赖机制的引入,大大简化了我们的工作,但同时,也会给我们带来一些困扰,比如说,我们
的项目依赖于一个第三方jar包,而由于某种原因,这个第三方jar包同时依赖另一个类库的SNAPSHOT版本,那个这个SNAPSHOT版本就会被传递依赖到我们项目中,这样的版本很有可能直接影响到我们的项目,所以,我们就要排除掉该依赖。
代码中使用exclustions元素声明排除依赖,注意:声明exclusion的时候只需要groupId和artifactId即可。
org.apache.zookeeper
zookeeper
${zookeeper.version}
org.slf4j
slf4j-log4j12
netty
io.netty
2·归类依赖
看如下两端代码的区别:
使用常量不仅让代码变得更加简洁,更重要的是可以避免重复,在需要更改PI的地方,只需要修改一处即可,降低了错误发生的概率。
//方式1
public double c(double r){
return 2*3.14*r;
}
public double s(double r){
return 3.14*r*r;
}
//方式2
public final double PI=3.14;
public double c1(double r){
return 2*PI*r;
}
public double s1(double r){
return PI*r*r;
}
同理,在项目开发中往往会引入同一个项目中的多个jar包,比如最常见的spring,如果我们项目中用到很多关于SpringFramework的依赖,它们分别是spring-core-3.2.8.RELEASE,spring-beans-3.2.8.RELEASE,spring-context-3.2.8.RELEASE,它们都是来自同一项目的不同模块。因此,所有这些依赖的版本都是相同的,而且可以预见,如果将来需要升级SpringFramework,这些依赖的版本会一起升级。
因此,我们应该在一个唯一的地方定义版本,并且在dependency声明引用这一版本,这一在SpringFramework升级的时候只需要修改一处即可。首先使用properties元素定义Maven属性,实例中定义了一个
4.0.0
com.dynamic
itoo-jboss-project-root
0.0.1-SNAPSHOT
../itoo-jboss-project-root/pom.xml
itoo-basic-parent
pom
3.2.1
commons-collections
commons-collections
${commons-collections.version}
·当前已解析依赖:mvn dependency:list
·依赖分析:mvn dependency:analyze
·依赖树分析:mvn dependency:tree>1.txt
在项目中我们进行过一次大型的pom重构,当时项目中存在的主要问题是:maven管理混乱。所以当时我们是通过依赖分析,按照maven的最佳实践对我们的项目进行大型的重构。下面,我拿一个简单的demo来分析,大家好好看哦!
·pom文件如下(主要看spring的相关配置):
4.0.0
com.mark
mark
war
1.0-SNAPSHOT
mark Maven Webapp
http://maven.apache.org
4.0.4.RELEASE
4.3.5.Final
3.8.1
5.1.2.Final
0.9.1.2
5.1.31
2.4.2
1.2.1
1.1.2
5.5.23
junit
junit
${junit.version}
test
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-orm
${spring.version}
org.hibernate
hibernate-validator
${hibernate-validator.version}
org.hibernate
hibernate-core
${hibernate.version}
c3p0
c3p0
${c3p0.version}
mysql
mysql-connector-java
${mysql.version}
com.fasterxml.jackson.core
jackson-databind
${jackson-databind.version}
javax.servlet.jsp.jstl
javax.servlet.jsp.jstl-api
${jstl.version}
taglibs
standard
${standard.version}
tomcat
servlet-api
${tomcat.version}
provided
tomcat
jsp-api
${tomcat.version}
provided
mark
org.apache.tomcat.maven
tomcat7-maven-plugin
2.2
8080
/
执行命令:mvn dependency:analyze,分析结果如下:
图中圈住的是关键内容,可以看出有两部分
使用未声明部分:指项目中需要这些jar包的依赖但是没有显示声明,
声明未使用部分:指项目中显示声明了但没有使用(仅限编译阶段,运行阶段检测不出来需再次分析)
这里我们还是主要看关于spring的相关依赖,可以发现pom中显示声明的webmvc依赖出现在声明未使用部分,而其他如核心jar包如spring-beans等出现在使用未声明部分,问题是,为什么核心jar包没有在项目中依赖,也不影响项目的正常运行呢?
下面结合依赖树一起来分析:
输入命令:mvn dependency:tree,结果如下:
只看spring相关的依赖树,发现,spring的一些核心jar文件被spring-webmvc传递依赖过来了,这也就是为什么项目中不显示声明也不影响使用的原因。但是这样做,不符合maven的最佳实践,最佳实践我们应该怎么做呢?就是按照依赖分析的那张图解决:使用未声明部分我们统统显示声明在pom结构里,声明未使用部分分析后我们再去删除相应jar文件,修改后如下(只显示依赖项)
junit
junit
${junit.version}
test
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-orm
${spring.version}
org.hibernate
hibernate-core
${hibernate.version}
org.hibernate.javax.persistence
hibernate-jpa-2.1-api
1.0.0.Final
javax.validation
validation-api
1.1.0.Final
c3p0
c3p0
${c3p0.version}
mysql
mysql-connector-java
${mysql.version}
com.fasterxml.jackson.core
jackson-databind
${jackson-databind.version}
commons-fileupload
commons-fileupload
1.3.1
会发现使用未声明部分不见了,也就是我们都在pom中显示声明了,但是未使用但声明部分还存在,这部分的依赖就要一个个的分析是否被依赖了,比如说junit,不能删,因为需要测试,jackson依赖不能删,因为json文件的解析需要改依赖,这些都是编译环境下无法检测出的,所以,通过分析后再做决定就ok了。
好了,这就是整个依赖解析优化的过程,也是对maven最佳实践的一个实现。
关于maven的依赖部分就暂时告一段落了,感兴趣的亲们一起交流哦!