- Day 1: Bower - 管理你的客户端依赖关系
- Day 2: AngularJS - 对 AngularJS 的初步认识
- Day 3: Flask - 使用 Python 和 OpenShift 进行即时 Web 开发
- Day 4:PredictionIO - 如何创建一个博客推荐器
- Day 5: GruntJS - 重复乏味的工作总会有人做(反正我不做)
- Day 6:在 Java 虚拟机上使用 Grails 进行快速 Web 开发
- Day 7: GruntJS 在线重载 提升生产率至新境界
- Day 8: Harp.JS - 现代静态 web 服务器
- Day 9: TextBlob - 对文本进行情感分析
- Day 10: PhoneGap - 开发手机应用如此简单
- Day 11: AeroGear 推送服务器:使应用的通知推送变得简单
- Day 12: OpenCV - Java 开发者的人脸检测
- Day 13: Dropwizard - 非常棒的 Java REST 服务器栈
- Day14:使用斯坦福 NER 软件包实现你自己的命名实体识别器(Named Entity Recognition,NER)
- Day 15:Meteor - 从零开始创建一个 Web 应用
- Day 16: Goose Extractor - 好用的文章提取工具
- Day 17: 使用 JBoss Forge 和 OpenShift 构建部署 JAVA EE 6 应用
- Day 18: BoilerPipe - Java 开发者的文章提取工具
- Day 19: EmberJS 入门指南
- Day 20: 斯坦福 CoreNLP - 用 Java 给 Twitter 进行情感分析
- Day 21:Docker 入门教程
- Day 22: 使用 Spring、MongoDB 和 AngularJS 开发单页面应用
- Day 23:使用 TimelineJS 构建精美的时间轴
- Day 24: 使用 Yeoman 自动构建 Ember 项目
- Day 25: 联合 Tornado、MongoDB 和 AngularJS 进行应用开发
- Day 26: TogetherJS - 让我们一起来编程!
- Day 27: Restify - 在 Node.js 中构建正确的 REST Web 服务
- Day 28: OpenShift 的 Eclipse 集成
- Day 29:编写你的第一个 Google Chrome 扩展程序
- Day 30: Play Framework - Java 开发者的梦想框架
Day 13: Dropwizard - 非常棒的 Java REST 服务器栈
-
我已经是一个使用了 8 年 Java 的软件开发人员了,我写过的大多数应用程序是用的 Spring 框架或 Java EE。最近,我花了一些时间学习用 Python 进行 web 开发,其中印象非常深刻的是 Flask 框架 - 一个微型架构,这使得它很容易写 REST 后端。所以今天我决定找一个 Java 的 Python Flask 框架替代品,做一些研究后,我发现 Dropwizard 框架可以帮助达到 Flask 框架同样的生产力。在这篇博客中,我们将学习如何使用 Dropwizard 构建一个基于 REST 的 Java MongoDB 应用程序。
什么是 Dropwizard?
Dropwizard 是一个开源的 Java 框架,用于开发 OPS 友好、高性能的基于 REST 的后端。它是由 Yammer 开发的,来驱动基于 JVM 的后端。
Dropwizard 提供同类最佳的 Java 库到一个嵌入式应用程序包。它由以下部分组成:
- 嵌入式 Jetty:每一个应用程序被打包成一个 jar(而不是 war)文件,并开始自己的嵌入式 Jetty 容器。没有任何 war 文件和外部 servlet 容器。
- JAX-RS:Jersey(JAX-RS 的参考实现)是用来写基于 REST 的 Web 服务的。
- JSON:REST 服务用的是 JSON,Jackson 库用来做所有的 JSON 处理。
- 日志:使用 Logback 和 SLF4J 完成。
- Hibernate 验证:Dropwizard 使用 Hibernate 验证 API 进行声明性验证。
- 指标:Dropwizard 支持监控使用标准库,它在监控代码方面有无与伦比的洞察力。
除了上面提到的这几个,Dropwizard 还使用了一些其他的库,你可以在 这里找到完整的列表 。
为什么是 Dropwizard?
我决定学 Dropwizard 的原因有以下几点:
- 快速的项目引导:如果你已经在使用 Spring 和 Java EE,你就会明白开发人员在引导项目时的痛苦。使用 Dropwizard,你只需要在你的
pom.xml
文件中添加一个依赖就完成了。 - 应用指标:Dropwizard 自带应用程序指标的支持。它提供了类似
请求/响应时间
这种非常有用的信息,只要把 @ 定时注解来获取方法的执行时间。 - 生产力:每个 Dropwizard 应用程序有一个启动 Jetty 容器的主程序。这意味着,完全可以把应用程序作为一个主程序在 IDE 中运行和调试。所以就没有重新编译或部署 war 文件。
Github 库
今天的演示应用程序的代码在 GitHub 上有: day13-dropwizard-mongodb-demo-app 。
必备条件
- 基础的 Java 知识是必须的;
- 下载并安装 MongoDB 数据库 ;
- 安装最新版本的 Java Development Kit (JDK), OpenJDK 7 或是 Oracle JDK 7 都可以,这篇文章中使用 JDK 7;
- 去 Eclipse 官网 下载最新版本的 Eclipse 包,就目前而言 eclipse 最新版的代号是 Kepler;
Eclipse 的安装很容易,只需要解压下载下来的包即可。如果是在 Linux 或者 Mac 机器上,开个命令行窗口,输入如下命令:
$ tar -xzvf eclipse-jee-kepler-R-*.tar.gz
Windows 下,你解压到哪里,那里就会有一个 eclipse 文件夹,这样就可以直接操作了,当然你也可以创建执行文件的快捷方式到桌面。
第 1 步:创建一个新的 Maven 项目
打开 Eclipse IDE,然后到项目工作区(project workspace)。要创建一个新的项目,转到 文件>新建> Maven 项目 (File > New > Maven Project)
,然后选择 Maven 原型 - 快速启动 (maven-archetype-quickstart)
,然后进入 Ground Id
和 Artifact Id
,最后点击“完成”。
第 2 步:更新 pom.xml
现在更新 pom.xml
文件以包括 dropwizard 核心 maven 依赖。同时也将更新 Maven 项目使用 Java 1.7 版本,更新 pom.xml
文件后,更新 Maven 项目 (右键单击>Maven>更新项目)
。
<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 步:创建配置类
每个 Dropwizard 应用程序都有一个配置类,它指定特定的环境参数。文章后面会将如主机、端口和数据库名之类的 MongoDB 的配置参数添加给它。这个类扩展了 com.yammer.dropwizard.config.Configuration
类。
import com.yammer.dropwizard.config.Configuration;
public class BlogConfiguration extends Configuration{
}
第 4 步:创建服务类
该 Dropwizard 项目由一个服务类自举。这个类将各种提供基本功能的捆绑和命令集合在一块,它还启动嵌入式 Jetty 服务器并延伸 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" });
}
@Override
public void initialize(Bootstrap<BlogConfiguration> bootstrap) {
bootstrap.setName("blog");
}
@Override
public void run(BlogConfiguration configuration, Environment environment) throws Exception {
}
}
上面的这些服务类可以:
- 有一个作为服务入口点的 main 方法。在 main 方法里面,创建 BlogService 的实例,并调用 run 方法。我们将服务器命令作为参数传递,服务器命令将启动嵌入式 Jetty 服务器。
- 初始化方法在服务运行方法之前被调用。
- 接下来,服务运行时将调用它的 run 方法,文章后面会将 JAX-RS 源加到这个方法里。
第 5 步:写 IndexResource
写一个当 GET 请求指向“/” URL 时会被调用的源,创建一个新的 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)
@Timed
public 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"));
}
}
上面这段代码是一个标准的 JAX-RS 资源类。它添加 @ Path
注释和定义 index()
方法,这个 index() 会返回一个博客集合,这些博客将被转换为 JSON 文档。
上面提到 IndexResource 是用博客表示的。下面这段则表明该博客使用 Hibernate 验证器注解,以确保内容是有效的。例如,使用 @URL 注释,以确保只有合法的 URL 存储在 MongoDB 数据库。
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();
@NotBlank
private String title;
@URL
@NotBlank
private 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;
}
}
接下来,在服务类的 run 方法注册 IndexResource。用下面的方式更新 BlogService run 方法。
@Override
public void run(BlogConfiguration configuration, Environment environment) throws Exception {
environment.addResource(new IndexResource());
}
现在,可以将 BlogService 类作为一个主程序来运行 (右键点击>运行方式> Java 应用程序)
,这将启动嵌入式 Jetty 容器,我们可以看到程序在 http://localhost:8080/
里运行。
$ 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}]
现在可以通过点击 “指标(Metrics)”
检查 IndexResource 的指标,该数据是可用的 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 数据库的详细信息(如主机、端口和数据库名等)更新 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
@NotEmpty
public String mongohost = "localhost";
@JsonProperty
@Min(1)
@Max(65535)
public int mongoport = 27017;
@JsonProperty
@NotEmpty
public String mongodb = "mydb";
}
接下来,创建一个名为 MongoManaged
的新类,它将允许你在应用程序启动和停止时管理程序资源。这样就实现了 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;
}
@Override
public void start() throws Exception {
}
@Override
public void stop() throws Exception {
mongo.close();
}
}
在上面的代码中,关闭了 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;
}
@Override
protected Result check() throws Exception {
mongo.getDatabaseNames();
return Result.healthy();
}
}
现在,更新 BlogService 类,将 MongoDB 的配置包含进来。
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" });
}
@Override
public void initialize(Bootstrap<BlogConfiguration> bootstrap) {
bootstrap.setName("blog");
}
@Override
public 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());
}
}
上面这段代码:
- 使用 BlogConfiguration 对象创建了一个新的 Mongo 实例。
- 一个新的 MongoManaged 实例被创建并添加到环境中。
- 健康检查被添加。
运行该应用程序作为主程序。你可以到本地的 http://localhost:8081/healthcheck
健康检查页面去检验 MongoDB 是否在运行,如果 MongoDB 没有运行,会看到一个异常堆栈跟踪。
! MongoDBHealthCheck: ERROR
! can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin
com.mongodb.MongoException$Network: can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin
at 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 refused
at 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
现在启动 MongoDB,可以看到:
* MongoDBHealthCheck: OK
* deadlocks: OK
第 7 步:创建 BlogResource
现在写 BlogResource 类,它负责创建博客条目。
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
@Timed
public Response publishNewBlog(@Valid Blog blog) {
collection.insert(blog);
return Response.noContent().build();
}
}
下一步,更新 BlogService run 方法,将 BlogResource 也加进来。
@Override
public 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 应用程序运行。为了测试 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/blogs
HTTP/1.1 204 No Content
Date: Sun, 10 Nov 2013 14:08:03 GMT
Content-Type: application/json
第 8 步:更新 IndexResource
现在,更新 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)
@Timed
public 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。
@Override
public 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 应用程序运行。为了测试 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 应用,点击 这里 。
今天就这些,欢迎反馈。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论