• Dropwizard框架入门


    最近项目用到了Dropwizard框架,个人感觉还不错,那么这里就从他们官网入手,然后加上自己的实现步骤让大家初步了解这个框架。

    官网对DW(Dropwizard)的定义是跨越了一个库和框架之间的界限。他的目标是提供一个生产就绪的web应用程序所需的一切性能可靠的实现。那么这句话可能有些绕,我个人理解就是他能免去我们部署web应用的很多步骤。由于这个功能被提取到可以重复使用的库中,我们的应用程序保持很大程度的精简和集中,这样可以减少我们程序的上线时间和维护负担。

    Jetty for HTTP

    由于Web应用不可能缺少HTTP,DW使用Jetty Http库将一个非常棒的HTTP服务器嵌入到我们的项目中。DW不是将你的程序提交到复杂的服务器上,DW上有个main方法来启动我们的服务器,DW是将我们的应用作为一个简单的线程来跑,消去了Java生产环境中一些非常复杂令人讨厌的过程,并且允许我们使用所有现有的Unix进程管理工具。

    Jersey for REST

    为了定义Restful的web应用,我们发现在性能和特性方面没有什么能比得过Jersey。它允许你编写干净的,可以测试的类,这个类可以优雅的将http请求映射成为简单的Java对象。它支持流输出,矩阵URL参数,条件GET请求,还有更多。

    Jackson for JSON

    在数据格式方面,JSON已经成为了网络的通用语,Jackson在jvm中就是Json的龙头老大。除了像闪电一样快速,他有一个复杂的对象映射,允许你直接导出你的域模型。

    Metrics for metrics

    Metrics库对事物进行舍入,在你的生产环境中,为你提供独一无二的洞察力。(也就是说这个是用来监控)那么到了这里,我们关于DW的总体印象应该已经差不多了,下面我结合官网实际操作。

    我使用maven和idea进行开发,项目名字为:dw_demo。关于如何创建maven项目不解释,创建完项目后如图所示:

    然后打开我们的pom.xml文件,加入dw的依赖(以下并非完全pom文件,仅展现部分):

    <properties>
        <dropwizard.version>0.9.2</dropwizard.version>
    </properties>
    <dependencies>
    <dependency>
        <groupId>io.dropwizard</groupId>
        <artifactId>dropwizard-core</artifactId>
        <version>${dropwizard.version}</version>
    </dependency>
    </dependencies>

    Creating A Configuration Class

     
    每个DW应用都有他自己的子类:Configuration,这个类指定环境中特定的参数。这
    些参数在YAML类型的配置文件中被指定,其被反序列化为应用程序配置类的实例并验
    证。(这句话的意思就是这个配置文件中指定的参数,会被映射到我们项目的一个类)
    我们将要构建的是一个helloworld高性能服务。我们的一个要求就是我们需要能够在不同
    的环境中让它说hello。在开始之前我们需要指定至少两个内容:一个说hello的模板 还有
    一个默认的名字以防用户忘记指定。
     
    那么我下面开始创建我的配置文件:
     
     

    内容如下:

    package com.config;
    
    import com.fasterxml.jackson.annotation.JsonProperty;
    import io.dropwizard.Configuration;
    import org.hibernate.validator.constraints.NotEmpty;
    
    /**
     * Created by moon on 2017/1/13.
     */
    public class HelloWorldConfiguration extends Configuration {
        @NotEmpty
        private String template;
    
        @NotEmpty
        private String defaultName = "Stranger";
    
        @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 name) {
            this.defaultName = name;
        }
    }

    当这个类被从YAML配置文件反序列化的时候,他会从YAML对象中获取两个根层次的变量:template 用来说helloworld的模板。defaultName 默认的名字。template和defaultName都用@NotEmpty被注释,所以在YAML配置文件中如果有空值或者忘了其中一者,异常将会被抛出,我们的应用将不会被启动。

    defaultName和template的get 和set 方法都被@JsonProperty标注,这不止允许jackson从YAML配置文件反序列化,同样允许它序列化。

    然后我们创建一个YAML的配置文件:

    里面的内容如下:

    template: Hello, %s!
    defaultName: Stranger

    大家可以看到,与我们的配置类中的变量一一对应,相信很多人看到这里就明白了。

    Creating An Application Class

    结合我们项目中的Configuration子类,我们的Application的子类形成了我们DW的应用的核心。Application的子类把不同的提供各式各样功能的包和命令拉取到了一起。

    现在,我们开始建立我们的Application子类:

    其内容如下:

    package com.app;
    
    import com.config.HelloWorldConfiguration;
    import io.dropwizard.Application;
    import io.dropwizard.setup.Bootstrap;
    import io.dropwizard.setup.Environment;
    
    /**
     * Created by moon on 2017/1/13.
     */
    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方法有点无趣,让我们开始丰富它。

    Creating A Representation Class

    在我们开始继续我们的程序之前,我们需要停下来思考一下我们程序的API。幸运的是,我们的应用需要符合行业标准。RFC 1149(别问我这是什么,连这个都不知道,我tm也不知道)。他指定了helloworld saying的如下json表达形式:

    {
      "id": 1,
      "content": "Hi!"
    }

    id字段是语法的唯一标识符。content是说的具体内容。

    为了建模这个表示,我们需要创建一个表示类 :

    该类的内容如下:

    package com.api;
    
    import com.fasterxml.jackson.annotation.JsonProperty;
    import org.hibernate.validator.constraints.Length;
    
    /**
     * Created by moon on 2017/1/13.
     */
    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。

    Creating A Resource Class

    Jersey资源是DW应用程序的肉和土豆(这种比喻我也是醉了)。每个资源类都与URL相关联(这个很重要,后面有说)。对于我们的应用程序来说,我们需要一个resources来通过url:/helloworld来返回新的Saying实例对象。

    现在我们开始建立的Resource:

    类的内容如下:

    package com.resource;
    
    import com.api.Saying;
    import com.codahale.metrics.annotation.Timed;
    import com.google.common.base.Optional;
    
    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;
    
    /**
     * Created by moon on 2017/1/13.
     */
    @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。

    Registering A 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.

    在pom文件中加入:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <createDependencyReducedPom>true</createDependencyReducedPom>
                    <filters>
                        <filter>
                            <artifact>*:*</artifact>
                            <excludes>
                                <exclude>META-INF/*.SF</exclude>
                                <exclude>META-INF/*.DSA</exclude>
                                <exclude>META-INF/*.RSA</exclude>
                            </excludes>
                        </filter>
                    </filters>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
           implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                                <transformer
            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.app.HelloWorldApplication</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
    
        </plugins>
    </build>

    然后打包:

    打包成功后,在我们的target目录下面会出现我们所需的包:

    然后我们开始运行:

    这里面官方为我们提供两个参数,我们需要启动服务,所以后面加入server参数,重新启动如下:

    这说明我们的项目已经启动了,那么让我们访问一下url看是否正确:



    返回结果正常,没毛病。

    以上仅仅是DW的初步,还有许多其他功能,由于时间关系,不做详细介绍,如果

    有时间我会再奉上一版深度版的。链接为:

    DW官网深层次内容

    希望这次的讲解对大家有帮助,感谢开源。

  • 相关阅读:
    (转载)SAPI 包含sphelper.h编译错误解决方案
    C++11标准的智能指针、野指针、内存泄露的理解(日后还会补充,先浅谈自己的理解)
    504. Base 7(LeetCode)
    242. Valid Anagram(LeetCode)
    169. Majority Element(LeetCode)
    100. Same Tree(LeetCode)
    171. Excel Sheet Column Number(LeetCode)
    168. Excel Sheet Column Title(LeetCode)
    122.Best Time to Buy and Sell Stock II(LeetCode)
    404. Sum of Left Leaves(LeetCode)
  • 原文地址:https://www.cnblogs.com/pangguoming/p/6855180.html
Copyright © 2020-2023  润新知