Dropwizard框架入门

最近项目用到了Dropwizard框架,个人感觉还不错,那么这里就从他们官网入手,然后加上自己的实现步骤让大家初步了解这个框架。

官网对DW(Dropwizard)的定义是跨越了一个库和框架之间的界限。他的目标是提供一个生产就绪的web应用程序所需的一切性能可靠的实现。那么这句话可能有些绕,我个人理解就是他能免去我们部署web应用的很多步骤。由于这个功能被提取到可以重复使用的库中,我们的应用程序保持很大程度的精简和集中,这样可以减少我们程序的上线时间和维护负担。

Jetty for HTTP

由于Web应用不可能缺少HTTP,DW使用Jetty Http库将一个非常棒的HTTP服务器嵌入到我们的项目中。DW不是将你的程序提交到复杂的服务器上,DW上有个main方法来启动我们的服务器,DW是将我们的应用作为一个简单的线程来跑,消去了Java生产环境中一些非常复杂令人讨厌的过程,并且允许我们使用所有现有的Unix进程管理工具。

Jersey for REST

为了定义Restful的web应用,我们发现在性能和特性方面没有什么能比得过Jersey。它允许你编写干净的,可以测试的类,这个类可以优雅的将http请求映射成为简单的Java对象。它支持流输出,矩阵URL参数,条件GET请求,还有更多。

Jackson for JSON

在数据格式方面,JSON已经成为了网络的通用语,Jackson在jvm中就是Json的龙头老大。除了像闪电一样快速,他有一个复杂的对象映射,允许你直接导出你的域模型。

Metrics for metrics

Metrics库对事物进行舍入,在你的生产环境中,为你提供独一无二的洞察力。(也就是说这个是用来监控)那么到了这里,我们关于DW的总体印象应该已经差不多了,下面我结合官网实际操作。

我使用maven和idea进行开发,项目名字为:dw_demo。关于如何创建maven项目不解释,创建完项目后如图所示:

然后打开我们的pom.xml文件,加入dw的依赖(以下并非完全pom文件,仅展现部分):

<properties>
    <dropwizard.version>0.9.2</dropwizard.version>
</properties>
<dependencies>
<dependency>
    <groupId>io.dropwizard</groupId>
    <artifactId>dropwizard-core</artifactId>
    <version>${dropwizard.version}</version>
</dependency>
</dependencies>

Creating A Configuration Class

每个DW应用都有他自己的子类:Configuration,这个类指定环境中特定的参数。这

些参数在YAML类型的配置文件中被指定,其被反序列化为应用程序配置类的实例并验

证。(这句话的意思就是这个配置文件中指定的参数,会被映射到我们项目的一个类)

我们将要构建的是一个helloworld高性能服务。我们的一个要求就是我们需要能够在不同

的环境中让它说hello。在开始之前我们需要指定至少两个内容:一个说hello的模板 还有

一个默认的名字以防用户忘记指定。

那么我下面开始创建我的配置文件:

内容如下:

package com.config;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.Configuration;
import org.hibernate.validator.constraints.NotEmpty;

/**
 * Created by moon on 2017/1/13.
 */
public class HelloWorldConfiguration extends Configuration {
    @NotEmpty
    private String template;

    @NotEmpty
    private String defaultName = "Stranger";

    @JsonProperty
    public String getTemplate() {
        return template;
    }

    @JsonProperty
    public void setTemplate(String template) {
        this.template = template;
    }

    @JsonProperty
    public String getDefaultName() {
        return defaultName;
    }

    @JsonProperty
    public void setDefaultName(String name) {
        this.defaultName = name;
    }
}

当这个类被从YAML配置文件反序列化的时候,他会从YAML对象中获取两个根层次的变量:template 用来说helloworld的模板。defaultName 默认的名字。template和defaultName都用@NotEmpty被注释,所以在YAML配置文件中如果有空值或者忘了其中一者,异常将会被抛出,我们的应用将不会被启动。

defaultName和template的get 和set 方法都被@JsonProperty标注,这不止允许jackson从YAML配置文件反序列化,同样允许它序列化。

然后我们创建一个YAML的配置文件:

里面的内容如下:

template: Hello, %s!
defaultName: Stranger

大家可以看到,与我们的配置类中的变量一一对应,相信很多人看到这里就明白了。

Creating An Application Class

结合我们项目中的Configuration子类,我们的Application的子类形成了我们DW的应用的核心。Application的子类把不同的提供各式各样功能的包和命令拉取到了一起。

现在,我们开始建立我们的Application子类:

其内容如下:

package com.app;

import com.config.HelloWorldConfiguration;
import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;

/**
 * Created by moon on 2017/1/13.
 */
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {
    public static void main(String[] args) throws Exception {
        new HelloWorldApplication().run(args);
    }

    @Override
    public String getName() {
        return "hello-world";
    }

    @Override
    public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
        // nothing to do yet
    }

    @Override
    public void run(HelloWorldConfiguration configuration,
                    Environment environment) {
        // nothing to do yet
    }

}

正如我们所看到的,HelloWorldApplication使用应用程序的configuration进行参数化。(因为用了我们的HelloWorldConfiuration,而它是Configuration的子类)。

initialize方法用于配置应用在正式启动之前所需:包,配置源等。同时我们需要加入一个main方法,这是我们应用的入口。到目前为止,我们并没有实现任何的功能,所以我们的run方法有点无趣,让我们开始丰富它。

Creating A Representation Class

在我们开始继续我们的程序之前,我们需要停下来思考一下我们程序的API。幸运的是,我们的应用需要符合行业标准。RFC 1149(别问我这是什么,连这个都不知道,我tm也不知道)。他指定了helloworld saying的如下json表达形式:

{
  "id": 1,
  "content": "Hi!"
}

id字段是语法的唯一标识符。content是说的具体内容。

为了建模这个表示,我们需要创建一个表示类 :

该类的内容如下:

package com.api;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.Length;

/**
 * Created by moon on 2017/1/13.
 */
public class Saying {
    private long id;

    @Length(max = 3)
    private String content;

    public Saying() {
        // Jackson deserialization
    }

    public Saying(long id, String content) {
        this.id = id;
        this.content = content;
    }

    @JsonProperty
    public long getId() {
        return id;
    }

    @JsonProperty
    public String getContent() {
        return content;
    }
}

这是一个非常简单的POJO,但是有些需要注意的地方。

首先,他是不可更改的。这使得saying在多线程环境和单线程环境非常容易被推理。其次,它使用java的JavaBean来保存id和content属性。这允许jackson把他序列化为我们需要的JSON。jackson对象的映射代码将会使用getId()返回的对象来填充JSON对象的id字段,content同理。最后,bean利用验证来确保内容不大于3。

Creating A Resource Class

Jersey资源是DW应用程序的肉和土豆(这种比喻我也是醉了)。每个资源类都与URL相关联(这个很重要,后面有说)。对于我们的应用程序来说,我们需要一个resources来通过url:/helloworld来返回新的Saying实例对象。

现在我们开始建立的Resource:

类的内容如下:

package com.resource;

import com.api.Saying;
import com.codahale.metrics.annotation.Timed;
import com.google.common.base.Optional;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created by moon on 2017/1/13.
 */
@Path("/hello-world")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {
    private final String template;
    private final String defaultName;
    private final AtomicLong counter;

    public HelloWorldResource(String template, String defaultName) {
        this.template = template;
        this.defaultName = defaultName;
        this.counter = new AtomicLong();
    }
    @GET
    @Timed
    public Saying sayHello(@QueryParam("name") Optional<String> name) {
        final String value = String.format(template, name.or(defaultName));
        return new Saying(counter.incrementAndGet(), value);
    }

}

HelloWorldResource有两个声明:@Path和@Produces。@Path("/hello-world")告诉Jersey这个resource可以通过 "/hello-world"URL被访问。

@Produces(MediaType.APPLICATION_JSON)让Jersey的内容协商代码知道这个资源产生的是application/json.

HelloWorldResource构造器接收两个参数,创建saying的template和当用户没有指明名字时的默认名称。AtomicLong为我们提供一种线程安全,简易的方式去生成(ish)ID。

sayHello方法是这个类的肉,也是一个非常简单的方法。@QueryParam("name")告诉Jersey把在查询参数中的name映射到方法中的name中。如果一个客户发送请求到:/hello-world?name=Dougie,sayHello 方法将会伴随Optional.of("Dougie")被调用。如果查询参数中没有name,sayHello将会伴随着Optional.absent()被调用。

在sayHello方法里面,我们增加计数器的值,使用String.format来格式化模板,返回一个新的Saying实例。因为sayHello被@Timed注释,DW将会自动调用他的持续时间和速率记录为度量定时器。

一旦sayHello返回,Jersey将会采用Saying的实例,并寻找一个提供程序类来将Saying实例写为:application/json。

Registering A Resource

在这些正式工作之前,我们需要到HelloWorldApplication中,并将新的resouce加入其中,在run方法中我们可以读取到HelloWorldConfiguration的template和defaultName实例,创建一个新的HelloWorldResource实例,并将其加入到新的Jersey环境中。

我们HelloWorldApplication中新的run方法如下:

@Override
public void run(HelloWorldConfiguration configuration,
                Environment environment) {
    final HelloWorldResource resource = new HelloWorldResource(
            configuration.getTemplate(),
            configuration.getDefaultName()
    );
    environment.jersey().register(resource);
}

当我们的应用启动的时候,我们使用配置文件中的参数创建一个新的资源类实例,并传递给environment.

在pom文件中加入:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.3</version>
            <configuration>
                <createDependencyReducedPom>true</createDependencyReducedPom>
                <filters>
                    <filter>
                        <artifact>*:*</artifact>
                        <excludes>
                            <exclude>META-INF/*.SF</exclude>
                            <exclude>META-INF/*.DSA</exclude>
                            <exclude>META-INF/*.RSA</exclude>
                        </excludes>
                    </filter>
                </filters>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer
       implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                            <transformer
        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.app.HelloWorldApplication</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>

    </plugins>
</build>

然后打包:

打包成功后,在我们的target目录下面会出现我们所需的包:

然后我们开始运行:

这里面官方为我们提供两个参数,我们需要启动服务,所以后面加入server参数,重新启动如下:

这说明我们的项目已经启动了,那么让我们访问一下url看是否正确:

返回结果正常,没毛病。

以上仅仅是DW的初步,还有许多其他功能,由于时间关系,不做详细介绍,如果

有时间我会再奉上一版深度版的。链接为:

DW官网深层次内容

希望这次的讲解对大家有帮助,感谢开源。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏人人都是极客

你知道何为线程与进程吗??

什么是线程?线程与进程与有什么关系?这是一个非常抽象的问题,也是一个特别广的话题,涉及到非常多的知识。我不能确保能把它讲的话,也不能确保讲的内容全部都正确。即使...

1232
来自专栏Spring相关

Getway网关管理ZUUL

微服务架构体系中,通常一个业务系统会有很多的微服务,比如:OrderService、ProductService、UserService...,为了让调用更简单...

744
来自专栏zhisheng

SpringBoot ActiveMQ 整合使用

介绍 ActiveMQ 它是 Apache 出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持 JMS1.1 和 J2EE 1.4 规范的...

3288
来自专栏JavaEE

ssm整合案例(超级详细)spring+springmvc+mybatis整合案例

30610
来自专栏子勰随笔

Android简单实现的多线程下载模块

1776
来自专栏aoho求索

Java SPI机制详解

1972
来自专栏Samego开发资源

JavaRMI Of Linux and Window

2325
来自专栏芋道源码1024

注册中心 Eureka 源码解析 —— Eureka-Client 初始化(一)之 EurekaInstanceConfig

本文主要基于 Eureka 1.8.X 版本 1. 概述 2. EurekaInstanceConfig 2.1 类关系图 2.2 配置属性 2.3 Abstr...

4176
来自专栏编舟记

R3 Corda 和 springboot 集成

因为Corda内置的Corda Webserver已经被标记成弃用了,一般不再提供支持;再者,springboot的生态明显占优。

852
来自专栏IT综合技术分享

使用Dropwizard搭建一个Hello World

官方例子中main方法写在Application类中,我这里将其提出来了,方便大家理解。

623

扫描关注云+社区