构建Spring Boot RESTful服务+ Spring Boot执行器

总览

什么是REST?

REST(代表状态转移)是Web构建的体系结构样式,已成为用于Web应用程序的标准软件设计模式 。 代表性国家转移一词最早由REST的发起人,HTTP规范的主要作者之一Roy Fielding在其博士论文中使用

REST上有很多很好的参考,包括:

本教程基于使用Spring构建Rest Services,并且本教程的开头也对REST进行了很好的概述。

什么是弹簧启动执行器?

Spring Boot ActuatorSpring 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
	@GeneratedValue
	private Long id;
	
	@JsonIgnore
	public 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参数。 再一次,我们需要定义一个默认的构造函数方法,以避免引发错误。
package com.michaelcgood.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

import com.fasterxml.jackson.annotation.JsonIgnore;

@Entity
public class Usernames {
	
	@JsonIgnore
	@ManyToOne
	private Account account;
	
	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public Long getId() {
		return id;
	}

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

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Id
	@GeneratedValue
	private Long id;
	
	public String url;
	public String username;
	
	Usernames(){
		
	}
	
	public Usernames(Account account, String url, String username){
		this.url=url;
		this.account=account;
		this.username=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;
	
	@Autowired
	UsernamesRestController(UsernamesRepository usernamesRepository, AccountRepository accountRepository){
		this.usernamesRepository = usernamesRepository;
		this.accountRepository = accountRepository;
	}
	
	@GetMapping
	Collection<Usernames> readUsernames (@PathVariable String userId){
		this.validateUser(userId);
		return this.usernamesRepository.findByAccountUsername(userId);
	}
	
	@PostMapping
	ResponseEntity<?> 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);
	}
	
	@Bean
	CommandLineRunner 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=false

9 –演示

要从服务器检索响应,您可以在浏览器中访问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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值