之前用的IDE是spring的STS,這次還是選它;
Maven來導入dropwizard及管理項目;
這裡的例子完全是按照dropwizard官網 入門執行個體來做的,官網講的比較詳細和規範,建議英文好的童鞋可以不用看這篇,去官網看一下。
一、
首先打開我的IDE,并建構maven項目,官網上說了建構的三種可替換方式,我這裡用了我自己的建構方式,
建立一個maven project,選擇 add Archetype,依次Group id、Artifact id及version。
點選确認後在maven的archetype中多了一個上述的類型。
二、
ok,選擇上述的archetype,依次填入你要建構的項目的groupId(我的是com.example)、artifactId(我的helloworld)以及項目name(我的是HelloWorld,這個名字會是分别加上appliacation和configuration的你的啟動類和配置類的名字 ,如下圖中的HelloWorldApplication和HelloWorldConfiguration兩個類),随即導入一個新的dropwizard maven管理的項目,大體結構如下:
其實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打包及本地運作
在該應用的本地目錄下target目錄中會有一個打包jar檔案,同目錄下還有hello-world.yml,
在target目錄下打開本地指令行,
輸入
java -jar helloworld--SNAPSHOT.jar server hello-world.yml
即可啟動内嵌jetty 應用伺服器,并可在8080端口通路/hello-world?name=fujian or /hello-world;在8081端口打開管理端部分,運作healthCheck
到此結束。這是架構的起步,官網 還介紹了在架構基礎上內建一些重要的功能,如JDBI、hibernate、authentic等,感興趣的可以去官網學習