快捷搜索:  汽车  科技

maven插件开发(构建工具的进化)

maven插件开发(构建工具的进化)maven最核心的改进就在于提出仓库这个概念。我可以把所有依赖的包,都放到仓库里去,在我的工程管理文件里,标明我需要什么什么包,什么什么版本。在构建的时候,maven就自动帮我把这些包打到我的包里来了。我们再也不用操心着自己去管理几十上百个jar文件了。但是ant有一个很致命的缺陷,那就是没办法管理依赖。我们一个工程,要使用很多第三方工具,不同的工具,不同的版本。每次打包都要自己手动去把正确的版本拷到lib下面去,不用说,这个工作既枯燥还特别容易出错。为了解决这个问题,maven闪亮登场。在linux上,有一个工具叫make。我们可以通过编写Makefile来执行工程的构建。windows上相应的工具是nmake。这个工具写起来比较罗嗦,所以从早期,Java的构建就没有选择它,而是新建了一个叫做ant的工具。ant的思想和makefile比较像。定义一个任务,规定它的依赖,然后就可以通过a

在讲解基础知识的过程中,我们也要动手去写。而Java发展到现在,可以帮助我们写程序,构建,发布的工具有一大堆。今天就来讨论一下构建工具。在开始之前,我们先讲点别的。

如何学习琳琅满目的框架和工具

学Java的新人,最头疼的事情,莫过于工具太多,挑花了眼。不管你要做什么,几乎都要面临各种各样的选择。我是应该选hibernate呢?还是mybatis。我新建一个工程,要不要加上spring?mvc tx都是什么?tomcat,jetty是什么?网络编程要不要用netty?要在服务端布署多个机器,用akka行不行?DMQ呢?protobuf是什么鬼?类似的问题还有很多。

如果利用搜索引擎,想去看看这些工具分别是干嘛的,会发现,搜索结果五花八门,说什么的都有,甚至有些说法都相互矛盾。造成这些乱象的原因,一是Java的应用过于广泛,在不同的软件体系中都能看到Java的身影,而不同的需求,会使用不同的工具,甚至是同样的工具,在不同的环境下,都会有不同的用法。二是,Java的流行已经有很长时间了,随着时间的推移,肯定会有很多工具没有跟上历史的潮流而慢慢被淘汰。比如J2EE现在的市场占有率只有不足5%。再比如java swing的图形界面编程,现在几乎没有公司再使用swing进行图形界面编程了,最早,互联网公司就几乎没有采用过swing的方案。swing只在某些企业级软件里发挥作用,而随着HTML5的兴起,越来越多的软件公司也把自己的界面搬到web前端,现在的html5对2d 3d都有非常好的支持,而一套简洁的CSS就能定制出漂亮的界面。所以swing被html5挤出历史进程实属正常。再比如JSF,JavaFX等等,也算是出师未捷身先死。有用的,没用的一堆堆地都堆到新手面前,新手的感觉肯定就是“那叫一个乱”啊。

maven插件开发(构建工具的进化)(1)

maven插件开发(构建工具的进化)(2)

在linux上,有一个工具叫make。我们可以通过编写Makefile来执行工程的构建。windows上相应的工具是nmake。这个工具写起来比较罗嗦,所以从早期,Java的构建就没有选择它,而是新建了一个叫做ant的工具。ant的思想和makefile比较像。定义一个任务,规定它的依赖,然后就可以通过ant来执行这个任务了。我们通过例子看一下。下面列出一个ant工具所使用的build.xml:

<?xml version="1.0" encoding="UTF-8" ?> <project name="HelloWorld" default="run" basedir="."> <property name="src" value="src"/> <property name="dest" value="classes"/> <property name="jarfile" value="hello.jar"/> <target name="init"> <mkdir dir="${dest}"/> </target> <target name="compile" depends="init"> <javac srcdir="${src}" destdir="${dest}"/> </target> <target name="build" depends="compile"> <jar jarfile="${jarfile}" basedir="${dest}"/> </target> <target name="test" depends="build"> <java classname="test.ant.HelloWorld" classpath="${hello_jar}"/> </target> <target name="clean"> <delete dir="${dest}" /> <delete file="${hello_jar}" /> </target> </project>

可以看到ant的构建脚本还是比较清楚的。ant定义了五个任务,init compile build test clean。每个任务做什么都定义清楚了。打包之前要先编译,所以通过depends来指定依赖的路径。如果在命令行里执行ant build,那就会先执行compile,而compile又依赖于init,所以就会先执行init。看起来很合理,对吧?有了这个东西以后,我们只要一条命令:

ant test

就可以执行编程,打包,测试了。为开发者带来了很大的便利。

但是ant有一个很致命的缺陷,那就是没办法管理依赖。我们一个工程,要使用很多第三方工具,不同的工具,不同的版本。每次打包都要自己手动去把正确的版本拷到lib下面去,不用说,这个工作既枯燥还特别容易出错。为了解决这个问题,maven闪亮登场。

maven最核心的改进就在于提出仓库这个概念。我可以把所有依赖的包,都放到仓库里去,在我的工程管理文件里,标明我需要什么什么包,什么什么版本。在构建的时候,maven就自动帮我把这些包打到我的包里来了。我们再也不用操心着自己去管理几十上百个jar文件了。

这了达到这个目标,maven提出,要给每个包都标上坐标,这样,便于在仓库里进行查找。所以,使用maven构建和发布的包都会按照这个约定定义自己的坐标,例如:

<?xml version="1.0" encoding="utf-8"?> <project ...xmlns...> <groupId>cn.hinus.recruit</groupId> <artifactId>Example</artifactId> <version>0.1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> </project>

这样,我就定义了我自己的包的坐标是cn.hinus.recruit:Example:0.1.0-SNAPSHOT,而我的工程要依赖junit:junit:4.10。那么maven就会自动去帮我把junit打包进来。如果我本地没有junit,maven还会帮我去网上下载。下载的地方就是远程仓库,我们可以通过repository标签来指定远程仓库。

maven里抛弃了ant中通过target定义任务的做法,而是引入了生命周期的概念。这个问题要讲下去,就是一个大的话题了。我们暂时放一下。因为我们要看今天的最终BOSS,gradle

Gradle

maven已经很好了,可以满足绝大多数工程的构建。那为什么我们还需要新的构建工具呢?第一,maven是使用xml进行配置的,语法不简洁。第二,最关键的,maven在约定优于配置这条路上走太远了。就是说,maven不鼓励你自己定义任务,它要求用户在maven的生命周期中使用插件的方式去工作。这有点像设计模式中的模板方法模式。说通俗一点,就是我使用maven的话,想灵活地定义自己的任务是不行的。基于这个原因,gradle做了很多改进。

gradle并不是另起炉灶,它充分地使用了maven的现有资源。继承了maven中仓库,坐标,依赖这些核心概念。文件的布局也和maven相同。但同时,它又继承了ant中target的概念,我们又可以重新定义自己的任务了。(gradle中叫做task)

我们来体验一下,新建一个空目录,在命令行,执行

gradle init --type java-library

可以看到新创建了一个工程,工程根目录下,有这几项:

build.gradle gradle settings.gradle src

我们看一下,build.gradle的内容:

// Apply the java plugin to add support for Java apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { // Use 'jcenter' for resolving your dependencies. // You can declare any Maven/Ivy/file repository here. jcenter() } // In this section you declare the dependencies for your production and test code dependencies { // The production code uses the SLF4J logging API at compile time compile 'org.slf4j:slf4j-api:1.7.21' // Declare the dependency for your favourite test framework you want to use in your tests. // TestNG is also supported by the Gradle Test task. Just change the // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add // 'test.useTestNG()' to your build script. testCompile 'junit:junit:4.12' }

内容很简单,引入了java插件,指定仓库,指定依赖。可以看到依赖的设定相比起xml的写法,变得大大简化了。

使用gradle,任务又变成了核心概念了。我们就来体验一下任务。

在build.gradle里添加这样的任务:

task hello << { println 'welcome to gradle'; }

然后在命令行执行

gradle -q hello

就可以看见打印一行"welcome to gradle"。在使用maven构建的时候,如果想临时对某一个构建任务加一点log,会是个非常困难的事情 。但在gradle里,就变得非常简单,因为gradle的背后其实是groovy这个编程语言在起作用。为了验证这一点,我们再改一下:

task hello << { 3.times { println 'welcome to gradle'; } }

然后执行gradle -q hello,就可以看到连续打印了三行。使用脚本语言进行构建,这几乎给了我们任何的能力,我们可以在构建的时候做任何的事情,甚至你可以直接让gradle帮你做表达式求值 :)

导入到 IDE

在Java的开发中,我们不可能脱离集成开发环境(Integrated Develop Environment)。因为IDE提供了代码补全和方便的代码跳转,这是普通的文本编辑软件(比如vim)很难做到的。所以我们还是要通过ide来进行代码开发。

以前在ide里使用spring的时候,我们要手动下载spring的包,如果spring依赖了其他的第三方的库,我们还要去下载那个库并且添加到IDE中去。现在,我们就不必再这样做了。只需要在build.gradle里定义好依赖,然后更新它,IDE就可以自动帮我们把包导进来。

例如,我要在新建的工程里使用spring,只需要在build.gradle里添加一行:

maven插件开发(构建工具的进化)(3)

然后,在gradle窗口里,点击更新:

maven插件开发(构建工具的进化)(4)

然后,intellij就自动把spring所依赖的所有包都下载下来了。

maven插件开发(构建工具的进化)(5)

非常方便。

猜您喜欢: