SpringBoot系列十一:SpringBoot整合Restful架構(使用 RestTemplate 模版實現 Rest 服務調用、Swagger 集成、動態修改日誌級別)
1、概念:SpringBoot整合Restful架構
2、背景
Spring 與 Restful 整合才是微架構的核心,雖然在整個 SpringBoot(SpringCloud)之中提供有大量的服務方便整合,但是這些 整合都不如 Rest 重要,因為 Rest 是整個在微架構之中進行通訊的基礎模式。那麽對於 Rest 首先必須對其有一個最為核心的解釋: 利用 JSON 實現數據的交互處理。而且 Spring 裏面提供有一個非常強大的 RestTemplate 操作模版,利用此模版可以非常輕松的實現 Rest 的 JSON 數據與各種對象間的自動轉換。
在默認狀態下 Spring 裏面針對於 Rest 的處理使用的都是 jackson 開發包支持包。
2.1、使用 RestTemplate 模版實現 Rest 服務調用
由於 Rest 屬於分布式的項目開發環境,所以本次進行項目建立的時候一共建立有三個子模塊:
· microboot-restful-api:作為公共的類定義,例如:可以將所有的 VO 類定義在此項目之中;
· microboot-restful-provider:作為服務提供者,這次的服務提供者提供兩個服務(獲得對象、增加對象);
· micorboot-restful-consumer:作為服務的消費者,消費者就是利用 RestTemplate 實現 Rest 服務的調用以及對象轉換
1、 【microboot-restful-api】建立一個公共的 VO 類對象:
package cn.study.microboot.vo; import java.io.Serializable; import java.util.Date; @SuppressWarnings("serial") public class Member implements Serializable { private Long mid ; private String name ; private Integer age ; private Double salary ; private Date birthday ; publicLong getMid() { return mid; } public void setMid(Long mid) { this.mid = mid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "Member [mid=" + mid + ", name=" + name + ", age=" + age + ", salary=" + salary + ", birthday=" + birthday + "]"; } }
2、 【microboot-restful-provider】修改 pom.xml 配置文件,去引用 microboot-restful-api模塊:
<dependency> <groupId>cn.mldn</groupId> <artifactId>microboot-restful-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
3、 【microboot-restful-provider】建立一個控制器實現 Rest 服務的處理:
package cn.study.microboot.controller; import java.util.Date; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import cn.study.microboot.vo.Member; @RestController public class MemberController { @RequestMapping(value = "/member/add") public Object add(@RequestBody Member member) { // 表示當前的配置可以直接將參數變為VO對象 System.err.println("【MemberController.add()接收對象】" + member); return true; } @RequestMapping(value = "/member/get") public Member get(long mid) { Member vo = new Member(); vo.setMid(mid); vo.setName("studyjava - " + mid); vo.setBirthday(new Date()); vo.setSalary(99999.99); vo.setAge(16); return vo; } }
4、 【microboot-restful-provider】定義程序啟動類,啟動服務,而後測試當前服務是否可用:
· 獲取對象信息:http://localhost:8080/member/get?mid=110;
· 增加對象信息:http://localhost:8080/member/add?mid=110&name=smith&age=12;
5、 【microboot-restful-consumer】如果要進行 Rest 操作,那麽一定要註意使用一個 RestTemplate 模版完成處理,所以首先要建立一個程序配置類,進行 RestTemplate 模版對象創建:
package cn.study.microboot.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestConfig { @Bean public RestTemplate getRestTemplate() { return new RestTemplate() ; } }
6、 【microboot-restful-consumer】修改 application.yml 配置端口:
server:
port: 80
7、 【microboot-restful-consumer】編寫測試程序類測試遠程 Rest 服務是否可用?
package cn.study.microboot; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.web.client.RestTemplate; import cn.study.microboot.vo.Member; @SpringBootTest(classes = StartSpringBootMain.class) @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class TestMemberRestful { @Resource private RestTemplate restTemplate; @Test public void testAdd() { Boolean flag = this.restTemplate.getForObject( "http://localhost:8080/member/add?mid=110&name=SMITH&age=10", Boolean.class); System.out.println("【ConsumerTest.add()】" + flag); } @Test public void testGet() { // 通過遠程的Rest服務中的信息將其自動轉換為Member對象實例 Member member = this.restTemplate.getForObject( "http://localhost:8080/member/get?mid=110", Member.class); System.out.println("【ConsumerTest.get()】" + member); } }
8、 【microboot-restful-provider】為了更方便的進行內容的傳輸,此時 Rest 服務的提供方一定要做出一點點修改:
package cn.study.microboot.controller; import java.util.Date; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import cn.study.microboot.vo.Member; @RestController public class MemberController { @RequestMapping(value = "/member/add",method=RequestMethod.POST) public Object add(@RequestBody Member member) { // 表示當前的配置可以直接將參數變為VO對象 System.err.println("【MemberController.add()接收對象】" + member); return true; } @RequestMapping(value = "/member/get/{mid}",method=RequestMethod.GET) public Member get(@PathVariable("mid") long mid) { Member vo = new Member(); vo.setMid(mid); vo.setName("studyjava - " + mid); vo.setBirthday(new Date()); vo.setSalary(99999.99); vo.setAge(16); return vo; } }
9、 【microboot-restful-consumer】編寫一個調用控制器進行處理;
package cn.study.microboot.controller; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; import cn.study.microboot.util.controller.AbstractBaseController; import cn.study.microboot.vo.Member; @Controller public class MemberConsumerController extends AbstractBaseController { @Resource private RestTemplate restTemplate; @RequestMapping(value = "/consumer/get", method = RequestMethod.GET) public String getMember(long mid,Model model) { Member member = this.restTemplate.getForObject( "http://localhost:8080/member/get/" + mid, Member.class); model.addAttribute("member", member) ; return "member_show"; } @RequestMapping(value = "/consumer/add", method = RequestMethod.GET) @ResponseBody public Object addMember(Member member) { Boolean flag = this.restTemplate.postForObject( "http://localhost:8080/member/add", member, Boolean.class); return flag; } }
10、 【microboot-restful-consumer】為了方便進行接收數據的演示,建立一個普通的 thymeleaf 頁面:
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>SpringBoot模版渲染</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> </head> <body> <p th:text="‘用戶編號:‘ + ${member.mid}"/> <p th:text="‘用戶姓名:‘ + ${member.name}"/> <p th:text="‘用戶年齡:‘ + ${member.age}"/> <p th:text="‘用戶工資:‘ + ${member.salary}"/> <p th:text="‘用戶生日:‘ + ${member.birthday}"/> </body> </html>
11、 【microboot-restful-consumer】訪問消費端服務:
· 測試信息獲得操作:http://localhost/consumer/get?mid=120;
· 測試信息追加操作:http://localhost/consumer/add?mid=120&name=ALLEN&age=10&salary=9.9&birthday=2000-10-10;
12、 現在在整個的項目處理之中會發現以下的幾個特點:
· Rest 服務的生產者只是按照自己返回的內容進行 JSON 數據的輸出;
· 消費者利用 RestTemplate 進行 JSON 數據的獲得以及自動向指定類型的對象進行轉換;
· 為了達到這種轉換的操作標準,特意準備了一個 api 項目保存公共的 VO 類型。
而對於 Rest 服務的更多考慮,應該包含如下幾點:
· 既然服務的提供者只能夠被消費者所訪問,證明其不可能被所有用戶操作,一定需要安全認證;
· 服務端一定要進行指定業務層和數據層的編寫,也就是說每一個服務端都應該具備有一個自己的服務器信息;
· 在服務端訪問非常繁忙的時候,消費端執行時有可能需要進行短期的熔斷處理;
· 服務端既然是一個獨立的組件,那麽就必須考慮負載均衡問題;
· 消費端進行服務端的調用操作,如果所有的調用都寫上明確的調用地址,太麻煩了;
· 消費端進行處理的時候如果都是自己來直接采用 RestTemplate 做處理,代碼結構太差了,因為畢竟服務端是遠程業務端,遠程業務端最好的調用應該就用接口完成。
2.2、Swagger 集成
當你現在建立一些公共的 Rest 服務的時候就可以利用 Swagger 進行所有 Rest 服務的描述了。也就是說它提供的只是一個說明 工具的概念。
1、 如果要想去使用 swagger 說明操作,則必須引入相應的依賴支持包:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
2、 定義一個進行 Swagger2 的配置程序類:
package cn.study.microboot.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket getDocket() { // 此類主要是整個的Swagger配置項,利用這個類需要來指派掃描包 return new Docket(DocumentationType.SWAGGER_2) .apiInfo(this.getApiInfo()).select() .apis(RequestHandlerSelectors .basePackage("cn.study.microboot.controller")) .paths(PathSelectors.any()).build(); // 設置文檔的顯示類型 } private ApiInfo getApiInfo() { return new ApiInfoBuilder().title("SpringBoot中使用Swagger構建項目說明信息") .description("接口描述信息") .termsOfServiceUrl("http://www.study.cn").contact("study——springbooot") .license("small lee").version("1.0").build(); } }
3、 修改 MemberController 程序類,追加相關註解信息:
package cn.study.microboot.controller; import java.util.Date; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import cn.study.microboot.vo.Member; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; @RestController public class MemberController { @ApiOperation(value = "實現人員信息的添加處理", notes = "添加") @ApiImplicitParams({ @ApiImplicitParam(name = "member", value = "用戶描述的詳細實體信息", required = true, dataType = "MemberClass")}) @RequestMapping(value = "/member/add", method = RequestMethod.POST) public Object add(@RequestBody Member member) { // 表示當前的配置可以直接將參數變為VO對象 System.err.println("【MemberController.add()接收對象】" + member); return true; } @ApiOperation(value = "獲取指定編號的人員信息", notes = "只需要設置mid的信息就可以獲取Member的完整內容") @ApiImplicitParams({ @ApiImplicitParam(name = "mid", value = "用戶編號", required = true, dataType = "String")}) @RequestMapping(value = "/member/get/{mid}", method = RequestMethod.GET) public Member get(@PathVariable("mid") long mid) { Member vo = new Member(); vo.setMid(mid); vo.setName("studyjava - " + mid); vo.setBirthday(new Date()); vo.setSalary(99999.99); vo.setAge(16); return vo; } }
4、 正常進行程序的啟動配置處理,而後打開瀏覽器進入到界面:http://localhost:8080/swagger-ui.html;
2.3、動態修改日誌級別
在項目開發之中日誌可以使用 info()、error()進行輸出在 SpringBoot 裏面提供有一個比較有意思的功能,就是說用戶可以通過 遠程的控制追加日誌的顯示級別的操作。
1、 定義一個簡單的控制器程序:
package cn.study.microboot.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MessageController { private Logger log = LoggerFactory.getLogger(MessageController.class); @RequestMapping(value = "/test") public Object test() { this.log.info("【*** INFO ***】日誌輸出"); this.log.error("【*** ERROR ***】日誌輸出"); return true; } }
2、 如果現在希望只進行 error 級別的日誌輸出,則修改 application.yml 配置文件:
logging:
level:
cn.mldn.microboot.controller: ERROR
3、 現在希望在以後程序運行的時候這個日誌的輸出級別可以動態的做一個擴充,所以這個時候要想達到這樣的目的就可以必須 進行安全的關閉操作,修改 pom.xml和application.yml 配置文件:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
management:
security :
enabled: false
4、 隨後在一個客戶端上進行修改,直接利用測試類完成。
package cn.study.microboot.vo; public class LogInfo { private String level; public String getLevel() { return level; } public void setLevel(String level) { this.level = level; } }
5、 隨後編寫一個測試類修改日誌級別:
package cn.study.microboot; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.web.client.RestTemplate; import cn.study.microboot.vo.LogInfo; @SpringBootTest(classes = StartSpringBootMain.class) @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class TestLogger { @Resource private RestTemplate restTemplate; @Test public void testLevel() { LogInfo log = new LogInfo(); log.setLevel("INFO"); // 新的日誌級別 this.restTemplate.postForLocation("http://localhost:8080/loggers/cn.study.microboot.controller", log); } }
動態修改日誌級別是 actuator 給出的一個簡單支持,但是在實際之中日誌的處理更多的情況下不會取消安全配置,所以這種日 誌的配置也不是全部可以使用。
SpringBoot系列十一:SpringBoot整合Restful架構(使用 RestTemplate 模版實現 Rest 服務調用、Swagger 集成、動態修改日誌級別)