javascript
构建Spring Boot RESTful服务+ Spring Boot执行器
總覽
什么是REST?
REST(代表狀態轉移)是Web構建的體系結構樣式,已成為用于Web應用程序的標準軟件設計模式 。 代表性國家轉移一詞最早由REST的發起人,HTTP規范的主要作者之一Roy Fielding在其博士論文中使用 。
REST上有很多很好的參考,包括:
- 維基百科
- 理查森成熟度模型
- Ryan Tomayko如何向他的妻子解釋REST
- Roy Fielding的REST API必須由超文本驅動
- Stackoverflow SOAP與REST
本教程基于使用Spring構建Rest Services,并且本教程的開頭也對REST進行了很好的概述。
什么是彈簧啟動執行器?
Spring Boot Actuator是Spring Boot的子項目。 它為您的應用程序添加了幾項生產級服務,而您只需花費很少的精力。
執行器的定義
致動器是負責移動或控制系統的組件。
執行器一詞不限于Spring Boot; 但是,這是我們在這里的重點。
在Spring Boot應用程序中配置Actuator之后,它允許您通過調用Spring Boot Actuator公開的不同技術不可知端點(例如應用程序運行狀況,Bean,記錄器,映射和跟蹤)來交互和監視應用程序。 在此Spring文檔中列出了更多信息。
0 –帶啟動器的Spring Boot RESTful Web服務示例應用程序
我們將使用Spring Boot和Actuator構建一個示例RESTful Web應用程序。
該應用程序將是“用戶名跟蹤器”。 在此應用程序中,一個人擁有一個帳戶,他們的帳戶可能具有許多用戶名。
查看并從 Github 下載代碼
1 –項目結構
和往常一樣,我們有一個正常的Maven項目結構。
2 –項目依賴性
除了典型的Spring Boot依賴關系之外,我們還為嵌入式數據庫提供了HSQLDB,并為所有Actuator依賴關系提供了spring-boot-starter-actuator。
<?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/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.michaelcgood</groupId><artifactId>michaelcgood-springbootactuator</artifactId><version>0.0.1</version><packaging>jar</packaging><name>Spring-Boot-Actuator-Example</name><description>Michael C Good - Spring Boot Actuator Example</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.6.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.hsqldb</groupId><artifactId>hsqldb</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>3 –運行空應用程序
盡管我們沒有編寫任何代碼,但是我們將運行Spring Boot應用程序。
轉到您的終端并按照命令進行操作。
mikes-MacBook-Air:Spring-Boot-Actuator-Example mike$ curl localhost:8080 {"timestamp":1505235455245,"status":404,"error":"Not Found","message":"No message available","path":"/"}我們尚未編寫任何代碼,除了默認的容器生成HTML錯誤響應外,Actuator還從/ error端點生成JSON響應。
mikes-MacBook-Air:Spring-Boot-Actuator-Example mike$ curl localhost:8080/health {"status":"UP"}執行器/運行狀況端點將讓您知道您的應用程序是否啟動。
4 –模型
現在,為用戶名跟蹤器應用程序定義模型的字段。
- 如前所述,一個人有一個帳戶,可能有許多用戶名。 所以我們用@OneToMany注釋映射Set
- 用戶名模型將具有密碼和用戶名
- 我們的模型將需要一個ID,并使其自動生成
- 我們進行類構造以定義可以使用用戶名和密碼創建的帳戶。 由于使用了這種自定義構造函數,因此我們還需要設置一個沒有參數的默認構造函數。
Account.java
package com.michaelcgood.model;import java.util.HashSet; import java.util.Set;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany;import com.fasterxml.jackson.annotation.JsonIgnore;@Entity public class Account {public Set<Usernames> getUsernames() {return usernames;}public void setUsernames(Set<Usernames> usernames) {this.usernames = usernames;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}@OneToMany(mappedBy= "account")private Set<Usernames> usernames = new HashSet<>();@Id@GeneratedValueprivate Long id;@JsonIgnorepublic String password;public String username;public Account(String name, String password) {this.username = name;this.password = password;}Account(){}}用戶名.java
- 由于一個帳戶可以使用多個用戶名,因此情況也相反:一個帳戶可以使用多個用戶名。 因此,我們使用@ManyToOne注釋映射Account
- 要跟蹤用戶名,我們需要:URL和用戶名
- 我們再次定義一個自動生成的ID
- 我們定義了一個自定義類構造函數,該構造函數需要account,url和username參數。 再一次,我們需要定義一個默認的構造函數方法,以避免引發錯誤。
5 –儲存庫
我們為兩個模型創建一個存儲庫,并使用派生查詢創建搜索功能。
AccountRepository.java
package com.michaelcgood.dao;import java.util.Optional;import org.springframework.data.jpa.repository.JpaRepository;import com.michaelcgood.model.Account;public interface AccountRepository extends JpaRepository<Account,Long> {Optional<Account> findByUsername(String username); }用戶名Repository.java
package com.michaelcgood.dao;import java.util.Collection;import org.springframework.data.jpa.repository.JpaRepository;import com.michaelcgood.model.Usernames;public interface UsernamesRepository extends JpaRepository<Usernames,Long> {Collection<Usernames> findByAccountUsername(String username);}6 –控制器
在控制器中,我們定義將用于RESTful Web服務的所有映射。
- 我們用@RestController而不是@Controller注釋控制器。 如javadoc中所述,它是“一種方便注釋,其本身通過@Controller和@ResponseBody進行了注釋?!?
- 我們聲明UsernamesRepository和AccountRepository的變量,并使其成為最終變量,因為我們只希望將值分配一次。 我們通過UsernamesRestController類構造函數將它們注釋為@Autowired。
- {userId}和{usernamesId}是路徑變量 。 這意味著這些值在URL中提供。 這將在我們的演示中顯示。
- Controller方法返回POJO(普通的舊Java對象) 。 Spring Boot自動連接HttpMessageConverter以將這些通用對象轉換為JSON。
用戶名RestController.java
package com.michaelcgood.controller;import java.net.URI; import java.util.Collection;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder;import com.michaelcgood.dao.AccountRepository; import com.michaelcgood.dao.UsernamesRepository; import com.michaelcgood.model.Usernames;@RestController @RequestMapping("/{userId}/usernames") public class UsernamesRestController {private final UsernamesRepository usernamesRepository;private final AccountRepository accountRepository;@AutowiredUsernamesRestController(UsernamesRepository usernamesRepository, AccountRepository accountRepository){this.usernamesRepository = usernamesRepository;this.accountRepository = accountRepository;}@GetMappingCollection<Usernames> readUsernames (@PathVariable String userId){this.validateUser(userId);return this.usernamesRepository.findByAccountUsername(userId);}@PostMappingResponseEntity<?> add(@PathVariable String userId,@RequestBody Usernames input){this.validateUser(userId);return this.accountRepository.findByUsername(userId).map(account -> {Usernames result = usernamesRepository.save(new Usernames(account,input.url,input.username));URI url = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(result.getId()).toUri();return ResponseEntity.created(url).build(); }).orElse(ResponseEntity.noContent().build());}@GetMapping(value="{usernamesId}")Usernames readUsername(@PathVariable String userId, @PathVariable Long usernameId){this.validateUser(userId);return this.usernamesRepository.findOne(usernameId);}private void validateUser(String userId){this.accountRepository.findByUsername(userId).orElseThrow(() -> new UserNotFoundException(userId));}}UserNotFoundException.java
在這里,我們定義了在Controller類中用來解釋找不到用戶的自定義異常。
package com.michaelcgood.controller;import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus;@ResponseStatus(HttpStatus.NOT_FOUND) public class UserNotFoundException extends RuntimeException {/*** */private static final long serialVersionUID = 7537022054146700535L;public UserNotFoundException(String userId){super("Sorry, we could not find user '" + userId +"'."); }}7 – @SpringBootApplication
我們使用CommandLineRunner創建帳戶并插入用戶名。 每個帳戶都有兩個用戶名。
package com.michaelcgood;import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean;import com.michaelcgood.dao.AccountRepository; import com.michaelcgood.dao.UsernamesRepository; import com.michaelcgood.model.Account; import com.michaelcgood.model.Usernames;import java.util.Arrays;@SpringBootApplication public class SpringBootActuatorExampleApplication {public static void main(String[] args) {SpringApplication.run(SpringBootActuatorExampleApplication.class, args);}@BeanCommandLineRunner init(AccountRepository accountRepository,UsernamesRepository usernamesRepository) {return (evt) -> Arrays.asList("ricksanchez,mortysmith,bethsmith,jerrysmith,summersmith,birdperson,squanchy,picklerick".split(",")).forEach(a -> {Account account = accountRepository.save(new Account(a,"password"));usernamesRepository.save(new Usernames(account,"http://example.com/login", a +"1"));usernamesRepository.save(new Usernames(account,"http://example2.com/login", "the_"+a));});} }8 –配置
在Spring文檔中有說明 :
默認情況下,所有敏感的HTTP端點都是安全的,因此只有具有ACTUATOR角色的用戶才能訪問它們。 使用標準的HttpServletRequest.isUserInRole方法可以增強安全性。
我們尚未設置任何安全性和用戶角色,因為這只是一個示例。 因此,為了便于演示,我將禁用安全性要求。 否則,我們將立即收到一個“未經授權”的錯誤,如下圖所示。
{"timestamp":1505321635068,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource.","path":"/beans"}application.properties
將此添加到application.properties以禁用身份驗證。
management.security.enabled=false9 –演示
要從服務器檢索響應,您可以在瀏覽器中訪問URL或使用curl。 對于我的演示,我正在使用curl。
REST查詢存儲庫中的數據
查詢屬于帳戶jerrysmith的用戶名。
mikes-MacBook-Air:Spring-Boot-Actuator-Example mike$ curl localhost:8080/jerrysmith/usernames [{"id":7,"url":"http://example.com/login","username":"jerrysmith1"},{"id":8,"url":"http://example2.com/login","username":"the_jerrysmith"}]查詢屬于帳戶picklerick的用戶名
mikes-MacBook-Air:Spring-Boot-Actuator-Example mike$ curl localhost:8080/picklerick/usernames [{"id":15,"url":"http://example.com/login","username":"picklerick1"},{"id":16,"url":"http://example2.com/login","username":"the_picklerick"}]執行器查詢
該查詢的響應非常長,因此被截斷了。
豆子
mikes-MacBook-Air:Spring-Boot-Actuator-Example mike$ curl localhost:8080/beans [{"context":"application","parent":null,"beans":[{"bean":"springBootActuatorExampleApplication","aliases":[],"scope":"singleton","type":"com.michaelcgood.SpringBootActuatorExampleApplication$$EnhancerBySpringCGLIB$$509f4984","resource":"null","dependencies":[]},{"bean":"org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory","aliases":[],"scope":"singleton","type":"org.springframework.core.type.classreading.CachingMetadataReaderFactory","resource":"null","dependencies":[]},{"bean":"usernamesRestController","aliases":[],"scope":"singleton","type":"com.michaelcgood.controller.UsernamesRestController","resource":"file [/Users/mike/javaSTS/Spring-Boot-Actuator-Example/target/classes/com/michaelcgood/controller/UsernamesRestController.class]","dependencies":["usernamesRepository","accountRepository"]},{"bean":"init","aliases":[],"scope":"singleton","type":"com.michaelcgood.SpringBootActuatorExampleApplication$$Lambda$11/889398176","resource":"com.michaelcgood.SpringBootActuatorExampleApplication", [...]指標
mikes-MacBook-Air:Spring-Boot-Actuator-Example mike$ curl localhost:8080/metrics {"mem":350557,"mem.free":208275,"processors":4,"instance.uptime":213550,"uptime":240641,"systemload.average":1.6552734375,"heap.committed":277504,"heap.init":131072,"heap.used":69228,"heap":1864192,"nonheap.committed":74624,"nonheap.init":2496,"nonheap.used":73062,"nonheap":0,"threads.peak":27,"threads.daemon":23,"threads.totalStarted":30,"threads":25,"classes":9791,"classes.loaded":9791,"classes.unloaded":0,"gc.ps_scavenge.count":11,"gc.ps_scavenge.time":139,"gc.ps_marksweep.count":2,"gc.ps_marksweep.time":148,"httpsessions.max":-1,"httpsessions.active":0,"datasource.primary.active":0,"datasource.primary.usage":0.0,"gauge.response.beans":14.0,"gauge.response.info":13.0,"counter.status.200.beans":2,"counter.status.200.info":1}9 –結論
恭喜,您已經創建了一個可以用Actuator監視的RESTful Web服務。 REST實際上是不同客戶端進行通信的最有機的方式,因為它由于HTTP而起作用。
源代碼在 Github上
翻譯自: https://www.javacodegeeks.com/2017/10/building-spring-boot-restful-service-spring-boot-actuator.html
總結
以上是生活随笔為你收集整理的构建Spring Boot RESTful服务+ Spring Boot执行器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 姚贝娜个人简介 姚贝娜个人简介是什么
- 下一篇: 使用基本身份验证来保护Spring Bo