maven的使用和爬过的坑
maven的介绍及使用
一. 为何要使用maven?
公司之前的项目类型是传统的hadoop项目
发到全国各地的现场去运行
但是遇到的问题是:
1. 版本管理混乱
开发人员直接从svn下载代码发送给现场
版本多的时候,出现问题难以定位,为问题的排查和解决增加了很大的难度
2. 对于部分地市,jar包过大
部分地市的特殊性,会需要一些独特的jar包,
为了保证程序正常运行,需要把相关jar包打进去, 导致jar包达到100-200M, 严重影响使用和效率
3. 项目过于复杂, 层次结构不够清晰,显得很臃肿,
不利于新同事理解业务
4. 容易出现jar包冲突或者是缺少相应的依赖jar包
随着需要的jar包越来越多, 每次引入jar包也需要小心翼翼, 说不准什么时候就会造成jar包冲突,
而且也很容易造成某个jar包依赖的jar包没有引入而导致整个项目跑不起来
基于以上, 我们引入了maven
1. 在集群的某个节点上面搭建了
maven3.5 + nexus3(私仓)
这样 对测试通过的版本才会进行编译
开发人员去私仓上面下载最新的版本发布给现场,保证了版本的稳定性
2. 在pom.xml里面添加相应依赖
并使用如下标签来排除不需要的配置:
一个例子 :
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
经过优化,原来200M才能跑起来的jar包后面4M就可以正常运行!
3.利用Maven将核心项目拆分成5个模块
层次结构非常清晰,一目了然!
4. jar包冲突的问题 :
在冲突的地方像第二条一样排除掉冲突的地方即可!
缺少依赖jar包的问题:
maven在导包的时候会自动把依赖的jar包导进来,完全不需要担心!
二. maven的使用
1. maven环境变量的配置
a. 下载解压maven
maven官网下载链接 : https://maven.apache.org/download.cgi
b. 设置环境变量
打开我的电脑(此电脑) -->右键选择-->属性-->高级系统设置-->环境变量
新建变量 : MAVEN_HOME
变量名为 : E:\softWare\bigData\maven3.5.4\apache-maven-3.5.4 // 这里是maven解压后的路径
修改变量 : PATH
win10 直接在原来的基础上添加
%MAVEN_HOME%\bin即可
c. 检验环境变量是否安装成功:
cmd命令输入 : echo %MAVEN_HOME%
继续输入 : mvn -version
出现上述提示证明安装成功!!
2.maven项目的打包
首先需要介绍一下 : maven项目的依赖
传统项目的依赖 : 项目右键 --> Build Path --> Configure Build Path
即可添加,编辑,和删除项目依赖
maven项目 通过pom.xml里面的配置来进行管理
比如说:
<dependencies>
<dependency>
<groupId>pers.xmr.bigdata</groupId>
<artifactId>bigdata-common</artifactId>
<version>${bigdata-common-version}</version>
</dependency>
<dependency>
<groupId>pers.xmr.bigdata</groupId>
<artifactId>bigdata-hadoop-tools</artifactId>
<version>${bigdata-hadoop-tools-version}</version>
</dependency>
</dependencies>
对于没有任何依赖的项目,右键
1.run as --> Maven clean
2.run as–>Maven install
3.去到该项目的target目录下拿jar包就可以了
jar包里面的第一个就是我们所需要的!
对于有依赖的项目:
第一次打包的时候: 需要先从依赖的项目开始,依次执行
maven clean --> maven install 命令
然后去target目录下拿jar包
之后, 如果依赖的项目没有变动的话,直接对该项目执行
maven clean–>maven install 命令即可!
3.引入jar包的时候排除掉jar包冲突
这个在介绍maven有点的时候已经介绍到, 这里举一个实例来详细说明:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop-version}</version>
<exclusions>
<!-- hadoop,spark程序运行的时候会导致jar包冲突 -->
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>*</artifactId>
</exclusion>
<!-- 已经有一个类来解析xml,再引入这个包会导致冲突 -->
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
<!-- 已经有一个类来解析xml,再引入这个包会导致冲突 -->
<exclusion>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
</exclusion>
</exclusions>
</dependency>
上面的这个实例就是在引入jar包的时候,去除掉会导致jar包冲突的实例
就是在 dependency里面加入exclusion标签,在里面配置好每一个不需要引入的包即可!
3. 打包的时候引入相应的包,或者是排除掉不需要的配置
引入相应的包 :
将需要的部分导入进jar包里,保证程序正常运行
<-- 以下的这个实例是我在原有的项目增加redis的使用添加的,需要将redis相关的类打到jar包里,保证程序正常执行!-->
<artifactSet>
<includes>
<include>cn.mastercom*</include>
<include>org.apache.commoms.pool2*</include>
<include>redis*</include>
</includes>
</artifactSet>
排除掉不需要的配置
我们的主要目的就是使打出来的jar包尽可能小,节约jar包传输的时间
<!-- 排除不需要的配置 -->
<excludes>
<exclude>org/**/*.xml</exclude>
<exclude>org/**/*.properties</exclude>
<exclude>remote/**</exclude>
<exclude>local/**</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
4. 如何使用我们自定义的xml文件而不是默认的pom.xml文件?
在打包的时候,maven默认会按照pom.xml里的配置来引入依赖,
但是有的地市比较特殊,需要为其配置单独的xml文件才能保证程序正常运行,那么如何使打包的时候按照我们配置的文件来打包呢?
1. 首先要保证该项目依赖的项目已经执行 maven install成功
2. 通过 cmd 命令进入到该项目的文件夹下面
3. 执行命令 mvn clean
4.执行命令: mvn -f=pom.guangxi.xml package
其中pom.guangxi.xml为我们想要其编译的xml文件名
5.打jar包的时候指定主类
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>pers.xmr.bigdata.mapr.Main</mainClass>
</transformer>
</transformers>
这样,该jar包就会以我们设置的pers.xmr.bigdata.mapr.Main为主类,
而不需要我们在运行的时候自己指定!
6. 自动排除掉不需要的类及我爬的坑
有一个minimizeJar标签, 将该标签设置为true的时候会自动排除掉不需要的类,很好用,
但是一定要慎用!!! 一定要慎用!!!
<minimizeJar>true</minimizeJar>
这里分享一个坑了我很久的问题 :
在hadoop框架里使用redis的时候,本地测试已经没有问题,但是打成jar包放到集群上的时候报了如下问题:
18/07/17 09:12:51 INFO mapreduce.Job: Task Id : attempt_1528676607738_0866_r_000000_2, Status : FAILED
Error: java.lang.IllegalArgumentException: Unable to create EvictionPolicy instance of type org.apache.commons.pool2.impl.DefaultEvictionPolicy
at org.apache.commons.pool2.impl.BaseGenericObjectPool.setEvictionPolicyClassName(BaseGenericObjectPool.java:615)
at org.apache.commons.pool2.impl.GenericObjectPool.setConfig(GenericObjectPool.java:321)
at org.apache.commons.pool2.impl.GenericObjectPool.<init>(GenericObjectPool.java:117)
at redis.clients.util.Pool.initPool(Pool.java:44)
at redis.clients.util.Pool.<init>(Pool.java:23)
at redis.clients.jedis.JedisPool.<init>(JedisPool.java:185)
at redis.clients.jedis.JedisClusterInfoCache.setupNodeIfNotExist(JedisClusterInfoCache.java:158)
at redis.clients.jedis.JedisClusterInfoCache.discoverClusterNodesAndSlots(JedisClusterInfoCache.java:74)
at redis.clients.jedis.JedisClusterConnectionHandler.initializeSlotsCache(JedisClusterConnectionHandler.java:39)
at redis.clients.jedis.JedisClusterConnectionHandler.<init>(JedisClusterConnectionHandler.java:17)
at redis.clients.jedis.JedisSlotBasedConnectionHandler.<init>(JedisSlotBasedConnectionHandler.java:24)
at redis.clients.jedis.BinaryJedisCluster.<init>(BinaryJedisCluster.java:54)
at redis.clients.jedis.JedisCluster.<init>(JedisCluster.java:93)
at cn.mastercom.bigdata.util.redis.JedisClusterFactory.getJedisCluster(JedisClusterFactory.java:68)
at cn.mastercom.bigdata.mapr.mro.loc.MroLableFileReducers$MroDataFileReducers.setup(MroLableFileReducers.java:61)
at org.apache.hadoop.mapreduce.Reducer.run(Reducer.java:168)
at org.apache.hadoop.mapred.ReduceTask.runNewReducer(ReduceTask.java:627)
at org.apache.hadoop.mapred.ReduceTask.run(ReduceTask.java:389)
at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:164)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:415)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1657)
at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.DefaultEvictionPolicy
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:191)
at org.apache.commons.pool2.impl.BaseGenericObjectPool.setEvictionPolicyClassName(BaseGenericObjectPool.java:606)
... 22 more
一看是ClassNotFoundException这种简单的报错,首先怀疑的肯定是没有导入jar包 :
于是去pom.xml里面去查看相应的配置 :
确实是没有直接引入commons-pool2 2.2这个jar包
只有这个 :
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
于是我画蛇添足的加上了这个配置:
<!--
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.2</version>
</dependency>
-->
打成jar包丢上去跑仍然报同样的问题,于是我去掉了这个配置
发现,在Maven Dependencies 是有相对应的jar包的:
maven会在导入jar包的时候自动导入相互依赖的jar包! 而jedis-2.9.0.jar是依赖于commons-pool2-2.4.2.jar的 所以会一并导入
那会不会是打包的时候没有把相应的jar包打进去呢?
但是我的pom.xml文件是有这样的配置的:
<artifactSet>
<includes>
<include>cn.mastercom*</include>
<include>org.apache.commons.pool2*</include>
<include>redis*</include>
</includes>
</artifactSet>
这证明了确实是把相关的jar包打进来的, 我就很疑惑了??
难道是集群抽风了?? 实际上,计算机永远都是比人可靠的
我打开了那个jar文件,发现了org.apache.commons.pool2.impl
这个包在jar包里面确实是存在的,而且这个包下面有一大堆的**.class文件
却唯独缺少了 DefaultEvictionPolicy类的.class**文件,证明确实是没有这个类的
那会不会是这个jar包本身就没有这个类呢?
当然是不可能的
排除了上述问题之后,把问题定位到了这个配置上面
<minimizeJar>true</minimizeJar>
尝试着把这个配置改成false,打成jar包丢到集群上跑,成功了!!!
也就是说,这个配置把我们需要的类给过滤掉了!
官网中对这个标签的解释如下 : (官网原话:)
附上这一部分的官网链接, 有兴趣的可以点进去看一下:
As of version 1.6, minimizeJar will respect classes that were specifically marked for inclusion in a filter.
Note that specifying an include filter for classes in an artifact implicitly excludes all non-specified classes in that artifact.
7.如何设置开发和编译的版本?
为什么需要有这个需求呢?
我们本地的开发人员普遍使用的是JDK1.8,但是有的现场集群环境JDK是1.7
所以编译需要设置为1.7,否则,程序执行的时候就会报错!
普通的项目
右键–>properties–>JavaCompiler–>被圈起来的地方设置为1.7即可
maven是这样配置的
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.7</source> <!-- 源代码使用的开发版本 -->
<target>1.7</target> <!-- 需要生成的目标class文件的编译版本 -->
<!-- 这下面的是可选项 -->
<!-- <meminitial>128m</meminitial>-->
<!--<maxmem>512m</maxmem>-->
<!--<fork>true</fork>--> <!-- fork is enable,用于明确表示不同于默认的JDK去编译 -->
<!-- <compilerVersion>1.3</compilerVersion>-->
<!-- 这个选项用来传递编译器自身不包含但是却支持的参数选项 -->
<!--<compilerArgument>-verbose -bootclasspath ${java.home}\lib\rt.jar</compilerArgument>-->
</configuration>
</plugin>
</plugins>
</build>
上面介绍到的只是冰山一角,maven还有很多有趣的特性值得去开发和应用,
而且对于一个深受传统项目毒害的人来说,用了maven,真的是回不去了!