Dropwizard:一個簡潔的RESTful Web框架
Dropwizard跨越了開發庫與框架的界限,旨在為Web應用所需的功能提供高性能、可靠的實現。Dropwizard將這些功能抽象為可重用的開發庫,因此應用程序可以保持精簡與專注,從而大大減少產品面世的時間以及維護負擔。
Jetty HTTP庫
Web應用都離不開HTTP,Dropwizard使用Jetty HTTP庫為項目嵌入HTTP服務器。與復雜的應用服務器不同,Dropwizard項目通過main方法加快HTTP服務器處理。在生產環境獨立進程中運行Java應用程序會減少很多麻煩(沒有PermGen問題,沒有應用服務器配置和維護,沒有神秘的部署工具、沒有類加載器問題、沒有隱藏的應用程序日期、沒有多個應用程序負載的垃圾回收器調優)。不僅如此,你還可以使用現成的Unix進程管理工具。Jersey REST庫
我們發現,要構建RESTful Web應用程序,從性能和功能角度考慮Jersey(JAX-RS參考實現)是最佳選擇。你可以編寫整潔、易於測試的類,將HTTP請求映射到簡單的Java對象。Jersey REST庫支持流輸出、URI參數矩陣、條件GET請求等功能。Jackson JSON庫
如果說JSON是Web領域的通用數據格式,那么Jackson就是JVM平台JSON處理的王者。除了處理速度飛快,Jackson還支持復雜的對象映射器,可以直接導出領域模型。Metrics度量庫
Metrics庫更加全面,它提供了無論倫比的視角,可以更好地了解代碼在生產環境下的行為。其它開發庫
除了Jetty、Jersey 和 Jackson,Dropwizard還包含了很多其它非常有幫助的開發庫:- Guava:支持不可變數據結構,提供日益豐富的Java工具類加速開發。
- Logback 和 slf4j 可以提供高效靈活的日志功能。
- Hibernate Validator(
JSR-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(); } }
template: Hello, %s!
defaultName: Stranger
2. 新建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/