跳到主要內容

Spring boot v1.5 (四) swagger,properties and structing your code

上一章有稍微讓大家看一下swagger怎麼跟spring boot無縫接軌,本章就深入一點來介紹swagger,把它定位在 API似乎有點薄弱,當作工具(Tools)我覺得倒很個很好的解釋,它主要提供的工具分別為 swagger core、swagger codegen、swagger editor、swagger ui等,以下是各自的說明:

  • swagger core:這是swagger的核心,使用Java撰寫出來的。
  • swagger codegen:可以依據swagger(yaml格式)產生出來的api json或是url,可產生客戶端(client)相對應的程式碼。
  • swagger editor:透過這個工具可以產生yaml,有點像是Eclipse IDE一樣,撰寫相對應的yaml,透過yaml可以產服務端(server)及客戶端(client)的程式碼,透過這裡可以玩玩看。
  • swagger ui:將這工具與你的程式結合,如上章的方式,程式啟動後就會自動生成 api 前端頁面提供使用。

調整一下原本的程式碼,增加客製化的功能,並修改原本Ex3Controller、User增加REST註解,你就會發現swagger的好用。
  • SwaggerConfiguration.java
package louisz.springboot.example3;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import static springfox.documentation.builders.PathSelectors.regex;

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
 @Bean
 public Docket usersApi() {
  return new Docket(DocumentationType.SWAGGER_2).select()
    .apis(RequestHandlerSelectors.basePackage("louisz.springboot.example3")).paths(regex("/user.*")).build()
    .apiInfo(metaData());

 }

 private ApiInfo metaData() {
  ApiInfo apiInfo = new ApiInfo("Louisz User RESTful API", "Louisz User RESTful API for blog", "1.0",
    "Terms of service", new Contact("Louisz Cheng", "#", "louisz6ster@gmail.com"),
    "Apache License Version 2.0", "https://www.apache.org/licenses/LICENSE-2.0");
  return apiInfo;
 }
}
  • Ex3Controller.java
package louisz.springboot.example3;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

@RestController
@RequestMapping(value = "/user") // 上層路徑配置
@Api(value = "User Function", description = "操作User的RESTful 線上文件說明") // 最上層的說明                 
public class Ex3Controller {
 // 單一儲存庫
 static Map users = Collections.synchronizedMap(new HashMap());

 /**
  * 取得所有User清單
  * 
  * @return
  */
 @ApiOperation(value = "取得所有User清單", response = User.class)
 @ApiResponses(value = { @ApiResponse(code = 200, message = "正常回覆"), @ApiResponse(code = 401, message = "你尚未驗證通過"),
   @ApiResponse(code = 403, message = "你沒有權限可以使用該功能"), @ApiResponse(code = 404, message = "URL錯誤") })
 @RequestMapping(value = "/all", method = RequestMethod.POST)
 public List getUserList() {
  // 取得所有User List
  return new ArrayList(users.values());
 }

 /**
  * 新增User資料,傳入User JSON資料進行新增,成功回傳success
  * 
  * @param user
  * @return
  */
 @ApiOperation(value = "新增一筆User的資料", response = Iterable.class)
 @ApiResponses(value = { @ApiResponse(code = 200, message = "正常回覆"), @ApiResponse(code = 401, message = "你尚未驗證通過"),
   @ApiResponse(code = 403, message = "你沒有權限可以使用該功能"), @ApiResponse(code = 404, message = "URL錯誤") })
 @RequestMapping(value = "/add", method = RequestMethod.POST)
 public String addUser(@ModelAttribute User user) {
  // 傳入Input JSON會自動轉換為User物件
  users.put(user.getId(), user);
  return "add success";
 }

 /**
  * 傳入id進行查詢,回傳User JSON
  * 
  * @param id
  * @return
  */
 @ApiOperation(value = "透過ID查詢User資料", response = User.class)
 @ApiResponses(value = { @ApiResponse(code = 200, message = "正常回覆"), @ApiResponse(code = 401, message = "你尚未驗證通過"),
   @ApiResponse(code = 403, message = "你沒有權限可以使用該功能"), @ApiResponse(code = 404, message = "URL錯誤") })
 @RequestMapping(value = "/find/{id}", method = RequestMethod.POST)
 public User findUser(@PathVariable Long id) {
  // Output User會自動轉換為JSON物件
  return users.get(id);
 }

 /**
  * 傳入id及JSON User進行資料更新
  * 
  * @param id
  * @param user
  * @return
  */
 @ApiOperation(value = "更新特定User資料", response = String.class)
 @ApiResponses(value = { @ApiResponse(code = 200, message = "正常回覆"), @ApiResponse(code = 401, message = "你尚未驗證通過"),
   @ApiResponse(code = 403, message = "你沒有權限可以使用該功能"), @ApiResponse(code = 404, message = "URL錯誤") })
 @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
 public String updateUser(@PathVariable Long id, @ModelAttribute User user) {
  // 更新User資料
  User u = users.get(id);
  // System.out.println(u.getName()+"=>"+user.getMobileNumber());
  // 使用了BeanUtils
  BeanUtils.copyProperties(user, u);
  users.put(id, u);
  return "update success";
 }

 /**
  * 傳入id進行資料刪除,回傳success
  * 
  * @param id
  * @return
  */
 @ApiOperation(value = "刪除特定User資料", response = String.class)
 @ApiResponses(value = { @ApiResponse(code = 200, message = "正常回覆"), @ApiResponse(code = 401, message = "你尚未驗證通過"),
   @ApiResponse(code = 403, message = "你沒有權限可以使用該功能"), @ApiResponse(code = 404, message = "URL錯誤") })
 @RequestMapping(value = "/remove/{id}", method = RequestMethod.DELETE)
 public String removeUser(@PathVariable Long id) {
  // 刪除user資料
  users.remove(id);
  return "delete success";
 }

}
  • User.java
package louisz.springboot.example3;

import io.swagger.annotations.ApiModelProperty;

public class User {
 @ApiModelProperty(notes = "ID", required = true)
 private Long id;
 @ApiModelProperty(notes = "使用者姓名", required = true)
 private String name;
 @ApiModelProperty(notes = "使用者emai", required = true)
 private String email;
 @ApiModelProperty(notes = "使用者手機號碼")
 private String mobileNumber;

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

 public String getMobileNumber() {
  return mobileNumber;
 }

 public void setMobileNumber(String mobileNumber) {
  this.mobileNumber = mobileNumber;
 }

}

執行並連結到http://localhost:8090/swagger-ui.html,結果如圖


    補充完swagger,進入本章的兩個主題,自訂屬性(application.properties)及專案結構定義,之前有提到application.properties(這裡),spring boot似乎一些主要的設定資訊都是透過這裡,一 般來說大部分專案都會使用到自己所需的設定資訊都會放在檔案中,可以透過File或是Apache Common Configuration或是其他方式進行,在spring boot當中已經把這機制放進去了,所以可以將所需的資訊放在application.properties或是resources下的其他自訂檔案中。
    在專案的開發中,分類一向都是很難決定的,因為伴隨著越來越多code,系統或專案結構就變得很重要,以下的分類方式提供給大家參考,當然有些情形是依據當下或是特定的規定進行,不過大致上這樣的分類應該可以應付大部分的專案才是,主要以Layer的概念去切割,如view、controller、model等,然後再細分下去如model.entities、model.facade、controller.service、controller.integrate等,當然也有些基本的如commons、utils,參考結構如下:

company(公司名稱).system(系統別).layer.patterns(design pattern)
company.project(專案).layer.functions(功能)
company.commons(通用)
company.utils(工具)
company.system.commons
company.system.utils


學習目的:使用自訂屬性設定檔、屬性一些進階的使用。
學習時數:1.5 hr
教學影片:




  • ExampleProperties.java
package louisz.springboot.example4;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component // DI之後介紹
@PropertySource("classpath:example4.properties") //設定自訂的設定檔檔名及路徑
@ConfigurationProperties("app") //prefix
public class ExampleProperties {
 //@Value("${app.name}") 也可以採用這種方式,這樣name可以不用相同,但是不建議
 //,因為後續不好維護
 private String name;
 
 private String version;
 
 private String nameversion;
 
 private String stringvalue;
 private int intvalue;
 private long longvalue;
 private int tenvalue;
 private int twentyvalue;
 
 
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getVersion() {
  return version;
 }
 public void setVersion(String version) {
  this.version = version;
 }
 public String getNameversion() {
  return nameversion;
 }
 public void setNameversion(String nameversion) {
  this.nameversion = nameversion;
 }
 public String getStringvalue() {
  return stringvalue;
 }
 public void setStringvalue(String stringvalue) {
  this.stringvalue = stringvalue;
 }
 public int getIntvalue() {
  return intvalue;
 }
 public void setIntvalue(int intvalue) {
  this.intvalue = intvalue;
 }
 public long getLongvalue() {
  return longvalue;
 }
 public void setLongvalue(long longvalue) {
  this.longvalue = longvalue;
 }
 public int getTenvalue() {
  return tenvalue;
 }
 public void setTenvalue(int tenvalue) {
  this.tenvalue = tenvalue;
 }
 public int getTwentyvalue() {
  return twentyvalue;
 }
 public void setTwentyvalue(int twentyvalue) {
  this.twentyvalue = twentyvalue;
 }

 
}

  • TestExampleProperties.java
package louisz.springboot.example4;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;


//spring boot 1.4以後的方式
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class TestExampleProperties {
 @Autowired
 private ExampleProperties exampleProperties;//注入
 
 @Test
 public void getHello() throws Exception {
  //驗證是否符合預期
  Assert.assertEquals(exampleProperties.getName(), "example4");
  Assert.assertEquals(exampleProperties.getVersion(), "1");
  Assert.assertEquals(exampleProperties.getNameversion(), "example4_1");
  System.out.println(exampleProperties.getIntvalue());
  System.out.println(exampleProperties.getTwentyvalue());
 }
 
}

Github code(louisz.springboot.example4)

參考連結:
http://blog.didispace.com/springbootproperties/

留言

這個網誌中的熱門文章

Pentaho kettle取指定目錄下的所有檔案

最近開始玩 BI的東西,而之前專案有使用到Pentaho Open Source這個好物.... Pentaho裡面很多東西,跟 Jasperreport一樣東西很多,我最先接觸到的是kettle ETL的工具,玩了一陣子,開始有空就把它寫下來,以免忘記。 Scan一個目錄下所有檔案,然後塞進去資料庫 1.先拉兩個 Input,一個Get File Names,一個是CSV file input,再拉一個 output 中的 Table output,然後把他連起來。 2.點開 Get File Names,File or directory設定你的指定目錄,Regular Expression則是輸入.*\.*$則是所有檔案,若是CSV則可.*\.torrent$這可以了,可以按一下 Preview rows看看是否正確。

Spring boot v1.5 (六) spring data jpa 基本操作

最近天氣好熱,做甚麼事都覺得很懶,想要寫個spring data jpa也是懶懶的,不過這部分卻也是滿重要的一部分,前一篇介紹 JDBCTemplate ,已經覺得跟以前寫SQL方式有所差異了,JPA帶來的是物件導向的設計面思考,說到JPA不得不提提 ORM ,Object-relational mapping主要想法為簡化及物件導向的設計,讓RDB更貼近Object,在設計上可以更加便利,甚至透過一些設計可以讓Table具有物件導向的特性如繼承等等,以往要使用ORM的框架,都會先以 Hibernate 進行,不過近來慢慢地轉向JPA,主要還是在減少程式碼、增加彈性等等,大體的功能沒有差異很大,所以從Hibernate轉到JPA問題不大,JPA要介紹的東西還滿多的,所以我這裡會再分成三個章節來介紹。 SPRING DATA JPA基本操作 JPQL & Named SQL & Native SQL Cache & DB Design Pattern SPRING DATA JPA更加簡化的程式撰寫,只需要一個 Interface內寫一些查詢 method就可以操作JPA,因為利用 method 組合查詢條件,確實很方便也很容易理解,若是都沒有辦法符合需求當然也可以自己實作一個來用當然沒有問題。 學習目的 :SPRING DATA JPA基本操作。 學習時數 :3.5hr 教學影片: pom.xml 說明 spring-boot-starter-web:配置 Web Project所需的函式庫。 spring-boot-starter-test:配置 unit or mock test 所需的函式庫。 spring-boot-starter-actuator:配置監控spring boot所需的函式庫,後續spring cloud會使用到,所以一開就導入。 spring-boot-starter-jdbc:配置使用jdbc所需的函式庫。 postgresql:配置postgresql連接Driver所需的函式庫。 jasypt-spring-boot-starter:加解密所需的函式庫。 spring-boot-starter-data-jpa:配置Spring data jpa所需的函式庫。

IReport字型下拉選單中文亂碼

這個問題其實也不是很大啦,不過當你有很多的中文字型檔的時候可能就不知道要選哪一個,啟動IReport後,開啟報表後會發現左邊下拉選單中,最下面的字型清單中有出現方框,顯示不出該字型的名稱,這幾個字型應該是判斷新細明體,標楷體及細明體,如下圖 下載IReport的Source Code來檢查一下,it.businesslogic.ireport.gui.MainFrame發現這個JComboBox有特別設定Arial字型,當然只要是中文的都顯示不出來ㄚ,所以點掉這一行後重新編譯,嘿嘿就可以了。 jComboBoxFont.setFont(new java.awt.Font("Arial", 0, 11)); 我目前使用的版本為 IReport-3.0.0-src