我已经是一个使用了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库到一个嵌入式应用程序包。它由以下部分组成:

  1. 嵌入式Jetty:每一个应用程序被打包成一个jar(而不是war)文件,并开始自己的嵌入式Jetty容器。没有任何war文件和外部servlet容器。
  2. JAX-RS:Jersey(JAX-RS的参考实现)是用来写基于REST的Web服务的。
  3. JSON:REST服务用的是JSON,Jackson库用来做所有的JSON处理。
  4. 日志:使用Logback和SLF4J完成。
  5. Hibernate验证:Dropwizard使用Hibernate验证API进行声明性验证。
  6. 指标:Dropwizard支持监控使用标准库,它在监控代码方面有无与伦比的洞察力。

除了上面提到的这几个,Dropwizard还使用了一些其他的库,你可以在这里找到完整的列表。


为什么是Dropwizard?

我决定学Dropwizard的原因有以下几点:

  1. 快速的项目引导:如果你已经在使用Spring和Java EE,你就会明白开发人员在引导项目时的痛苦。使用Dropwizard,你只需要在你的 pom.xml 文件中添加一个依赖就完成了。
  2. 应用指标:Dropwizard自带应用程序指标的支持。它提供了类似请求/响应时间这种非常有用的信息,只要把@ 定时注解来获取方法的执行时间。
  3. 生产力:每个Dropwizard应用程序有一个启动Jetty容器的主程序。这意味着,完全可以把应用程序作为一个主程序在IDE中运行和调试。所以就没有重新编译或部署war文件。

Github库

今天的演示应用程序的代码在GitHub上有:day13-dropwizard-mongodb-demo-app。

必备条件

  1. 基础的Java知识是必须的;
  2. 下载并安装 MongoDB数据库;
  3. 安装最新版本的Java Development Kit (JDK),OpenJDK 7 或是 Oracle JDK 7 都可以,这篇文章中使用JDK 7;
  4. 去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 IdArtifact 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" });}@Overridepublic void initialize(Bootstrap<BlogConfiguration> bootstrap) {bootstrap.setName("blog");}@Overridepublic void run(BlogConfiguration configuration, Environment environment) throws Exception {}}

上面的这些服务类可以:

  1. 有一个作为服务入口点的main方法。在main方法里面,创建BlogService的实例,并调用run方法。我们将服务器命令作为参数传递,服务器命令将启动嵌入式Jetty服务器。
  2. 初始化方法在服务运行方法之前被调用。
  3. 接下来,服务运行时将调用它的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)@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"));}
}

上面这段代码是一个标准的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();@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;}
}

接下来,在服务类的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@NotEmptypublic String mongohost = "localhost";@JsonProperty@Min(1)@Max(65535)public int mongoport = 27017;@JsonProperty@NotEmptypublic 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;}@Overridepublic void start() throws Exception {}@Overridepublic 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;}@Overrideprotected 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" });}@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());}
}

上面这段代码:

  1. 使用BlogConfiguration对象创建了一个新的Mongo实例。
  2. 一个新的MongoManaged实例被创建并添加到环境中。
  3. 健康检查被添加。

运行该应用程序作为主程序。你可以到本地的 http://localhost:8081/healthcheck 健康检查页面去检验MongoDB是否在运行,如果MongoDB没有运行,会看到一个异常堆栈跟踪。

! 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

现在启动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@Timedpublic Response publishNewBlog(@Valid Blog blog) {collection.insert(blog);return Response.noContent().build();}
}

下一步,更新BlogService run方法,将BlogResource也加进来。

 @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应用程序运行。为了测试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

现在,更新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应用程序运行。为了测试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应用,点击这里。

今天就这些,欢迎反馈。


原文 Day 13: Dropwizard--The Awesome Java REST Server Stack
整理 SegmentFault

Day 13: Dropwizard —— 非常棒的Java REST服务器栈相关推荐

  1. LeetCode(13.罗马数字转整数) JAVA Hashmap

    LeetCode(13.罗马数字转整数) JAVA Hashmap 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D ...

  2. Java后端服务器点餐系统的部署+前端微信小程序开发(13)

    Java后端服务器点餐系统的部署+前端微信小程序开发(13) 编译运行小程序 1,本地调试 这和你上面java的运行要保持一致,如果是本地的java项目运行你就用 localhost 后台Java直接 ...

  3. 你应该知道的 7 个很棒的 Java 项目

    Java生态系统拥有庞大而多样化的开源项目,旨在满足几乎所有想象得到的需求.很容易错过其中一些伟大的项目.以下是七个开源Java项目,涵盖从全栈应用程序开发到微服务.Wasm和JVM替代方案的所有内容 ...

  4. ioGame 网络游戏服务器框架 (java)、java游戏服务器、netty 集群分步式的网络游戏服务器

    ioGame 国内首个基于蚂蚁金服 SOFABolt 的 java 网络游戏服务器框架:无锁异步化.事件驱动的架构设计 通过 ioGame 可以很容易的搭建出一个集群无中心节点.有状态多进程的分步式游 ...

  5. 在java的实现栈的插入数据_栈之Java实现数据结构

    一.什么是栈? 栈(stack)又叫先进后出表,它是一种运算受限的线性表.它只允许在表的一端进行插入和删除操作,我们称之为栈顶,相对另一端称为栈底. 我们可以通俗一点,将栈比喻为一个垃圾桶,垃圾桶底就 ...

  6. 我的世界java服核心下载_我的世界java版服务器

    我的世界java版服务器是由网易推出全自由的一款游戏,自由化的模式保证你可以在这里创建你喜欢的所有物品,多种玩法设计,而且还有大量的挑战等你来体验,在这个充满乐趣的世界当中,不断的完成你的冒险,享受有 ...

  7. Java堆和栈的基本理解

    Java 堆和栈的区别 参考背景: 堆内存:用来存放由new创建的对象和数组: 栈内存:存放基本类型的变量,对象的引用变量: 堆存放的原因:由于在堆中创建对象(或数组)后,可在栈中定义一个特殊变量,让 ...

  8. java云服务器系统选择,java云服务器系统选择

    java云服务器系统选择 内容精选 换一换 登录Windows操作系统弹性云服务器时,无法正常进入系统.自启动系统修复模式,但选择修复选项后报错,无法继续进行系统恢复.Windows文件已损坏.登录管 ...

  9. Java游戏服务器系列之Netty详解

    今天带大家来学习Java游戏服务器的相关知识,文中对Netty作了非常详细的介绍,对正在学习java的小伙伴们有很好的帮助,需要的朋友可以参考下 一.简介 Java的底层API逐渐复杂,而开发者面对的 ...

最新文章

  1. c++ 虚函数_到底什么情况下会合成默认构造函数?
  2. RxJava初入学习(一)之Gifts-for-designers
  3. 建站四部曲之前端显示篇(React+上线)
  4. 电商网站全链路压测实战
  5. 媒体层图形技术之AssetsLibrary 学习笔记
  6. 字体设计师必备灵感来源
  7. 你就要清楚的知道,自己日后的“盈利模式”是什么?
  8. php版本越高越好么,php版本越高越好吗
  9. 亚马逊AI惹众怒:一个没有意识的程序,竟然自己学会了“重男轻女”
  10. devexpress html编辑器,DevExpress 通用控件系列:TextEdit(2)
  11. OpenCV c接口与c++接口
  12. 获取包名列表android,更多获取app包名方法
  13. 一个icsp header和复位按钮。_手动火灾报警按钮的设置要求及检查方法
  14. 使用flutter控制蓝牙通讯_使用Flutter控制蓝牙通讯
  15. 护眼仪眼部按摩器单芯片蓝牙方案开发说明
  16. 【POJ3683】Priest John's Busiest Day(Special Judge)
  17. 移动端适配:font-size设置方案的理解(浏览器调试移动端网页工具使用)
  18. Java 常用工具类 - 校验身份证 IdCardUtils
  19. [W ParallelNative.cpp:212] Warning: Cannot set number of intraop threads after parallel work h
  20. 祖母绿canutillos宝石

热门文章

  1. 机器学习入门系列二(关键词:多变量(非)线性回归,批处理,特征缩放,正规方程
  2. 2015年微软亚洲研究院的惊艳项目,人工智能抢眼
  3. 食品安全溯源区块链解决方案探索-转载
  4. Java8 - 使用CompletableFuture 构建异步应用
  5. form 多个submit php,一个复杂的PHP表单处理方案?
  6. JavaScript开发优化技巧
  7. 混沌系统 matlab仿真分析
  8. 2021-02-28 LQG控制的主动悬架1/2车垂向动力学模型
  9. 主题图标_Avada主题网站favicon图标设置详细图文教程
  10. block才会执行 mono_C-BLOCK录制《我要上春晚》,目测会上湖南分会场