NO END FOR LEARNING

Writing blog if you feel tired | 学海无涯 苦写博客

Gradle深入与实战(五)自定义插件

| Comments

利用Gradle做构建,必然逃不掉Gradle的插件的使用,即便是最简单的Java或Groovy的应用都需要使用Java插件或者Groovy插件。

Gradle插件的作用就是将会被重复利用的逻辑打包,这样就可以在不同的项目中重复的使用。比如在上一节中实现的集成测试任务,就可以打包到插件中,然后在其他的工程中使用,而不需要重复的写相同的任务。

Gradle提供了三种写插件的方式:

1.直接在build.gradle文件中写插件,然后直接使用,不好的地方很明显,不能在该build.gradle脚本之外的位置(其他脚本或者工程)中使用。

2.将插件写在项目的rootProjectDir/buildSrc/src/main/groovy包下,Gradle会负责编译和放置到classpath,虽然可以多个gradle脚本中使用,但是不能在其他工程中使用。

3.一个独立的插件工程,很明显,这是最常见的实现方式,因为可以被任何脚本或者工程使用。

为了节省时间,我们直接进入到最常见的实现方式:实现一个独立的插件工程

这是一个Groovy工程

我们知道,Gradle项目是基于Groovy语言开发的,所以插件功能必然是一个Groovy工程,构建创建一个build.gradle文件。

build.gradle
1
2
3
4
5
6
7
8
9
10
11
apply plugin: 'idea'
apply plugin: 'groovy'

dependencies {
    compile gradleApi()
    compile localGroovy()
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

按照Groovy插件推荐的目录结构建立好下面结构的目录,你可以忽略java那一级

src/main/java
src/main/resources
src/main/groovy
src/test/java
src/test/resources
src/test/groovy

利用Plugin接口实现插件

然后,我们写一个Groovy类,让它实现Plugin接口,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package me.zeph.gradle.plugin

import me.zeph.gradle.extension.HelloExtension
import org.gradle.api.Plugin
import org.gradle.api.Project

class HelloPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task('hello') << {
            println 'hello plugin'
        }
    }
}

这里,我们通过project的task方法实现了一个名字是hello的task,里面打印了一句话。这个任务很简单,现在我们来增加一点点复杂度。记不记得大部分插件在使用之后,除了提供一些列的task,还提供了许多的closure(闭包),可以通过这些闭包传递一些参数进去。那么,这是怎么是实现的呢?很简单,利用project提供的扩展。

扩展的使用

定义一个名字是HelloExtension的Groovy类(名字其实无所谓叫什么,而且居然不需要实现任何的接口):

1
2
3
4
5
package me.zeph.gradle.extension

class HelloExtension {
    String message;
}

改变一些插件的实现:

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package me.zeph.gradle.plugin

import me.zeph.gradle.extension.HelloExtension
import org.gradle.api.Plugin
import org.gradle.api.Project

class HelloPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.extensions.add('hello', HelloExtension)
        project.task('hello') << {
            println project.hello.message
        }
    }
}

project.extensions.add(‘hello’, HelloExtension),这段代码将HelloExtension添加到project的extensions中,于是task就可以通过project.hello.message来获取。是不是很简单?

告诉别人这是个插件:插件id

那么,功能部分都写完了,怎么样让其他构件脚本知道这是一个插件能?配置META-INF。

在resources目录下建立这样一个目录结构:/resources/META-INF/gradle-plugins

然后在这里建立一个名字是me.zeph.hello.properties的Property文件,文件里的内容是:

me.zeph.hello.properties
1
implementation-class=me.zeph.gradle.plugin.HelloPlugin

这个Property文件的命名并不是随意定义的,名字的作用是定义该插件的id,什么意思?说白了就是apply时使用的名字。如下:

1
apply plugin: 'me.zeph.hello'

至于里面的内容,我就不解释了,一眼就看明白了。

使用生成的插件

到这里,一个独立的插件工程就完成了,实验一把!!

运行gredlew clean assemble,将生成的jar文件,拷贝到其他的项目目录中(这里没有upload到仓库,所以直接文件形式引入依赖)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath fileTree(dir: 'libs', include: '*.jar')
    }
}

apply plugin: 'me.zeph.hello'

hello {
    message = 'hello gradle plugin'
}

然后运行gradlew hello,就可以看到hello任务的执行。

总结,其实实现一个Gradle的独立插件工程,从建立工程的角度还是比较简单的,关键在如何通过Groovy实现插件,已经理解插件的api。

参考资料:

1.https://gradle.org/docs/current/userguide/custom_plugins.html

用Spring Boot开发Spring项目 快速上手

| Comments

Spring Boot是Spring团队提供的全新框架,其设计目的是用来简化新的Spring应用的初始搭建以及开发过程。

Spring Boot的主要目的是:(参考:https://github.com/spring-projects/spring-boot%EF%BC%89

1.Provide a radically faster and widely accessible getting started experience for all Spring development
2.Be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults
3.Provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration)
4.Absolutely no code generation and no requirement for XML configuration

快速启动Spring的开发,快速响应变化,提供非功能的特性,无需XML配置,这是Spring Boot所带来的优势。

下面通过一个例子,来快速的开发一个基于Spring Boot的Rest应用

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'gs-spring-boot'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web") {
        exclude module: "spring-boot-starter-tomcat"
    }
    compile("org.springframework.boot:spring-boot-starter-jetty")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

在Gradle中,应用了Spring Boot Gradle插件,你可以看到dependencies中并没有指定Spring Boot依赖的版本,这是因为已经在插件的定义中已经指定了版本:1.2.2.RELEASE,所以这里就可以省略。(关于省略版本这一点,在之后的文章中会解释)。

而且,依赖spring-boot-starter-web会一站式的帮你将与web开发相关的所有相关依赖下载,而不需要你一个个复制粘贴。这也是所有其他Spring Boot Starter的主要目的。

HelloController.java
1
2
3
4
5
6
7
8
9
10
11
12
package me.zeph.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
  @RequestMapping(value = "/")
  public String hello() {
      return "hello spring boot";
  }
}

这里定义了一个HelloController使用RestController注解,@RestController代表着@Controller和@ResponseBody,可以简化写Restful类型Controller的流程。

Application.java
1
2
3
4
5
6
7
8
9
10
11
package me.zeph;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
  public static void main(String args[]) {
      SpringApplication.run(Application.class, args);
  }
}

最后一步,定义Spring boot应用的main方法,@SpringBootApplication等价于另外三个注解@Configuration,@EnableAutoConfiguration,@ComponentScan。

运行Gradle的命令: gradlew bootRun就可以启动应用程序。

通过Spring Boot创建的Java应用可以直接通过java -jar启动(即便它是Web应用)。也就是说,在这里,首先运行gradle assemble,得到jar文件,然后运行java -jar gs-spring-boot-0.1.0.jar。

参考资料:
1.https://github.com/spring-projects/spring-boot
2.http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-first-application