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

1概要

Spring Bootは、Springプラットフォームにコンスティテューションオーバーコンスティチューションを重視した追加機能です。最小限の労力で作業を開始し、スタンドアロンの本番グレードのアプリケーションを作成するのに非常に役立ちます。

  • このチュートリアルはBoot ** の出発点です - 基本的なWebアプリケーションを使って簡単な方法で始める方法です。

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

2セットアップ

まず、https://start.spring.io/[Spring Initializr]を使用してプロジェクトのベースを生成しましょう。

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

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
    <relativePath/>
</parent>

初期の依存関係は非常に単純になるでしょう。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

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に変更します。もちろんもっとたくさんのhttps://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html[利用可能なスプリングブートのプロパティ]があります。

4シンプルMVCビュー

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

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

これはデフォルトで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 です。

<html>
<head><title>Home Page</title></head>
<body>
<h1>Hello !</h1>
<p>Welcome to <span th:text="${appName}">Our App</span></p>
</body>
</html>

私達が私達の特性で定義した特性をどのように使用したかに注意してください - そしてそれを注入して私達のホームページにそれを表示できるようにします。

5セキュリティ

次に、まずセキュリティスターターを含めて、アプリケーションにセキュリティを追加しましょう。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

これまでのところ、パターンに気付いていることをお勧めします。 ほとんどのSpringライブラリは、単純なBootスターターを使用して簡単にプロジェクトにインポートされます

アプリケーションのクラスパスへの 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は広範なトピックであり、2、3行の構成では簡単に説明できないものです。

** 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<Book, Long> {
    List<Book> findByTitle(String title);
}

最後に、もちろん私たちの新しい永続層を設定する必要があります。

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

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

指定されたパッケージをスキャンする** @ EnableJpaRepositories

リポジトリ ** @ EntityScan - JPAエンティティを取得します

簡単にするために、ここではH2インメモリデータベースを使用しています。そのため、プロジェクトを実行するときに外部の依存関係はありません。

H2依存関係を含めると、Spring Bootはそれを自動的に検出し、データソースプロパティ以外の追加の設定を必要とせずに永続性を設定します。

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=

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

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 アノテーションを使用しました。これは、 @ ResponseBody とともに @ Controller__と同等です - 各メソッドは、返されたリソースをHTTP応答に整列化します。

1つだけ指摘しておく価値があります - ここでは、外部のリソースとして Book エンティティを公開しています。ここでの単純なアプリケーションではこれで問題ありませんが、実際のアプリケーションでは、次のリンクを使用することをお勧めします。

8エラー処理

コアアプリケーションを使用する準備が整ったので、 @ ControllerAdvice を使用した 簡単な集中型エラー処理メカニズム に焦点を絞りましょう。

@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ BookNotFoundException.class })
    protected ResponseEntity<Object> 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<Object> 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);
    }
   //...
}

これにより、このグローバルな例外処理メカニズムで何が可能になるのかがわかります。完全なインプリメンテーションを見たい場合は、リンクを見てください:/例外処理 - 春休み - [詳細なチュートリアル]。

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

<html lang="en">
<head><title>Error Occurred</title></head>
<body>
    <h1>Error Occurred!</h1>
    <b>[<span th:text="${status}">status</span>]        <span th:text="${error}">error</span>
    </b>
    <p th:text="${message}">message</p>
</body>
</html>

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

server.error.path=/error2

9テスト中

最後に、新しいBooks APIをテストしましょう。

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

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

    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つの紹介記事で説明できることがたくさんあります。

ここにある私たちの例の完全なソースコードは、いつものように、https://github.com/eugenp/tutorials/tree/master/spring-boot-bootstrap[GitHubで動く]です。