Spring Boot Tutorial - 簡単なアプリケーションのブートストラップ

Spring Bootチュートリアル-シンプルなアプリケーションのブートストラップ

1. 概要

Spring Bootは、Springプラットフォームに加えられた、設定よりも規約に重点を置いた独創的な追加機能です。最小限の労力で開始し、スタンドアロンの実稼働グレードのアプリケーションを作成するのに非常に便利です。

This tutorial is a starting point for Boot –基本的なWebアプリケーションを使用して簡単に開始する方法。

いくつかのコア構成、フロントエンド、迅速なデータ操作、および例外処理について説明します。

参考文献:

Spring Bootでデフォルトポートを変更する方法

Spring Bootアプリケーションでデフォルトのポートを変更する方法をご覧ください。

Spring Boot Startersの紹介

最も一般的なSpring Boot Startersの簡単な概要と、実際のプロジェクトでそれらを使用する方法の例。

2. セットアップ

まず、Spring Initializrを使用してプロジェクトのベースを生成しましょう。

生成されたプロジェクトは、親のブートに依存します。


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

最初の依存関係は非常に単純になります。


    org.springframework.boot
    spring-boot-starter-web


    org.springframework.boot
    spring-boot-starter-data-jpa


    com.h2database
    h2

3. アプリケーション構成

次に、アプリケーション用に単純なmainクラスを構成します。

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

プライマリアプリケーション構成クラスとして@SpringBootApplicationを使用していることに注目してください。舞台裏では、これは@Configuration@EnableAutoConfiguration、および@ComponentScanを合わせたものに相当します。

最後に、単純なapplication.propertiesファイルを定義します。このファイルには今のところ1つのプロパティしかありません。

server.port=8081

server.portは、サーバーポートをデフォルトの8080から8081に変更します。もちろん、もっと多くのSpring Boot properties availableがあります。

4. シンプルなMVCビュー

次に、Thymeleafを使用して簡単なフロントエンドを追加しましょう。

まず、spring-boot-starter-thymeleafの依存関係をpom.xmlに追加する必要があります。


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

これにより、デフォルトでThymeleafが有効になります。追加の構成は不要です。

これで、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

次に、簡単なコントローラーと基本的なホームページを定義し、ウェルカムメッセージを表示します。

@Controller
public class SimpleController {
    @Value("${spring.application.name}")
    String appName;

    @GetMapping("/")
    public String homePage(Model model) {
        model.addAttribute("appName", appName);
        return "home";
    }
}

最後に、home.htmlを示します。


Home Page

Hello !

Welcome to Our App

プロパティで定義したプロパティをどのように使用し、それを挿入してホームページに表示できるようにすることに注意してください。

5. セキュリティ

次に、最初にセキュリティスターターを含めることで、アプリケーションにセキュリティを追加しましょう。


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

ここまでで、パターン(most Spring libraries are easily imported into our project with the use of simple Boot starters)に気付いていることを願っています。

spring-boot-starter-securityがアプリケーションのクラスパスに依存すると、すべてのエンドポイントは、Spring Securityのコンテンツネゴシエーション戦略に基づいてhttpBasicまたはformLoginのいずれかを使用して、デフォルトで保護されます。

そのため、クラスパスにスターターがある場合は、通常、WebSecurityConfigurerAdapterクラスを拡張して独自のカスタムセキュリティ構成を定義する必要があります。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest()
            .permitAll()
            .and().csrf().disable();
    }
}

この例では、すべてのエンドポイントへの無制限のアクセスを許可しています。

 

もちろん、Spring Securityは広範なトピックであり、数行の構成では簡単にカバーできないため、go deeper into the topicを使用することを強くお勧めします。

6. 単純な永続性

データモデルを定義することから始めましょう–単純なBookエンティティ:

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(nullable = false, unique = true)
    private String title;

    @Column(nullable = false)
    private String author;
}

そしてそのリポジトリは、ここでSpring Dataをうまく利用しています:

public interface BookRepository extends CrudRepository {
    List findByTitle(String title);
}

最後に、もちろん新しい永続化レイヤーを構成する必要があります。

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

以下を使用していることに注意してください。

  • @EnableJpaRepositoriesは、指定されたパッケージをスキャンしてリポジトリを探します

  • @EntityScanでJPAエンティティを取得します

簡単にするために、ここではH2インメモリデータベースを使用しています。これにより、プロジェクトの実行時に外部の依存関係がなくなります。

H2依存関係を含めると、データソースプロパティ以外の追加の構成を必要としないSpring Boot auto-detects it and sets up our persistence

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=

もちろん、セキュリティと同様に、永続性はここでのこの基本セットよりも幅広いトピックであり、certainly explore furtherにする必要があります。

7. Webとコントローラー

次に、Web層を見てみましょう。まず、単純なコントローラーであるBookControllerを設定します。

いくつかの簡単な検証を使用して、Bookリソースを公開する基本的なCRUD操作を実装します。

@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}")
    public Book updateBook(@RequestBody Book book, @PathVariable Long id) {
        if (book.getId() != id) {
          throw new BookIdMismatchException();
        }
        bookRepository.findById(id)
          .orElseThrow(BookNotFoundException::new);
        return bookRepository.save(book);
    }
}

アプリケーションのこの側面がAPIであるため、ここでは@RestControllerアノテーション(@Controller@ResponseBodyに相当)を使用して、各メソッドが返されたリソースを正しくマーシャルするようにしました。 HTTP応答に。

ここでは、Bookエンティティを外部リソースとして公開しています。 ここでの単純なアプリケーションでは問題ありませんが、実際のアプリケーションでは、separate these two conceptsが必要になる可能性があります。

8. エラー処理

コアアプリケーションの準備ができたので、@ControllerAdviceを使用してa simple centralized error handling mechanismに焦点を当てましょう。

@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ BookNotFoundException.class })
    protected ResponseEntity handleNotFound(
      Exception ex, WebRequest request) {
        return handleExceptionInternal(ex, "Book not found",
          new HttpHeaders(), HttpStatus.NOT_FOUND, request);
    }

    @ExceptionHandler({ BookIdMismatchException.class,
      ConstraintViolationException.class,
      DataIntegrityViolationException.class })
    public ResponseEntity handleBadRequest(
      Exception ex, WebRequest request) {
        return handleExceptionInternal(ex, ex.getLocalizedMessage(),
          new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
    }
}


ここで処理している標準の例外に加えて、カスタム例外も使用しています。

BookNotFoundException

public class BookNotFoundException extends RuntimeException {

    public BookNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
    // ...
}

これにより、このグローバルな例外処理メカニズムで何が可能かがわかります。 完全な実装を確認したい場合は、the in-depth tutorialをご覧ください。

Spring Bootはデフォルトで/errorマッピングも提供することに注意してください。 単純なerror.htmlを作成することで、ビューをカスタマイズできます。


Error Occurred

    

Error Occurred!

[status] error

message

Bootの他のほとんどの側面と同様に、単純なプロパティでそれを制御できます。

server.error.path=/error2

9. テスト

最後に、新しいBooksAPIをテストしてみましょう。

すぐに@SpringBootTestを使用して、アプリケーションコンテキストをロードします。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Application.class }, webEnvironment
  = WebEnvironment.DEFINED_PORT)
public class SpringBootBootstrapLiveTest {

    private static final String API_ROOT
      = "http://localhost:8081/api/books";

    private Book createRandomBook() {
        Book book = new Book();
        book.setTitle(randomAlphabetic(10));
        book.setAuthor(randomAlphabetic(15));
        return book;
    }

    private String createBookAsUri(Book book) {
        Response response = RestAssured.given()
          .contentType(MediaType.APPLICATION_JSON_VALUE)
          .body(book)
          .post(API_ROOT);
        return API_ROOT + "/" + response.jsonPath().get("id");
    }
}

最初に、さまざまな方法を使用して本を見つけることができます。

@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(book);
    Response response = 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();
    String location = createBookAsUri(book);
    Response response = RestAssured.get(location);

    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertEquals(book.getTitle(), response.jsonPath()
      .get("title"));
}

@Test
public void whenGetNotExistBookById_thenNotFound() {
    Response response = RestAssured.get(API_ROOT + "/" + randomNumeric(4));

    assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}

次に、新しい本の作成をテストします。

@Test
public void whenCreateNewBook_thenCreated() {
    Book book = createRandomBook();
    Response response = 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);
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .post(API_ROOT);

    assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode());
}

既存の本を更新します。

@Test
public void whenUpdateCreatedBook_thenUpdated() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    book.setId(Long.parseLong(location.split("api/books/")[1]));
    book.setAuthor("newAuthor");
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .put(location);

    assertEquals(HttpStatus.OK.value(), response.getStatusCode());

    response = RestAssured.get(location);

    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertEquals("newAuthor", response.jsonPath()
      .get("author"));
}

そして本を削除します:

@Test
public void whenDeleteCreatedBook_thenOk() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    Response response = RestAssured.delete(location);

    assertEquals(HttpStatus.OK.value(), response.getStatusCode());

    response = RestAssured.get(location);
    assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}

10. 結論

これは、Spring Bootの簡単かつ包括的な紹介です。

もちろん、ここでは表面をかじっただけです。このフレームワークには、1つの紹介記事でカバーできるものがたくさんあります。

ここでの例の完全なソースコードは、いつものように、over on GitHubです。