非常棒的Java REST服务器栈
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支持监控使用标准库,它在监控代码方面有无与伦比的洞察力。
1
、配置maven以导入jar包。
<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>
</project>
2
、创建配置类
每个Dropwizard应用程序都有一个配置类,它指定特定的环境参数。文章后面会将如主机、端口和数据库名之类的MongoDB的配置参数添加给它。这个类扩展了 com.yammer.dropwizard.config.Configuration类。
import
com.yammer.dropwizard.config.Configuration;
public
class
BlogConfiguration
extends
Configuration{
}
3
、创建服务类
该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源加到这个方法里。
4
、写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
}]
The administrative
interface
is available at http:
//localhost:8081/.
现在可以通过点击“指标(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
}
}
},
5
、配置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
6
、创建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
7
、更新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
}]
8
、部署到云端
这里有一篇文章,教你如何在OpenShift部署Dropwizard应用,打开http:
//dmly.github.io/blog/2013/05/01/diy-java-app-server-on-openshift-so-far-so-good/。
非常棒的Java REST服务器栈的更多相关文章
- [转]Java工程师技术栈--成神之路
一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http://www.jcp.org/en/jsr/detail?id=133http://if ...
- Java堆、栈和常量池以及相关String的详细讲解(经典中的经典) (转)
原文链接 : http://www.cnblogs.com/xiohao/p/4296088.html 一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的 ...
- 一个简单的Java web服务器实现
前言 一个简单的Java web服务器实现,比较简单,基于java.net.Socket和java.net.ServerSocket实现: 程序执行步骤 创建一个ServerSocket对象: 调用S ...
- Java堆、栈和常量池
摘录自 http://www.cnblogs.com/xiohao/p/4296088.html 1. 栈(stack)与堆(heap)都是Java用来在RAM中存放数据的地方.与C++不同,Java ...
- Java堆、栈和常量池以及相关String的详细讲解
一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部.但是寄存器的数量极其有限,所以寄存器由编译器根据 ...
- Java堆和栈详解
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...
- java堆、栈、堆栈的区别
1.栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. 2. 栈的优势是,存取速度比堆要快,仅次于直接位于CP ...
- Java从服务器上获取时间,动态在jsp页面显示
Java获取服务器时间,动态显示到jsp页面,大家都是到Java只能获取一次,到页面的时间是静态的,不过通过js和Java的合作,巧妙地实现此功能 本人是给电视做系统,客户要求页面能显示时间,因为电视 ...
- 常用Java Web 服务器
Java Web应用程序需要部署在Java web服务器中运行,常用的Java Web服务器有Tomcat.GlassFish.WebLogic.JBoss.WebSphere.Jetty.JRun等 ...
随机推荐
- Divide Two Integers 解答
Question Divide two integers without using multiplication, division and mod operator. If it is overf ...
- Python 协程(gevent)
协程,又叫微线程,协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈.因此: 协程能保留上 ...
- RESTEasy 3.X Helloworld
最近呢,RESTEasy也升级了.升到了3.X. 官网:http://www.jboss.org/resteasy 集成使用也非常简单(相比SOAP而言) 第一步:下载jar包 resteasy是托管 ...
- 向html某个元素中添加信息
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...
- EXPDP和IMPDP简单测试
一.EXPDP和IMPDP使用说明 Oracle Database 10g引入了最新的数据泵(Data Dump)技术,数据泵导出导入(EXPDP和IMPDP)的作用 1)实现逻辑备份和逻辑恢复. ...
- EffectiveC#01--避免返回内部类对象的引用
此篇是对00中第3点的再一次阐述. 1.如果一个属性返回一个引用类型,那么调用者就可以访问这个对象的公共成员,也包括修改这些属性的状态. public class MyBusinessObject { ...
- bower安装使用以及git安装
bower需要:node 和 git node安装包下载:http://blog.csdn.net/myan/article/details/2028545 Git安装: 选择第二项:Use Git ...
- JS判断RadioButtonList是否有选中项
提交表单之前对RadioButtonList控件的选中项进行判断: 方法一: <script type="text/javascript"> function chec ...
- Android 定义重名权限问题
一直以来对android的权限机制就有一个疑问,因为在使用权限时,实际上只需要permission的name这一个标签,而在定义权限时,android是不会检查是否重名的,那么在两个应用定义了重名权限 ...
- Android Service(下)
转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...