天天看點

DropWizard--輕量級REST開發架構初次搭建

之前用的IDE是spring的STS,這次還是選它;

Maven來導入dropwizard及管理項目;

這裡的例子完全是按照dropwizard官網 入門執行個體來做的,官網講的比較詳細和規範,建議英文好的童鞋可以不用看這篇,去官網看一下。

一、

首先打開我的IDE,并建構maven項目,官網上說了建構的三種可替換方式,我這裡用了我自己的建構方式,

建立一個maven project,選擇 add Archetype,依次Group id、Artifact id及version。

DropWizard--輕量級REST開發架構初次搭建

點選确認後在maven的archetype中多了一個上述的類型。

二、

ok,選擇上述的archetype,依次填入你要建構的項目的groupId(我的是com.example)、artifactId(我的helloworld)以及項目name(我的是HelloWorld,這個名字會是分别加上appliacation和configuration的你的啟動類和配置類的名字 ,如下圖中的HelloWorldApplication和HelloWorldConfiguration兩個類),随即導入一個新的dropwizard maven管理的項目,大體結構如下:

DropWizard--輕量級REST開發架構初次搭建

其實maven管理的優點在于:你導入dropwizard的archetype後,整個項目的結構及pom檔案中所依賴的package及plugins都不用你自己去添加了,真的省了好多事情,是以和springboot一樣被叫做輕量級架構嘛。

三、配置pom.xml

官網上詳細講述了pom.xml的配置,但是這裡pom的所有配置從你建構maven應用後都自動給你配置好了,當然如果你不滿意可以自行填删,這裡根據官網的介紹講一下pom的幾個組成及作用:

<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <prerequisites>
        <maven>3.0.0</maven>
    </prerequisites>

    <groupId>com.example</groupId>
    <artifactId>helloworld</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>HelloWorld</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <dropwizard.version>1.0.0</dropwizard.version> //版本号後邊會引用
        <mainClass>com.example.helloworld.HelloWorldApplication</mainClass> //這個mainClass後邊會引用
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.dropwizard</groupId>
                <artifactId>dropwizard-bom</artifactId>
                <version>${dropwizard.version}</version>//引用
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>//導入dropwizard的核心部分
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-core</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            //正常導出的是war檔案,這裡為了導出jar檔案(fat jar),需要添加這個plugin。
            <plugin>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
 //建立一個無依賴的pom檔案,Produce a pom.xml file which doesn’t include dependencies for the libraries whose contents are included in the fat JAR.                  <createDependencyReducedPom>true</createDependencyReducedPom> 

                     //将mainClass設定為JAR的mainClass,可以對jar檔案運作java -jar
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>${mainClass}</mainClass>
                        </transformer>
                    </transformers>
                    <!-- exclude signed Manifests -->
                    <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>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>${mainClass}</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            //将自動配置的compiler plugin部分去掉,編譯的時候會報錯,原因可能是本地編譯器版本和該plugin不一緻,這部分不需要
            <!-- <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin> -->
            <plugin>
                <artifactId>maven-source-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.10.3</version>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <reporting>
        <plugins>
            <plugin>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>2.8.1</version>
                <configuration>
                    <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
                    <dependencyDetailsEnabled>false</dependencyDetailsEnabled>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.10.3</version>
            </plugin>
        </plugins>
    </reporting>
</project>
           

以上的pom檔案是建構dropwizard project自動生成的,約定大于配置,但需删掉compiler的plugin,其餘的保留即可。

四、開始例子,

例子大緻是提供一個輸出模闆,格式是 ‘Hello, %s’,假如request中沒有name這個parameter時,預設輸出‘Hello, Stranger’。假如request中有name=fujian這種時,輸出‘Hello,fujian’。以restful的形式發送請求,傳回json格式資料。

首先,我們需要一個class類,它是對最終輸入結果的封裝,輸出結果為 “id”:1,”content”: “hello,stranger”,是以建構一個saying class,這裡是放到api這個package目錄下的:

package com.example.helloworld.api;

import org.hibernate.validator.constraints.Length;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Saying {

    private long id;

    //規定内容最大長度3
    @Length(max = )
    private String content;

    public Saying() {

    }

    public Saying(long id, String content) {
        this.id = id;
        this.content = content;
    }
    //jsonproperty注釋可以将實體類映射到json格式資料,将兩者關聯起來
    @JsonProperty
    public long getId() {
        return id;
    }

    @JsonProperty
    public String getContent() {
        return content;
    }
}
           

ok,上邊的封裝類有了,因為dropwizard是個輕量級的rest架構,下邊就開始寫request過來對應的資源了,在resources這個package目錄下,

package com.example.helloworld.resources;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
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 com.codahale.metrics.annotation.Timed;
import com.example.helloworld.api.Saying;

//javax.ws.rs.*屬于java擴充類的restful library
//@Path注釋映射request請求的url
@Path("/hello-world")
//@Produces說明輸出的為json格式資料
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {

    private final String template;
    private final String defaultName;
    private final AtomicLong counter;

    //這裡構造該resource類時傳入template和defaultName,後邊會講到是從configuration中拿到這兩個參數的
    public HelloWorldResource(String template, String defaultName) {
        this.template = template;
        this.defaultName = defaultName;
        this.counter = new AtomicLong();
    }

    //@GET說明是個GET請求,@Timed說明從接受請求到傳回結果是有時間限制的,具體可以自行查找
    @GET
    @Timed
    public Saying sayHello(@QueryParam("name") Optional<String> name) {
        final String value = String.format(template, name.orElse(defaultName));//name是個Optional,可有可無,無時使用defaultName
        return new Saying(counter.incrementAndGet(), value);//每次請求傳回一個id唯一的json data
    }
}
           

上邊的resource class 有了,按照dropwizard的風格,需要将其在HelloWorldApplication的run方法中注冊。

@Override
public void run(HelloWorldConfiguration configuration,
                Environment environment) {
    final HelloWorldResource resource = new HelloWorldResource(
        configuration.getTemplate(),
        configuration.getDefaultName()
    );
    environment.jersey().register(resource);
}
           

ok,可以看到執行個體化resource時,從configuration中取得template和defaultName,那麼就看一下configuration這個類吧:

package com.example.helloworld;

import io.dropwizard.Configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.*;
import javax.validation.constraints.*;

public class HelloWorldConfiguration extends Configuration {

    @NotEmpty
    private String template;

    @NotEmpty
    private String defaultName =  "Stranger";

    //jsonproperty注釋主要是為了和json格式資料映射起來
    @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 defaultName) {
        this.defaultName = defaultName;
    }
}
           

這個配置類主要把json格式的template和defaultName映射起來,set方法是從json檔案中提取相關部分到該類。這裡有一點需要注意:該應用用maven打包成jar檔案後,與該類相映射的json檔案(hello-world.yml)與jar檔案放到同一目錄下,這樣就能将yml中的json資料映射進來了,hello-world.yml如下:

template: hello %s!
defaultName: stranger
           

以上是針對resource資源的請求,官網的例子中還設計到了template這個模闆合法性的檢查,一并介紹下:

這個TemplateHealthCheck 類是在health package下的。

package com.example.helloworld.health;

import com.codahale.metrics.health.HealthCheck;

//繼承HealthCheck抽象類,需實作check()抽象方法
public class TemplateHealthCheck  extends HealthCheck {

    private final String template;

    public TemplateHealthCheck(String template) {
        this.template = template;
    }
    @Override
    protected Result check() throws Exception {
        final String  saying = String.format(template, "TEST");
        //這裡實際上檢查了兩點:1)TEST是否能寫入template,即模闆的建立性是否合理;2)是否包括TEST,即是否能正确輸出含有目的name的template
        if(!saying.contains("TEST")) {
            return Result.unhealthy("template doesn't contain a name");

        }
        return Result.healthy();
    }

}
           

當然與resource一樣,需要注冊到Application的run方法中:

這裡與resource的注冊一并寫出來:

package com.example.helloworld;

import com.example.helloworld.health.TemplateHealthCheck;
import com.example.helloworld.resources.HelloWorldResource;

import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;

public class HelloWorldApplication extends Application<HelloWorldConfiguration> {

    public static void main(final String[] args) throws Exception {
        new HelloWorldApplication().run(args);
    }

    @Override
    public String getName() {
        return "HelloWorld";
    }

    @Override
    public void initialize(final Bootstrap<HelloWorldConfiguration> bootstrap) {
        // TODO: application initialization
    }

    @Override
    public void run(HelloWorldConfiguration configuration,
                    Environment environment) {
        final HelloWorldResource resource = new HelloWorldResource(
                configuration.getTemplate(), 
                configuration.getDefaultName()
                );
        final TemplateHealthCheck healthCheck = 
                new TemplateHealthCheck(configuration.getTemplate());
        environment.healthChecks().register("template", healthCheck);
        environment.jersey().register(resource);
    }

}
           

ok,梳理下,上邊主要分成兩個部分的建構:

1)resource類的建構:過來的類似‘/hello-world?name=fujian’請求,傳回json格式的資料;

2)healthCheck會校驗yml檔案中template的合法性

五、maven打包及本地運作

DropWizard--輕量級REST開發架構初次搭建

在該應用的本地目錄下target目錄中會有一個打包jar檔案,同目錄下還有hello-world.yml,

DropWizard--輕量級REST開發架構初次搭建

在target目錄下打開本地指令行,

輸入

java -jar helloworld--SNAPSHOT.jar server hello-world.yml
           

即可啟動内嵌jetty 應用伺服器,并可在8080端口通路/hello-world?name=fujian or /hello-world;在8081端口打開管理端部分,運作healthCheck

到此結束。這是架構的起步,官網 還介紹了在架構基礎上內建一些重要的功能,如JDBI、hibernate、authentic等,感興趣的可以去官網學習

繼續閱讀