Dropwizard簡單入門


Dropwizard:一個簡潔的RESTful Web框架

    Dropwizard跨越了開發庫與框架的界限,旨在為Web應用所需的功能提供高性能、可靠的實現。Dropwizard將這些功能抽象為可重用的開發庫,因此應用程序可以保持精簡與專注,從而大大減少產品面世的時間以及維護負擔。

Jetty HTTP庫

    Web應用都離不開HTTP,Dropwizard使用Jetty HTTP庫為項目嵌入HTTP服務器。與復雜的應用服務器不同,Dropwizard項目通過main方法加快HTTP服務器處理。在生產環境獨立進程中運行Java應用程序會減少很多麻煩(沒有PermGen問題,沒有應用服務器配置和維護,沒有神秘的部署工具、沒有類加載器問題、沒有隱藏的應用程序日期、沒有多個應用程序負載的垃圾回收器調優)。不僅如此,你還可以使用現成的Unix進程管理工具。

Jersey REST庫

    我們發現,要構建RESTful Web應用程序,從性能和功能角度考慮JerseyJAX-RS參考實現)是最佳選擇。你可以編寫整潔、易於測試的類,將HTTP請求映射到簡單的Java對象。Jersey REST庫支持流輸出、URI參數矩陣、條件GET請求等功能。

Jackson JSON庫

    如果說JSON是Web領域的通用數據格式,那么Jackson就是JVM平台JSON處理的王者。除了處理速度飛快,Jackson還支持復雜的對象映射器,可以直接導出領域模型。

Metrics度量庫

    Metrics庫更加全面,它提供了無論倫比的視角,可以更好地了解代碼在生產環境下的行為。

其它開發庫

除了JettyJersey 和 Jackson,Dropwizard還包含了很多其它非常有幫助的開發庫:
  • Guava:支持不可變數據結構,提供日益豐富的Java工具類加速開發。
  • Logback 和 slf4j 可以提供高效靈活的日志功能。
  • Hibernate ValidatorJSR-349_ 參考實現)提供了簡潔、聲明式的用戶輸入驗證框架,生成非常有用支持i18n的錯誤信息。
  • Apache HttpClient 和 Jersey 客戶端開發庫提供了與其它Web服務的底層和高層交互。
  • JDBI:為Java關系數據庫提供了最直接的方式交互。
  • Liquibase:在開發和發布周期中,為數據庫schema提供全程檢查。支持高層數據庫重構,取代了一次性DDL腳本。
  • Freemarker 和 Mustache為面向用的應用程序提供了簡單的模板系統。
  • Joda Time:完整強大的時間日期處理開發庫。

簡單示例

    推薦使用Maven構建新的Dropwizard應用,首先,在POM中加入 dropwizard.version 屬性及最新版本:

<properties>
    <dropwizard.version>INSERT VERSION HERE</dropwizard.version>
</properties>

    把 dropwizard-core 加為依賴項:

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

1. 新建Configuration類

    每個DW應用都有他自己的子類:Configuration,這個類指定環境中特定的參數。這些參數在YAML類型的配置文件中被指定,其被反序列化為應用程序配置類的實例並驗證。(這句話的意思就是這個配置文件中指定的參數,會被映射到我們項目的一個類)我們將要構建的是一個helloworld高性能服務。我們的一個要求就是我們需要能夠在不同的環境中讓它說hello,在開始之前我們需要指定至少兩個內容:一個說hello的模板 還有一個默認的名字以防用戶忘記指定。

package com.example.helloworld;

import com.example.helloworld.core.Template;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableMap;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.Map;

public class HelloWorldConfiguration extends Configuration {
    @NotEmpty
    private String template;

    @NotEmpty
    private String defaultName = "Stranger";

    @Valid
    @NotNull
    private DataSourceFactory database = new DataSourceFactory();

    @NotNull
    private Map<String, Map<String, String>> viewRendererConfiguration = Collections.emptyMap();

    @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 defaultName) {
        this.defaultName = defaultName;
    }

    public Template buildTemplate() {
        return new Template(template, defaultName);
    }

    @JsonProperty("database")
    public DataSourceFactory getDataSourceFactory() {
        return database;
    }

    @JsonProperty("database")
    public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
        this.database = dataSourceFactory;
    }

    @JsonProperty("viewRendererConfiguration")
    public Map<String, Map<String, String>> getViewRendererConfiguration() {
        return viewRendererConfiguration;
    }

    @JsonProperty("viewRendererConfiguration")
    public void setViewRendererConfiguration(Map<String, Map<String, String>> viewRendererConfiguration) {
        final ImmutableMap.Builder<String, Map<String, String>> builder = ImmutableMap.builder();
        for (Map.Entry<String, Map<String, String>> entry : viewRendererConfiguration.entrySet()) {
            builder.put(entry.getKey(), ImmutableMap.copyOf(entry.getValue()));
        }
        this.viewRendererConfiguration = builder.build();
    }
}
   當這個類被從YAML配置文件反序列化的時候,他會從YAML對象中獲取兩個根層次的變量:template 用來說helloworld的模板。defaultName 默認的名字。template和defaultName都用@NotEmpty被注釋,所以在YAML配置文件中如果有空值或者忘了其中一者,異常將會被拋出,我們的應用將不會被啟動。defaultName和template的get 和set 方法都被@JsonProperty標注,這不止允許jackson從YAML配置文件反序列化,同樣允許它序列化。
      然后我們創建一個YAML的配置文件,內容如下:
template: Hello, %s!
defaultName: Stranger

2. 新建Application類

    結合我們項目中的Configuration子類,我們的Application的子類形成了我們DW的應用的核心,Application的子類把不同的提供各式各樣功能的包和命令拉取到了一起。
package com.example.helloworld;

import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import com.example.helloworld.resources.HelloWorldResource;
import com.example.helloworld.health.TemplateHealthCheck;

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方法有點無趣,讓我們開始豐富它。

3. 新建Representation類

    在我們開始繼續我們的程序之前,我們需要停下來思考一下我們程序的API。幸運的是,我們的應用需要符合行業標准RFC 1149,它指定了helloworld saying的如下json表達形式:

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

id字段是唯一標識,content是文字內容。下面是representation實現,一個簡單的POJO類:

package com.example.helloworld.api;

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

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。

4. 新建Resource類

    Jersey資源是DW應用程序的主要內容,每個資源類都與URL相關聯(這個很重要,后面有說),對於我們的應用程序來說,我們需要一個resources來通過url:/helloworld來返回新的Saying實例對象。

package com.example.helloworld.resources;

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

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;

@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。

5. 注冊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.

轉自:http://blog.csdn.net/qq_23660243/article/details/54406075 以及 http://hao.jobbole.com/dropwizard/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM