天天看点

Maven学习总结(二)——Maven依赖

     1·何为maven依赖?

     何为依赖?我要和我的好朋友打电话,如果啥都没有,这事肯定办不了,所以,我需要借助于手机来帮助我完成此项任务。此时手机就充当了“依赖”的角色。

     同理,maven依赖里,功能单一化的原则,迫使我们不得不站在巨人的肩膀上,借助第三方封装好的工具,来帮助我们完成想要完成的工作。很幸运的是,我们不用去各个官网下载我们需要的各个jar包,这一切,Maven先生看来眼里,怎舍得高薪资的程序猿浪费时间于查找jar上?so,Maven先生说了,只要你们声明你们想要的东西,我都会主动推送给你们。这个时候,我们只要进行 依赖配置,想要的自然会推送过来。         

 2·Maven依赖配置

<project>      
     <dependencies> 
        <dependency>
             <groupId>junit</groupId>      
             <artifactId>junit</artifactId>      
             <version>3.8.1</version>
             <type>...</type>
             <scope>test</scope>
             <optional>...</optional>
             <exclusions>      
                 <exclusion>      
                   <groupId>...</groupId>      
                   <artifactId>...</artifactId>      
                 </exclusion> 
           </exclusions>      
         </ependency>         
       </dependencies>      
 </project>  
           

      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即可。

<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>${zookeeper.version}</version>
	<exclusions>
		<exclusion>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
		</exclusion>
		<exclusion>
			<artifactId>netty</artifactId>
			<groupId>io.netty</groupId>
		</exclusion>
	</exclusions>
	</dependency>
           

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属性,实例中定义了一个<springframework.version>子元素,其值为3.2.8.RELEASE,有了这个属性定义之后,Maven运行的时候会将pom.xml中所有的${springframework.version}替换成实际的值:3.2.8.RELEASE。也就是可以使用$和{}的方式引用Maven的属性。然后将所有springframework依赖的版本替换成<version>${springframework.version}</version>这个样子,就和在Java代码中定义了一个不变的常量一样,以后要升级版本就只需要把这个值改了。如下代码:

<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指定了当前POM模型的版本,对于maven2及maven3来说,它只能是4.0.0 commented by 
		bill 2017-1-7 -->
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>com.dynamic</groupId>
		<artifactId>itoo-jboss-project-root</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<relativePath>../itoo-jboss-project-root/pom.xml</relativePath>
	</parent>


	<artifactId>itoo-basic-parent</artifactId>
	<packaging>pom</packaging>

	<properties>
             <commons-collections.version>3.2.1</commons-collections.version>
        </properties>
       <dependencyManagement>
           <dependencies>
	               <dependency>
				<groupId>commons-collections</groupId>
				<artifactId>commons-collections</artifactId>
				<version>${commons-collections.version}</version>
			</dependency>
            </dependencies>
	</dependencyManagement>
</project>
           

3 ·优化依赖

       ·当前已解析依赖:mvn dependency:list

       ·依赖分析:mvn dependency:analyze

       ·依赖树分析:mvn dependency:tree>1.txt

在项目中我们进行过一次大型的pom重构,当时项目中存在的主要问题是:maven管理混乱。所以当时我们是通过依赖分析,按照maven的最佳实践对我们的项目进行大型的重构。下面,我拿一个简单的demo来分析,大家好好看哦!

·pom文件如下(主要看spring的相关配置):

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mark</groupId>
  <artifactId>mark</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>mark Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <spring.version>4.0.4.RELEASE</spring.version>
    <hibernate.version>4.3.5.Final</hibernate.version>
    <junit.version>3.8.1</junit.version>
    <hibernate-validator.version>5.1.2.Final</hibernate-validator.version>
    <c3p0.version>0.9.1.2</c3p0.version>
    <mysql.version>5.1.31</mysql.version>
    <jackson-databind.version>2.4.2</jackson-databind.version>
    <jstl.version>1.2.1</jstl.version>
    <standard.version>1.1.2</standard.version>
    <tomcat.version>5.5.23</tomcat.version>
  </properties>

  <dependencies>

    <!--测试相关-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <!--spring相关依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
  <!--hibernate相关依赖-->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>${hibernate-validator.version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <!--mysql及连接池相关依赖-->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>${c3p0.version}</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>

    <!-- Jackson Json处理工具包 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson-databind.version}</version>
    </dependency>
    <!--jsp相关-->
    <dependency>
      <groupId>javax.servlet.jsp.jstl</groupId>
      <artifactId>javax.servlet.jsp.jstl-api</artifactId>
      <version>${jstl.version}</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>${standard.version}</version>
    </dependency>
    <dependency>
      <groupId>tomcat</groupId>
      <artifactId>servlet-api</artifactId>
      <version>${tomcat.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>tomcat</groupId>
      <artifactId>jsp-api</artifactId>
      <version>${tomcat.version}</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>mark</finalName>
    <plugins>
      <!-- 配置Tomcat插件 -->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8080</port>
          <path>/</path>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
           

从pom结构中可以看出,关于spring的依赖只有两个,那么接下来我们就来分析一下该pom文件:

执行命令:mvn dependency:analyze,分析结果如下:

Maven学习总结(二)——Maven依赖

图中圈住的是关键内容,可以看出有两部分

使用未声明部分:指项目中需要这些jar包的依赖但是没有显示声明,

声明未使用部分:指项目中显示声明了但没有使用(仅限编译阶段,运行阶段检测不出来需再次分析)

这里我们还是主要看关于spring的相关依赖,可以发现pom中显示声明的webmvc依赖出现在声明未使用部分,而其他如核心jar包如spring-beans等出现在使用未声明部分,问题是,为什么核心jar包没有在项目中依赖,也不影响项目的正常运行呢?

    下面结合依赖树一起来分析:

    输入命令:mvn dependency:tree,结果如下:

Maven学习总结(二)——Maven依赖

只看spring相关的依赖树,发现,spring的一些核心jar文件被spring-webmvc传递依赖过来了,这也就是为什么项目中不显示声明也不影响使用的原因。但是这样做,不符合maven的最佳实践,最佳实践我们应该怎么做呢?就是按照依赖分析的那张图解决:使用未声明部分我们统统显示声明在pom结构里,声明未使用部分分析后我们再去删除相应jar文件,修改后如下(只显示依赖项)

<dependencies>

    <!--测试相关-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <!--spring相关依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
  <!--hibernate相关依赖-->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate.javax.persistence</groupId>
      <artifactId>hibernate-jpa-2.1-api</artifactId>
      <version>1.0.0.Final</version>
    </dependency>
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.1.0.Final</version>
    </dependency>
    <!--mysql及连接池相关依赖-->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>${c3p0.version}</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>

    <!-- Jackson Json处理工具包 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson-databind.version}</version>
    </dependency>

    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
  </dependencies>
           

再次依赖分析,结果如下:

Maven学习总结(二)——Maven依赖

会发现使用未声明部分不见了,也就是我们都在pom中显示声明了,但是未使用但声明部分还存在,这部分的依赖就要一个个的分析是否被依赖了,比如说junit,不能删,因为需要测试,jackson依赖不能删,因为json文件的解析需要改依赖,这些都是编译环境下无法检测出的,所以,通过分析后再做决定就ok了。

    好了,这就是整个依赖解析优化的过程,也是对maven最佳实践的一个实现。

关于maven的依赖部分就暂时告一段落了,感兴趣的亲们一起交流哦!