Day 13: Dropwizard —— 非常棒的Java REST服务器栈
我已經(jīng)是一個使用了8年Java的軟件開發(fā)人員了,我寫過的大多數(shù)應(yīng)用程序是用的Spring框架或Java EE。最近,我花了一些時間學(xué)習(xí)用Python進(jìn)行web開發(fā),其中印象非常深刻的是 Flask 框架——一個微型架構(gòu),這使得它很容易寫REST后端。所以今天我決定找一個Java的Python Flask框架替代品,做一些研究后,我發(fā)現(xiàn) Dropwizard 框架可以幫助達(dá)到Flask框架同樣的生產(chǎn)力。在這篇博客中,我們將學(xué)習(xí)如何使用Dropwizard構(gòu)建一個基于REST的Java MongoDB應(yīng)用程序。
什么是Dropwizard?
Dropwizard 是一個開源的Java框架,用于開發(fā)OPS友好、高性能的基于REST的后端。它是由Yammer開發(fā)的,來驅(qū)動基于JVM的后端。
Dropwizard提供同類最佳的Java庫到一個嵌入式應(yīng)用程序包。它由以下部分組成:
除了上面提到的這幾個,Dropwizard還使用了一些其他的庫,你可以在這里找到完整的列表。
為什么是Dropwizard?
我決定學(xué)Dropwizard的原因有以下幾點(diǎn):
Github庫
今天的演示應(yīng)用程序的代碼在GitHub上有:day13-dropwizard-mongodb-demo-app。
必備條件
Eclipse的安裝很容易,只需要解壓下載下來的包即可。如果是在Linux或者M(jìn)ac機(jī)器上,開個命令行窗口,輸入如下命令:
$ tar -xzvf eclipse-jee-kepler-R-*.tar.gzWindows下,你解壓到哪里,那里就會有一個eclipse文件夾,這樣就可以直接操作了,當(dāng)然你也可以創(chuàng)建執(zhí)行文件的快捷方式到桌面。
第1步:創(chuàng)建一個新的Maven項(xiàng)目
打開Eclipse IDE,然后到項(xiàng)目工作區(qū)(project workspace)。要創(chuàng)建一個新的項(xiàng)目,轉(zhuǎn)到 文件>新建> Maven項(xiàng)目 (File > New > Maven Project) ,然后選擇 Maven 原型 - 快速啟動 (maven-archetype-quickstart),然后進(jìn)入Ground Id 和 Artifact Id,最后點(diǎn)擊“完成”。
第2步:更新pom.xml
現(xiàn)在更新pom.xml文件以包括dropwizard核心maven依賴。同時也將更新Maven項(xiàng)目使用Java 1.7版本,更新pom.xml文件后,更新Maven項(xiàng)目(右鍵單擊>Maven>更新項(xiàng)目)。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion><groupId>com.shekhar</groupId> <artifactId>blog</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging><name>blog</name> <url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties><dependencies><dependency><groupId>com.yammer.dropwizard</groupId><artifactId>dropwizard-core</artifactId><version>0.6.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>1.7</source><target>1.7</target></configuration></plugin></plugins> </build>第3步:創(chuàng)建配置類
每個Dropwizard應(yīng)用程序都有一個配置類,它指定特定的環(huán)境參數(shù)。文章后面會將如主機(jī)、端口和數(shù)據(jù)庫名之類的MongoDB的配置參數(shù)添加給它。這個類擴(kuò)展了 com.yammer.dropwizard.config.Configuration類。
import com.yammer.dropwizard.config.Configuration;public class BlogConfiguration extends Configuration{}第4步:創(chuàng)建服務(wù)類
該Dropwizard項(xiàng)目由一個服務(wù)類自舉。這個類將各種提供基本功能的捆綁和命令集合在一塊,它還啟動嵌入式Jetty服務(wù)器并延伸com.yammer.dropwizard.Service。
import com.yammer.dropwizard.Service; import com.yammer.dropwizard.config.Bootstrap; import com.yammer.dropwizard.config.Environment;public class BlogService extends Service<BlogConfiguration> {public static void main(String[] args) throws Exception {new BlogService().run(new String[] { "server" });}@Overridepublic void initialize(Bootstrap<BlogConfiguration> bootstrap) {bootstrap.setName("blog");}@Overridepublic void run(BlogConfiguration configuration, Environment environment) throws Exception {}}上面的這些服務(wù)類可以:
第5步:寫IndexResource
寫一個當(dāng)GET請求指向“/” URL時會被調(diào)用的源,創(chuàng)建一個新的JAX-RS源(此資源將列出所有的博客),如下:
import java.util.Arrays; import java.util.List;import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType;import com.yammer.metrics.annotation.Timed;@Path("/") public class IndexResource {@GET@Produces(value = MediaType.APPLICATION_JSON)@Timedpublic List<Blog> index() {return Arrays.asList(new Blog("Day 12: OpenCV--Face Detection for Java Developers","https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers"));} }上面這段代碼是一個標(biāo)準(zhǔn)的JAX-RS資源類。它添加@ Path注釋和定義index()方法,這個index()會返回一個博客集合,這些博客將被轉(zhuǎn)換為JSON文檔。
上面提到IndexResource是用博客表示的。下面這段則表明該博客使用Hibernate驗(yàn)證器注解,以確保內(nèi)容是有效的。例如,使用@URL注釋,以確保只有合法的URL存儲在MongoDB數(shù)據(jù)庫。
import java.util.Date; import java.util.UUID;import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.URL;public class Blog {private String id = UUID.randomUUID().toString();@NotBlankprivate String title;@URL@NotBlankprivate String url;private final Date publishedOn = new Date();public Blog() {}public Blog(String title, String url) {super();this.title = title;this.url = url;}public String getId() {return id;}public String getTitle() {return title;}public String getUrl() {return url;}public Date getPublishedOn() {return publishedOn;} }接下來,在服務(wù)類的run方法注冊IndexResource。用下面的方式更新BlogService run方法。
@Override public void run(BlogConfiguration configuration, Environment environment) throws Exception {environment.addResource(new IndexResource()); }現(xiàn)在,可以將BlogService類??作為一個主程序來運(yùn)行(右鍵點(diǎn)擊>運(yùn)行方式> Java應(yīng)用程序),這將啟動嵌入式Jetty容器,我們可以看到程序在 http://localhost:8080/ 里運(yùn)行。
$ curl http://localhost:8080[{"id":"9bb43d53-5436-4dac-abaa-ac530c833df1","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384090975372}]現(xiàn)在可以通過點(diǎn)擊“指標(biāo)(Metrics)”檢查IndexResource的指標(biāo),該數(shù)據(jù)是可用的JSON格式。
"com.shekhar.blog.IndexResource" : {"index" : {"type" : "timer","duration" : {"unit" : "milliseconds","min" : 17.764,"max" : 17.764,"mean" : 17.764,"std_dev" : 0.0,"median" : 17.764,"p75" : 17.764,"p95" : 17.764,"p98" : 17.764,"p99" : 17.764,"p999" : 17.764},"rate" : {"unit" : "seconds","count" : 1,"mean" : 7.246537731991882E-4,"m1" : 2.290184897291144E-12,"m5" : 3.551918562683463E-5,"m15" : 2.445031498756583E-4}}},第6步:配置MongoDB
在pom.xml 里加入 mongo-jackson-mapper 的依賴。
<dependency><groupId>net.vz.mongodb.jackson</groupId><artifactId>mongo-jackson-mapper</artifactId><version>1.4.2</version> </dependency>用MongoDB數(shù)據(jù)庫的詳細(xì)信息(如主機(jī)、端口和數(shù)據(jù)庫名等)更新BlogConfiguration類。
import javax.validation.constraints.Max; import javax.validation.constraints.Min;import org.codehaus.jackson.annotate.JsonProperty; import org.hibernate.validator.constraints.NotEmpty;import com.yammer.dropwizard.config.Configuration;public class BlogConfiguration extends Configuration {@JsonProperty@NotEmptypublic String mongohost = "localhost";@JsonProperty@Min(1)@Max(65535)public int mongoport = 27017;@JsonProperty@NotEmptypublic String mongodb = "mydb"; }接下來,創(chuàng)建一個名為MongoManaged的新類,它將允許你在應(yīng)用程序啟動和停止時管理程序資源。這樣就實(shí)現(xiàn)了com.yammer.dropwizard.lifecycle.Managed。
import com.mongodb.Mongo; import com.yammer.dropwizard.lifecycle.Managed;public class MongoManaged implements Managed {private Mongo mongo;public MongoManaged(Mongo mongo) {this.mongo = mongo;}@Overridepublic void start() throws Exception {}@Overridepublic void stop() throws Exception {mongo.close();}}在上面的代碼中,關(guān)閉了stop方法中的MongoDB連接。
下一步,寫一個MongoHealthCheck來檢查MongoDB的連接與否。
import com.mongodb.Mongo; import com.yammer.metrics.core.HealthCheck;public class MongoHealthCheck extends HealthCheck {private Mongo mongo;protected MongoHealthCheck(Mongo mongo) {super("MongoDBHealthCheck");this.mongo = mongo;}@Overrideprotected Result check() throws Exception {mongo.getDatabaseNames();return Result.healthy();} }現(xiàn)在,更新BlogService類??,將MongoDB的配置包含進(jìn)來。
package com.shekhar.blog;import com.mongodb.Mongo; import com.yammer.dropwizard.Service; import com.yammer.dropwizard.config.Bootstrap; import com.yammer.dropwizard.config.Environment;public class BlogService extends Service<BlogConfiguration> {public static void main(String[] args) throws Exception {new BlogService().run(new String[] { "server" });}@Overridepublic void initialize(Bootstrap<BlogConfiguration> bootstrap) {bootstrap.setName("blog");}@Overridepublic void run(BlogConfiguration configuration, Environment environment) throws Exception {Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);MongoManaged mongoManaged = new MongoManaged(mongo);environment.manage(mongoManaged);environment.addHealthCheck(new MongoHealthCheck(mongo));environment.addResource(new IndexResource());} }上面這段代碼:
運(yùn)行該應(yīng)用程序作為主程序。你可以到本地的 http://localhost:8081/healthcheck 健康檢查頁面去檢驗(yàn)MongoDB是否在運(yùn)行,如果MongoDB沒有運(yùn)行,會看到一個異常堆棧跟蹤。
! MongoDBHealthCheck: ERROR ! can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admincom.mongodb.MongoException$Network: can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/adminat com.mongodb.DBTCPConnector.call(DBTCPConnector.java:227)at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:305)at com.mongodb.DB.command(DB.java:160)at com.mongodb.DB.command(DB.java:183)at com.mongodb.Mongo.getDatabaseNames(Mongo.java:327)at com.shekhar.blog.MongoHealthCheck.check(MongoHealthCheck.java:17)at com.yammer.metrics.core.HealthCheck.execute(HealthCheck.java:195)at Caused by: java.io.IOException: couldn't connect to [Shekhars-MacBook-Pro.local/192.168.1.101:27017] bc:java.net.ConnectException: Connection refusedat com.mongodb.DBPort._open(DBPort.java:228)at com.mongodb.DBPort.go(DBPort.java:112)at com.mongodb.DBPort.call(DBPort.java:79)at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:218)... 33 more* deadlocks: OK現(xiàn)在啟動MongoDB,可以看到:
* MongoDBHealthCheck: OK * deadlocks: OK第7步:創(chuàng)建BlogResource
現(xiàn)在寫B(tài)logResource類,它負(fù)責(zé)創(chuàng)建博客條目。
import java.util.ArrayList; import java.util.List;import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response;import net.vz.mongodb.jackson.DBCursor; import net.vz.mongodb.jackson.JacksonDBCollection;import com.yammer.metrics.annotation.Timed;@Path("/blogs") @Produces(value = MediaType.APPLICATION_JSON) @Consumes(value = MediaType.APPLICATION_JSON) public class BlogResource {private JacksonDBCollection<Blog, String> collection;public BlogResource(JacksonDBCollection<Blog, String> blogs) {this.collection = blogs;}@POST@Timedpublic Response publishNewBlog(@Valid Blog blog) {collection.insert(blog);return Response.noContent().build();} }下一步,更新BlogService run方法,將BlogResource也加進(jìn)來。
@Overridepublic void run(BlogConfiguration configuration, Environment environment) throws Exception {Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);MongoManaged mongoManaged = new MongoManaged(mongo);environment.manage(mongoManaged);environment.addHealthCheck(new MongoHealthCheck(mongo));DB db = mongo.getDB(configuration.mongodb);JacksonDBCollection<Blog, String> blogs = JacksonDBCollection.wrap(db.getCollection("blogs"), Blog.class, String.class);environment.addResource(new IndexResource());environment.addResource(new BlogResource(blogs));}將BlogService類作為一個Java應(yīng)用程序運(yùn)行。為了測試BlogResource,做一個curl請求:
$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers"}' http://localhost:8080/blogsHTTP/1.1 204 No Content Date: Sun, 10 Nov 2013 14:08:03 GMT Content-Type: application/json第8步:更新IndexResource
現(xiàn)在,更新IndexResource index()方法來從MongoDB獲取所有的博客文件。
import java.util.ArrayList; import java.util.List;import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType;import net.vz.mongodb.jackson.DBCursor; import net.vz.mongodb.jackson.JacksonDBCollection;import com.yammer.metrics.annotation.Timed;@Path("/") public class IndexResource {private JacksonDBCollection<Blog, String> collection;public IndexResource(JacksonDBCollection<Blog, String> blogs) {this.collection = blogs;}@GET@Produces(value = MediaType.APPLICATION_JSON)@Timedpublic List<Blog> index() {DBCursor<Blog> dbCursor = collection.find();List<Blog> blogs = new ArrayList<>();while (dbCursor.hasNext()) {Blog blog = dbCursor.next();blogs.add(blog);}return blogs;}}更新BlogService run方法將博客集合傳遞給IndexResource。
@Overridepublic void run(BlogConfiguration configuration, Environment environment) throws Exception {Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);MongoManaged mongoManaged = new MongoManaged(mongo);environment.manage(mongoManaged);environment.addHealthCheck(new MongoHealthCheck(mongo));DB db = mongo.getDB(configuration.mongodb);JacksonDBCollection<Blog, String> blogs = JacksonDBCollection.wrap(db.getCollection("blogs"), Blog.class, String.class);environment.addResource(new IndexResource(blogs));environment.addResource(new BlogResource(blogs));}將BlogService類作為一個Java應(yīng)用程序運(yùn)行。為了測試BlogResource,做一個curl請求:
$ curl http://localhost:8080[{"id":"527f9806300462bbd300687e","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384093702592}]第9步:部署到云端
這里有一篇文章,教你如何在OpenShift部署Dropwizard應(yīng)用,點(diǎn)擊這里。
今天就這些,歡迎反饋。
原文 Day 13: Dropwizard--The Awesome Java REST Server Stack
整理 SegmentFault
總結(jié)
以上是生活随笔為你收集整理的Day 13: Dropwizard —— 非常棒的Java REST服务器栈的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Day 12: OpenCV —— Ja
- 下一篇: Day14:使用斯坦福 NER 软件包实