Spring Boot Tutorial - Bootstrap a Simple Application

1. Oversikt

Spring Boot er et meningsfylt, konvensjon-over-konfigurasjonsfokusert tillegg til Spring-plattformen - veldig nyttig for å komme i gang med minimal innsats og lage frittstående applikasjoner i produksjonsklasse.

Denne opplæringen er et utgangspunkt for Boot - en måte å komme i gang på en enkel måte, med en grunnleggende webapplikasjon.

Vi går over noen kjernekonfigurasjoner, en front-end, rask datamanipulering og unntakshåndtering.

2. Oppsett

La oss først bruke Spring Initializr til å generere basen for prosjektet vårt.

Det genererte prosjektet er avhengig av Boot-foreldrene:

 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 

De første avhengighetene kommer til å være ganske enkle:

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

3. Programkonfigurasjon

Deretter konfigurerer vi en enkel hoved- klasse for søknaden vår:

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

Legg merke til hvordan vi bruker @SpringBootApplication som vår primære applikasjonskonfigurasjonsklasse; bak kulissene tilsvarer det @Konfigurasjon, @EnableAutoConfiguration, og @ComponentScan sammen.

Til slutt vil vi definere en enkel application.properties fil - som foreløpig bare har en egenskap:

server.port = 8081 

server.port endrer serverporten fra standard 8080 til 8081; det er selvfølgelig mange flere Spring Boot-egenskaper tilgjengelig.

4. Enkel MVC-visning

La oss nå legge til en enkel frontend ved hjelp av Thymeleaf.

Først må vi legge til spring-boot-starter-thymeleaf avhengighet til vår pom.xml:

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

Dette muliggjør Thymeleaf som standard - ingen ekstra konfigurasjon er nødvendig.

Vi kan nå konfigurere den i vår application.properties:

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

Deretter definerer vi en enkel kontroller og en grunnleggende hjemmeside - med en velkomstmelding:

@Controller offentlig klasse SimpleController {@Value ("$ {spring.application.name}") String appName; @GetMapping ("/") public String homePage (Model model) {model.addAttribute ("appName", appName); vende hjem"; }} 

Til slutt, her er vår hjem.html:

 Hjemmeside 

Velkommen til appen vår

Legg merke til hvordan vi brukte en eiendom vi definerte i eiendommene våre - og deretter injiserte den slik at vi kan vise den på hjemmesiden vår.

5. Sikkerhet

La oss deretter legge til sikkerhet i applikasjonen vår - ved først å inkludere sikkerhetsstarter:

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

Nå ser du forhåpentligvis et mønster - de fleste vårbiblioteker importeres enkelt til prosjektet vårt ved bruk av enkle oppstartsstartere.

Først når vår-boot-starter-sikkerhet avhengighet av klassestien til applikasjonen - alle sluttpunkter er sikret som standard, ved hjelp av en av dem httpBasic eller formLogin basert på Spring Securitys innholdsforhandlingsstrategi.

Derfor, hvis vi har starteren på klassestien, bør vi vanligvis definere vår egen tilpassede sikkerhetskonfigurasjon ved å utvide WebSecurityConfigurerAdapter klasse:

@Configuration @EnableWebSecurity offentlig klasse SecurityConfig utvider WebSecurityConfigurerAdapter {@Override-beskyttet ugyldig konfigurering (HttpSecurity http) kaster unntak {http.authorizeRequests () .anyRequest () .permitAll () .and (). Csrf (). }}

I vårt eksempel tillater vi ubegrenset tilgang til alle sluttpunkter.

Selvfølgelig er vårsikkerhet et omfattende tema og ikke lett dekket av et par konfigurasjonslinjer - så jeg oppfordrer deg definitivt til å gå dypere inn i emnet.

6. Enkel utholdenhet

La oss starte med å definere datamodellen vår - en enkel Bok enhet:

@Entity public class Bok {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat lang id; @Column (nullable = false, unique = true) privat strengtittel; @Column (nullable = false) privat strengforfatter; }

Og dets lager, som gjør god bruk av Spring Data her:

offentlig grensesnitt BookRepository utvider CrudRepository {List findByTitle (String title); }

Til slutt må vi selvfølgelig konfigurere vårt nye utholdenhetslag:

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

Merk at vi bruker:

  • @EnableJpaRepositories for å skanne den angitte pakken for arkiver
  • @EntityScan å hente JPA-enhetene våre

For å gjøre ting enkelt, bruker vi en H2-minne-database her - slik at vi ikke har noen eksterne avhengigheter når vi kjører prosjektet.

Når vi inkluderer H2-avhengighet, Spring Boot oppdager den automatisk og setter opp vår utholdenhet uten behov for ekstra konfigurasjon, annet enn datakildeegenskapene:

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 = 

Selvfølgelig, som sikkerhet, er utholdenhet et bredere tema enn dette grunnleggende settet her, og du bør absolutt utforske videre.

7. Nett og kontrolleren

Deretter, la oss ta en titt på et nettnivå - og vi starter det med å sette opp en enkel kontroller - BookController.

Vi implementerer grunnleggende CRUD-operasjoner som avslører Bok ressurser med noen enkle valideringer:

@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) {retur 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}") offentlig ugyldig sletting (@PathVariable Long id) {bookRepository.findById (id) .orElseThrow (BookNotFoundException :: new); bookRepository.deleteById (id); } @PutMapping ("/ {id}") offentlig bokoppdateringsbok (@RequestBody Book book, @PathVariable Long id) {if (book.getId ()! = Id) {kast ny BookIdMismatchException (); } bookRepository.findById (id) .orElseThrow (BookNotFoundException :: new); returner bokenRepository.save (bok); }} 

Gitt at dette aspektet av applikasjonen er en API, brukte vi @RestController kommentar her - som tilsvarer en @Kontrollør sammen med @ResponseBody - slik at hver metode marshallerer den returnerte ressursen rett til HTTP-responsen.

Bare ett notat som er verdt å påpeke - vi avslører vårt Bok enhet som vår eksterne ressurs her. Det er greit for vår enkle applikasjon her, men i en virkelig applikasjon vil du sannsynligvis ønske å skille disse to konseptene.

8. Feilhåndtering

Nå som kjerneapplikasjonen er klar til bruk, la oss fokusere på en enkel sentralisert feilhåndteringsmekanisme ved hjelp av @ControllerAdvice:

@ControllerAdvice offentlig klasse RestExceptionHandler utvider ResponseEntityExceptionHandler {@ExceptionHandler ({BookNotFoundException.class}) beskyttet ResponseEntity handleNotFound (Exception ex, WebRequest request) {return handleExceptionInternal (ex, "Book not found", new HttpHttpeaders) ; } @ExceptionHandler ({BookIdMismatchException.class, ConstraintViolationException.class, DataIntegrityViolationException.class}) public ResponseEntity handleBadRequest (Exception ex, WebRequest request) {return handleExceptionInternal (ex, ex.getLocalisedMessage, Http, HAD) ); }} 

Utover standard unntakene vi håndterer her, bruker vi også et tilpasset unntak:

BookNotFoundException:

offentlig klasse BookNotFoundException utvider RuntimeException {public BookNotFoundException (strengmelding, kastbar årsak) {super (melding, årsak); } // ...} 

Dette skal gi deg en ide om hva som er mulig med denne globale unntakshåndteringsmekanismen. Hvis du vil se en full implementering, kan du ta en titt på den grundige veiledningen.

Merk at Spring Boot også gir en /feil kartlegging som standard. Vi kan tilpasse visningen ved å lage en enkel error.html:

 Feil oppsto [status] feil 

beskjed

Som de fleste andre aspekter i Boot, kan vi kontrollere det med en enkel egenskap:

server.error.path = / error2

9. Testing

Til slutt, la oss teste vår nye Books API.

Vi kan gjøre bruk av @SpringBootTest for å laste inn applikasjonskonteksten og kontrollere at det ikke er noen feil når du kjører appen:

@RunWith (SpringRunner.class) @SpringBootTest offentlig klasse SpringContextTest {@Test offentlig ugyldig contextLoads () {}}

Deretter la oss legge til en JUnit-test som verifiserer samtalene til API-en vi er skrevet, ved hjelp av RestAssured:

offentlig klasse SpringBootBootstrapLiveTest {private static final String API_ROOT = "// localhost: 8081 / api / books"; private Book createRandomBook () {Book book = new Book (); book.setTitle (randomAlphabetic (10)); book.setAuthor (randomAlphabetic (15)); returbok; } privat streng createBookAsUri (bokbok) {Response response = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (book) .post (API_ROOT); returner API_ROOT + "/" + respons.jsonPath (). get ("id"); }} 

Først kan vi prøve å finne bøker ved hjelp av variantmetoder:

@Test offentlig ugyldig nårGetAllBooks_thenOK () {Response response = RestAssured.get (API_ROOT); assertEquals (HttpStatus.OK.value (), respons.getStatusCode ()); } @Test offentlig ugyldig nårGetBooksByTitle_thenOK () {Book book = createRandomBook (); createBookAsUri (bok); Svarrespons = RestAssured.get (API_ROOT + "/ tittel /" + book.getTitle ()); assertEquals (HttpStatus.OK.value (), respons.getStatusCode ()); assertTrue (respons.as (List.class) .størrelse ()> 0); } @Test offentlig ugyldig nårGetCreatedBookById_thenOK () {Book book = createRandomBook (); Stringplassering = createBookAsUri (bok); Svarrespons = RestAssured.get (plassering); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); assertEquals (book.getTitle (), response.jsonPath () .get ("title")); } @Test offentlig ugyldig nårGetNotExistBookById_thenNotFound () {Response response = RestAssured.get (API_ROOT + "/" + randomNumeric (4)); assertEquals (HttpStatus.NOT_FOUND.value (), response.getStatusCode ()); } 

Deretter tester vi å lage en ny bok:

@Test offentlig ugyldig nårCreateNewBook_thenCreated () {Book book = createRandomBook (); Svarrespons = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (bok) .post (API_ROOT); assertEquals (HttpStatus.CREATED.value (), respons.getStatusCode ()); } @Test offentlig ugyldig nårInvalidBook_thenError () {Book book = createRandomBook (); book.setAuthor (null); Svarrespons = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (bok) .post (API_ROOT); assertEquals (HttpStatus.BAD_REQUEST.value (), response.getStatusCode ()); } 

Oppdater en eksisterende bok:

@Test offentlig ugyldig nårUpdateCreatedBook_thenUpdated () {Book book = createRandomBook (); Stringplassering = createBookAsUri (bok); book.setId (Long.parseLong (location.split ("api / books /") [1])); book.setAuthor ("newAuthor"); Svarrespons = RestAssured.given () .contentType (MediaType.APPLICATION_JSON_VALUE) .body (bok) .put (plassering); assertEquals (HttpStatus.OK.value (), respons.getStatusCode ()); respons = RestAssured.get (plassering); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); assertEquals ("newAuthor", response.jsonPath () .get ("forfatter")); } 

Og slett en bok:

@Test offentlig ugyldig nårDeleteCreatedBook_thenOk () {Book book = createRandomBook (); Stringplassering = createBookAsUri (bok); Svarrespons = RestAssured.delete (plassering); assertEquals (HttpStatus.OK.value (), response.getStatusCode ()); respons = RestAssured.get (plassering); assertEquals (HttpStatus.NOT_FOUND.value (), response.getStatusCode ()); } 

10. Konklusjon

Dette var en rask, men omfattende intro til Spring Boot.

Vi klarte selvfølgelig knapt overflaten her - det er mye mer i dette rammeverket som vi kan dekke i en enkelt intro-artikkel.

Det er nettopp derfor vi ikke bare har en eneste artikkel om Boot på nettstedet.

Den fulle kildekoden til eksemplene våre her er som alltid over på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found