📓 概要: 阅读本教程,以了解如何使用Spring Boot 2.0,Prometheus和Grafana为CRUD创建REST API。
在第1部分中,我们将使用Spring Boot 2.0,JPA,H2数据库和SWAGGER UI创建文档,从而为CRUD操作创建REST API。
我们将创建一个简单的应用程序,该应用程序将为要使用的人员实体提供基于REST的CRUD操作
因此,让我们开始创建一个新项目。
在Eclipse中创建一个spring starter项目(我正在使用STS),或者您可以使用Spring Initializer 来开始。添加Web,Lombok,Actuator,H2和JPA的依赖项。
此时,我们的项目结构应如下所示:
现在,让我们添加控制器,实体,模型和服务类。
pom.xml
:包含所有依赖项的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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.satish.monitoring</groupId>
<artifactId>person-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>person-application</name>
<description>Sample application to be monitored</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.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>
<springfox-version>2.5.0</springfox-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-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- DB-JPA boot related -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--SpringFox swagger dependencies -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox-version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.satish.monitoring.db.entities;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import com.satish.monitoring.web.models.Person;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity
@Table(name = "PERSON")
@NoArgsConstructor
public class PersonEntity implements Serializable{
private static final long serialVersionUID = -8003246612943943723L;
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private int personId;
private String firstName;
private String lastName;
private String email;
public PersonEntity( String firstName, String lastName, String email) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public PersonEntity(int personId, String firstName, String lastName, String email) {
super();
this.personId = personId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
}
package com.satish.monitoring.db.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.satish.monitoring.db.entities.PersonEntity;
@Repository
public interface PersonRepository extends JpaRepository<PersonEntity, Integer>{
}
package com.satish.monitoring.services;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
import com.satish.monitoring.web.models.Person;
@Service
public interface PersonService {
/**
*
* @param personId
* @return {@link Optional} {@link Person} objects if present in database
* for supplied person ID
*/
public Optional<Person> getPersonById(int personId);
/**
*
* @return {@link List} of {@link Person} model class fo rall available
* entities
*/
public List<Person> getAllPersons();
/**
*
* @param personId
* @return Delete the person from database for supplied id
*/
public boolean removePerson(int personId);
/**
*
* @param person
* @return {@link Optional} {@link Person} objects after save or update Save
* if no personId present else update
*/
public Optional<Person> saveUpdatePerson(Person person);
}
package com.satish.monitoring.services;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.satish.monitoring.db.entities.PersonEntity;
import com.satish.monitoring.db.repositories.PersonRepository;
import com.satish.monitoring.web.models.Person;
@Component
public class PersonServiceImpl implements PersonService {
private final PersonRepository personRepo;
@Autowired
PersonServiceImpl(PersonRepository personRepo) {
this.personRepo = personRepo;
}
/**
* Convert {@link Person} Object to {@link PersonEntity} object Set the
* personId if present else return object with id null/0
*/
private final Function<Person, PersonEntity> personToEntity = new Function<Person, PersonEntity>() {
@Override
public PersonEntity apply(Person person) {
if (person.getPersonId() == 0) {
return new PersonEntity(person.getFirstName(), person.getLastName(), person.getEmail());
} else {
return new PersonEntity(person.getPersonId(), person.getFirstName(), person.getLastName(),
person.getEmail());
}
}
};
/**
* Convert {@link PersonEntity} to {@link Person} object
*/
private final Function<PersonEntity, Person> entityToPerson = new Function<PersonEntity, Person>() {
@Override
public Person apply(PersonEntity entity) {
return new Person(entity.getPersonId(), entity.getFirstName(), entity.getLastName(), entity.getEmail());
}
};
/**
* If record is present then convert the record else return the empty {@link Optional}
*/
@Override
public Optional<Person> getPersonById(int personId) {
return personRepo.findById(personId).map(s -> entityToPerson.apply(s));
}
@Override
public List<Person> getAllPersons() {
return personRepo.findAll().parallelStream()
.map(s -> entityToPerson.apply(s))
.collect(Collectors.toList());
}
@Override
public boolean removePerson(int personId) {
personRepo.deleteById(personId);
return true;
}
@Override
public Optional<Person> saveUpdatePerson(Person person) {
if(person.getPersonId() == 0 || personRepo.existsById(person.getPersonId())){
PersonEntity entity = personRepo.save(personToEntity.apply(person));
return Optional.of(entityToPerson.apply(entity));
}else{
return Optional.empty();
}
}
}
package com.satish.monitoring.web.rest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.satish.monitoring.services.PersonService;
import com.satish.monitoring.web.models.Person;
@RestController
@RequestMapping("/person")
public class PersonResource {
private final PersonService personService;
/**
* Constructor to autowire PersonService instance.
* Look we have declared personService as final without initialization
*/
@Autowired
PersonResource(PersonService personService) {
this.personService = personService;
}
/**
*
* @return expose GET endpoint to return {@link List} of all available persons
*/
@GetMapping
public List<Person> getAllPerson() {
return personService.getAllPersons();
}
/**
*
* @param personId supplied as path variable
* @return expose GET endpoint to return {@link Person} for the supplied person id
* return HTTP 404 in case person is not found in database
*/
@GetMapping(value = "/{personId}")
public ResponseEntity<Person> getPerson(@PathVariable("personId") int personId) {
return personService.getPersonById(personId).map(person -> {
return ResponseEntity.ok(person);
}).orElseGet(() -> {
return new ResponseEntity<Person>(HttpStatus.NOT_FOUND);
});
}
/**
*
* @param person JSON body
* @return expose POST mapping and return newly created person in case of successful operation
* return HTTP 417 in case of failure
*/
@PostMapping
public ResponseEntity<Person> addNewPerson(@RequestBody Person person) {
return personService.saveUpdatePerson(person).map(p -> {
return ResponseEntity.ok(p);
}).orElseGet(() -> {
return new ResponseEntity<Person>(HttpStatus.EXPECTATION_FAILED);
});
}
/**
*
* @param person JSON body
* @return expose PUT mapping and return newly created or updated person in case of successful operation
* return HTTP 417 in case of failure
*
*/
@PutMapping
public ResponseEntity<Person> updatePerson(@RequestBody Person person) {
return personService.saveUpdatePerson(person).map(p -> {
return ResponseEntity.ok(p);
}).orElseGet(() -> {
return new ResponseEntity<Person>(HttpStatus.EXPECTATION_FAILED);
});
}
/**
*
* @param personId person id to be deleted
* @return expose DELETE mapping and return success message if operation was successful.
* return HTTP 417 in case of failure
*
*/
@DeleteMapping(value = "/{personId}")
public ResponseEntity<String> deletePerson(@PathVariable("personId") int personId) {
if (personService.removePerson(personId)) {
return ResponseEntity.ok("Person with id : " + personId + " removed");
} else {
return new ResponseEntity<String>("Error deleting enitty ", HttpStatus.EXPECTATION_FAILED);
}
}
}
package com.satish.monitoring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
*
* @author Satish Sharma
*
*/
@EnableSwagger2
@Configuration
public class SwaggerAPIDocumentationConfig {
ApiInfo apiInfo() {
return new ApiInfoBuilder().title("Person REST CRUD operations API in Spring-Boot 2")
.description(
"Sample REST API for monitoring using Spring Boot, Prometheus and Graphana ")
.termsOfServiceUrl("").version("0.0.1-SNAPSHOT").contact(new Contact("Satish Sharma", "https://github.com/hellosatish/monitoring/person", "https://github.com/hellosatish")).build();
}
@Bean
public Docket configureControllerPackageAndConvertors() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.satish.monitoring")).build()
.directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
.apiInfo(apiInfo());
}
}
package com.satish.monitoring.web.rest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class SwaggerUIController {
@RequestMapping(value = "/")
public String index() {
return "redirect:swagger-ui.html";
}
}
# Server configurations.
server.port=9000
logging.level.com.satish.monitoring=debug
logging.file=logs/monitoring.log
# Database configurations.
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:file:./db/target/person_db;DB_CLOSE_DELAY=-1
spring.datasource.username=satish
spring.datasource.data=classpath:/db-scripts/init-script.sql
spring.h2.console.enabled=true
spring.h2.console.path=/db-console
spring.jpa.show-sql=true
# change the below to none in production
spring.jpa.hibernate.ddl-auto=create-drop
运行:我们都准备好了。现在,让我们使用下面的命令运行该应用程序。或在STS中,您可以在项目浏览器中的项目上单击鼠标右键 ,然后选择Run As,然后 选择Spring Boot App 。
mvn clean spring-boot:run
现在浏览URL http://localhost:9000 ,您应该能够看到 SWAGGER UI
您已经成功创建了用于CRUD操作的REST API。您可以从GitHub Repo中查看/下载代码。
在 接下来的部分,我们应使端点暴露指标为JSON。