Tavaszi Boot Tutorial - Bootstrap egy egyszerű alkalmazás

1. Áttekintés

A Spring Boot egy véleményes, a konvenciókon túlkonfigurációra összpontosító kiegészítő a Spring platformhoz - nagyon hasznos a minimális erőfeszítéssel történő induláshoz, és önálló, gyártási szintű alkalmazások létrehozásához.

Ez az oktatóanyag a Boot kiindulópontja - az első lépések egyszerű módon, egy alapvető webalkalmazással.

Áttekintünk néhány alapvető konfigurációt, egy kezelőfelületet, gyors adatkezelést és kivételkezelést.

2. Beállítás

Először is használjuk a Spring Initializr programot a projektünk alapjának létrehozásához.

A létrehozott projekt a Boot szülőre támaszkodik:

 org.springframework.boot spring-boot-starter-parent 2.2.2.KÖZLEMÉNY 

A kezdeti függőségek meglehetősen egyszerűek lesznek:

 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 

3. Az alkalmazás konfigurálása

Ezután konfigurálunk egy egyszerűt fő- osztály az alkalmazásunkhoz:

@SpringBootApplication public class Application {public static void main (String [] args) {SpringApplication.run (Application.class, args); }} 

Figyelje meg, hogyan használjuk @SpringBootApplication mint elsődleges alkalmazáskonfigurációs osztályunk; a kulisszák mögött ez egyenértékű @ Konfiguráció, @EnableAutoConfiguration, és @ComponentScan együtt.

Végül meghatározunk egy egyszerűt alkalmazás.tulajdonságok fájl - amelynek egyelőre csak egy tulajdonsága van:

szerver.port = 8081 

szerver port a kiszolgáló portját az alapértelmezett 8080-ról 8081-re változtatja; természetesen sokkal több Spring Boot tulajdonság érhető el.

4. Egyszerű MVC nézet

Adjunk hozzá egy egyszerű kezelőfelületet a Thymeleaf segítségével.

Először hozzá kell adnunk a tavaszi-bakancs-indító-timeleaf függőség a mi pom.xml:

 org.springframework.boot spring-boot-starter-thymeleaf 

Ez alapértelmezés szerint engedélyezi a Thymeleaf alkalmazást - nincs szükség további konfigurálásra.

Mostantól konfigurálhatjuk alkalmazás.tulajdonságok:

spring.thymeleaf.cache = false spring.thymeleaf.enabled = true spring.thymeleaf.prefix = classpath: / templates / spring.thymeleaf.suffix = .html spring.application.name = Bootstrap Spring Boot 

Ezután meghatározunk egy egyszerű vezérlőt és egy alapvető kezdőlapot - üdvözlő üzenettel:

@Controller public class SimpleController {@Value ("$ {spring.application.name}") String appName; @GetMapping ("/") public String homePage (Model model) {model.addAttribute ("appName", appName); hazatérni"; }} 

Végül itt van a miénk home.html:

 Kezdőlap 

Üdvözöljük a mi App

Vegye figyelembe, hogyan használtunk egy tulajdonságot, amelyet a tulajdonságunkban definiáltunk, majd beadtuk, hogy megmutathassuk a kezdőlapunkon.

5. Biztonság

Ezután adjunk hozzá biztonságot alkalmazásunkhoz - először a biztonsági indítót is belefoglalva:

 org.springframework.boot spring-boot-starter-security 

Mostanra remélhetőleg észrevesz egy mintát - a legtöbb tavaszi könyvtár egyszerűen importálható projektünkbe az egyszerű Boot starterek használatával.

Egyszer a spring-boot-starter-security az alkalmazás osztályútvonalától való függőség - alapértelmezés szerint az összes végpontot bármelyik használatával biztonságban tartják httpBasic vagy formLogin a Spring Security tartalmi-tárgyalási stratégiája alapján.

Éppen ezért, ha az osztályúton van az indító, akkor általában meg kell határoznunk saját egyéni biztonsági konfigurációnkat a WebSecurityConfigurerAdapter osztály:

@Configuration @EnableWebSecurity nyilvános osztály A SecurityConfig kiterjeszti a WebSecurityConfigurerAdapter {@Orride védett void konfigurációját (HttpSecurity http) a {http.authorizeRequests () .anyRequest () .permitAll () .and. }}

Példánkban korlátlan hozzáférést engedélyezünk az összes végponthoz.

Természetesen a Spring Security egy átfogó téma, amelyet nem könnyű lefedni a konfigurációs pár sorban - ezért mindenképpen arra biztatom, hogy mélyebben foglalkozzon a témával.

6. Egyszerű kitartás

Kezdjük azzal, hogy meghatározzuk az adatmodellünket - egy egyszerű Könyv entitás:

@Entity public class Book {@Id @GeneratedValue (strategy = GenerationType.AUTO) private long id; @Column (nullable = false, egyedi = true) private String title; @Column (nullable = false) private String szerző; }

És a tárháza, a Spring Data ideális kihasználásával:

a nyilvános felület BookRepository kiterjeszti a CrudRepository {List findByTitle (String title); }

Végül természetesen konfigurálnunk kell az új perzisztencia rétegünket:

@EnableJpaRepositories ("com.baeldung.persistence.repo") @EntityScan ("com.baeldung.persistence.model") @SpringBootApplication public class Application {...}

Vegye figyelembe, hogy a következőket használjuk:

  • @EnableJpaRepositories hogy a megadott csomagot tárolja-e tárolók számára
  • @EntityScan hogy felvegyük JPA-entitásainkat

A dolgok egyszerűségének megőrzése érdekében itt H2 memóriában lévő adatbázist használunk - hogy ne legyenek külső függőségeink a projekt futtatásakor.

Miután belefoglaltuk a H2-függőséget, A Spring Boot automatikusan felismeri és beállítja kitartásunkat az adatforrás tulajdonságain kívül további konfigurációra nincs szükség:

spring.datasource.driver-class-name = org.h2.Driver spring.datasource.url = jdbc: h2: mem: bootapp; DB_CLOSE_DELAY = -1 spring.datasource.username = sa spring.datasource.password = 

Természetesen, a biztonsághoz hasonlóan, a kitartás is tágabb téma, mint ez az alapkészlet itt, és amelyet mindenképpen tovább kell vizsgálnia.

7. Web és a vezérlő

Ezután vessünk egy pillantást egy webszintre - és ezt egy egyszerű vezérlő - a BookController.

Megvalósítjuk az alapvető CRUD műveleteket Könyv erőforrások egyszerű ellenőrzéssel:

@RestController @RequestMapping ("/ api / books") public class BookController {@Autowired private BookRepository bookRepository; @GetMapping public Iterable findAll () {return bookRepository.findAll (); } @GetMapping ("/ title / {bookTitle}") public list findByTitle (@PathVariable String bookTitle) {return bookRepository.findByTitle (bookTitle); } @GetMapping ("/ {id}") public Book findOne (@PathVariable Long id) {return bookRepository.findById (id) .orElseThrow (BookNotFoundException :: new); } @PostMapping @ResponseStatus (HttpStatus.CREATED) public Book create (@RequestBody Book book) {return bookRepository.save (book); } @DeleteMapping ("/ {id}") public void delete (@PathVariable Long id) {bookRepository.findById (id) .orElseThrow (BookNotFoundException :: new); bookRepository.deleteById (id); } @PutMapping ("/ {id}") nyilvános könyvfrissítési könyv (@RequestBody Book book, @PathVariable Long id) {if (book.getId ()! = Id) {dobjon új BookIdMismatchException (); } bookRepository.findById (id) .orElseThrow (BookNotFoundException :: new); return bookRepository.save (könyv); }} 

Mivel az alkalmazás ezen aspektusa egy API, a @RestController annotáció itt - amely megfelel a @Vezérlő együtt @ResponseBody - úgy, hogy minden módszer a visszaküldött erőforrást a HTTP-válaszhoz rendeli.

Csak egy megjegyzésre érdemes felhívni a figyelmet - kitesszük a sajátunkat Könyv entitás mint külső erőforrásunk itt. Ez az itteni egyszerű alkalmazásunknak megfelel, de egy valós alkalmazásban valószínűleg el kívánja különíteni ezt a két fogalmat.

8. Hibakezelés

Most, hogy az alapalkalmazás készen áll, összpontosítsunk egyszerű központosított hibakezelő mechanizmus felhasználásával @ControllerAdvice:

A @ControllerAdvice nyilvános osztály RestExceptionHandler kiterjeszti a ResponseEntityExceptionHandler {@ExceptionHandler ({BookNotFoundException.class}) védett ResponseEntity handleNotFound (Exception ex, WebRequest kérés) {return handExceptionInternal (ex, "Book request" ; } @ExceptionHandler ({BookIdMismatchException.class, ConstraintViolationException.class, DataIntegrityViolationException.class}) public ResponseEntity handleBadRequest (Exception ex, WebRequest request) {return handExceptionInternal (ex, ex.get (ex, ex.get) ); }} 

Az itt kezelt szokásos kivételeken túl egyedi kivételt is használunk:

BookNotFoundException:

public class BookNotFoundException kiterjeszti a RuntimeException {public BookNotFoundException (karakterlánc üzenet, dobható ok) {super (üzenet, ok); } // ...} 

Ennek képet kell adnia arról, hogy mi lehetséges ezzel a globális kivételkezelő mechanizmussal. Ha teljes megvalósítást szeretne, tekintse meg a részletes oktatóanyagot.

Ne feledje, hogy a Spring Boot egy /hiba alapértelmezés szerint leképezés. Személyre szabhatjuk nézetét egy egyszerű létrehozásával error.html:

 Hiba történt [állapot] hiba 

üzenet

A Boot legtöbb más aspektusához hasonlóan ezt is egyszerű tulajdonsággal tudjuk ellenőrizni:

server.error.path = / hiba2

9. Tesztelés

Végül teszteljük az új Books API-t.

Használhatjuk @SpringBootTest az alkalmazáskörnyezet betöltéséhez és annak ellenőrzéséhez, hogy nincsenek-e hibák az alkalmazás futtatásakor:

@RunWith (SpringRunner.class) @SpringBootTest nyilvános osztály SpringContextTest {@Test public void contextLoads () {}}

Ezután adjunk hozzá egy JUnit tesztet, amely a RestAssured használatával ellenőrzi a hívásokat az általunk írt API-ra:

public class SpringBootBootstrapLiveTest {private static final String API_ROOT = "// localhost: 8081 / api / books"; privát Könyv createRandomBook () {Könyvkönyv = új Könyv (); book.setTitle (randomAlfabetikus (10)); book.setAuthor (randomAlphabetic (15)); visszatérő könyv; } privát karakterlánc createBookAsUri (könyv) {Response response = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (book) .post (API_ROOT); return API_ROOT + "/" + response.jsonPath (). get ("id"); }} 

Először megpróbálhatunk könyveket találni variáns módszerekkel:

@Test public void whenGetAllBooks_thenOK () {Response response = RestAssured.get (API_ROOT); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); } @Test public void whenGetBooksByTitle_thenOK () {Book book = createRandomBook (); createBookAsUri (könyv); Válaszválasz = RestAssured.get (API_ROOT + "/ title /" + book.getTitle ()); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); assertTrue (response.as (List.class) .size ()> 0); } @Test public void whenGetCreatedBookById_thenOK () {Book book = createRandomBook (); Karakterlánc helye = createBookAsUri (könyv); Válasz válasz = RestAssured.get (hely); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); assertEquals (könyv.getTitle (), válasz.jsonPath () .get ("cím")); } @Test public void whenGetNotExistBookById_thenNotFound () {Response response = RestAssured.get (API_ROOT + "/" + randomNumeric (4)); assertEquals (HttpStatus.NOT_FOUND.value (), response.getStatusCode ()); } 

Ezután teszteljük egy új könyv létrehozását:

@Test public void whenCreateNewBook_thenCreated () {Book book = createRandomBook (); Válaszválasz = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (book) .post (API_ROOT); assertEquals (HttpStatus.CREATED.value (), response.getStatusCode ()); } @Test public void whenInvalidBook_thenError () {Book book = createRandomBook (); book.setAuthor (null); Válaszválasz = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (book) .post (API_ROOT); assertEquals (HttpStatus.BAD_REQUEST.value (), response.getStatusCode ()); } 

Meglévő könyv frissítése:

@Test public void whenUpdateCreatedBook_thenUpdated () {Book book = createRandomBook (); Karakterlánc helye = createBookAsUri (könyv); book.setId (Long.parseLong (hely.split ("api / könyvek /") [1])); book.setAuthor ("newAuthor"); Válasz válasz = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (könyv) .put (hely); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); válasz = RestAssured.get (hely); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); assertEquals ("newAuthor", response.jsonPath () .get ("szerző")); } 

És törölj egy könyvet:

@Test public void whenDeleteCreatedBook_thenOk () {Book book = createRandomBook (); Karakterlánc helye = createBookAsUri (könyv); Válasz válasz = RestAssured.delete (hely); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); válasz = RestAssured.get (hely); assertEquals (HttpStatus.NOT_FOUND.value (), response.getStatusCode ()); } 

10. Következtetés

Ez a Spring Boot gyors, de átfogó bevezetője volt.

Természetesen alig karcoltuk meg itt a felszínt - ennél a keretrendszernél sokkal többről van szó, amelyeket egyetlen bevezető cikkben áttekinthetünk.

Pontosan ezért nem csak egyetlen cikkünk van a Bootról az oldalon.

Példáink teljes forráskódja itt is, mint mindig, a GitHubon van.