Spring Boot:基礎から実践まで

概要

Spring BootはSpringの迅速な利用方法を提供し、設定より規約という思想に基づいています。

Springの欠点

  • 設定が煩雑
  • 依存関係が複雑

Spring Bootの機能

  • 自動設定
    • どのSpring設定を使用し、使用しないかをSpring Bootが自動的に判断
  • スターターデペンデンシ
  • 補助機能
    • 埋め込みサーバー、セキュリティ、メトリクス、ヘルスチェック、外部設定など、大規模プロジェクトで一般的な非機能的特性を提供

Spring BootはSpringの機能を拡張するものではなく、Springを迅速に利用する方法を提供するものです

クイックスタート

  • Mavenプロジェクトの作成
  • Spring Bootスターターデペンデンシの導入
<!--Spring Bootプロジェクトで継承する親プロジェクト-->
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.2.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
  • コントローラーの定義
package com.example.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {

    @RequestMapping("/greet")
    public String greet(){
        return "こんにちは、Spring Boot!";
    }
}
  • 起動クラスの作成
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 起動クラス。Spring Bootプロジェクトのエントリーポイント
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}
  • 起動とテスト

Spring Bootプロジェクトの迅速な構築

IDEAを介した迅速な構築方法

スターターデペンデンシの原理分析

spring-boot-starter-parent

各技術のバージョン情報を定義し、最適な技術バージョンの組み合わせを提供します

spring-boot-starter-web

各スターターでは、その機能を完成させる必要な座標の集合が定義されており、その大部分のバージョン情報は親プロジェクトから来ています。

私たちのプロジェクトはparentを継承し、starterを導入することで、依存関係を通じて必要なjarパッケージを簡単に取得でき、バージョン競合などの問題が発生しません

設定

分類

Spring Bootは規約に基づいているため、多くの設定にはデフォルト値がありますが、独自の設定でデフォルト設定を置き換えたい場合は、application.propertiesまたはapplication.yml(application.yaml)を使用して設定できます。

  • Spring Bootは2種類の設定ファイルタイプを提供しています:propertiesとyml/yaml
  • デフォルト設定ファイル名:application
  • 同じディレクトリレベルでの優先順位:properties > yml > yaml

YAML

紹介

YAMLは、コンピュータが認識できる直感的なデータシリアライズ形式であり、人間が読みやすく、スクリプト言語と簡単に連携できます。YAMLライブラリをサポートする異なるプログラミング言語のプログラムにインポートできます。YAMLファイルはデータを中心としており、従来のXML方式よりも簡潔です。

YAMLファイルの拡張子は.ymlまたは.yamlを使用できます

構文

  • 大文字と小文字を区別
  • データ値の前にスペースが必要で、区切り文字として機能します
  • インデントを使用して階層関係を表現
  • インデントにはTabキーの使用は許可されておらず、スペースのみが使用可能です(各システムのTabに対応するスペース数が異なるため、階層が混乱する可能性があります)
  • インデントのスペース数は重要ではなく、同じレベルの要素が左側で整列していれば十分です
  • #はコメントを表し、この文字から行末まではパーサーによって無視されます

データ形式

  • オブジェクト(マップ):キーバリューペアのコレクション
user:
  name: taro
# 行内記述法
user:{name, taro}
  • 配列:順序付けられた値のグループ
locations:
  - tokyo
  - osaka
# 行内記述法
locations:[tokyo, osaka]
  • スカラー:単一で、さらに分割できない値
msg1: 'こんにちは \n 世界' # シングルクォートはエスケープ文字を無視
msg2: "こんにちは \n 世界" # ダブルクォートはエスケープ文字を認識

パラメータ参照:${key}

設定ファイルの読み取り

  • @Value
@Value("${app.name}")
private String applicationName;
app.name: サンプルアプリケーション
  • Environment
@Autowired
private Environment env;

@GetMapping("/config")
public String getConfig(){
    System.out.println(env.getProperty("app.name"));
    return "設定値取得";
}
app.name: サンプルアプリケーション
  • @ConfigurationProperties
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties{
    private String name;
    private int port;
    
    // getter/setter/toString
}
app:
  name: サンプルアプリ
  port: 8080
@Autowired
private AppProperties appProperties;

@GetMapping("/props")
public String getProps(){
    System.out.println(appProperties);
    return "プロパティ取得";
}

プロファイル動的設定ファイル

紹介

Spring Bootアプリケーションを開発する際、通常同じプログラムが開発、テスト、本番など異なる環境にインストールされます。データベースのアドレス、サーバーポートなどが異なるため、毎回設定ファイルを修正してパッケージングするのは非常に面倒です。プロファイル機能は、このような動的設定切り替えを行うために使用されます。

設定方式
  • 複数プロファイルファイル方式

複数プロファイルファイル方式–properties

spring.profiles.active=prod
server.port=8080
server.port=8081

複数プロファイルファイル方式—yml

spring:
  profiles:
    # active: prod
    active: dev
起動方式
  • 設定ファイル

プロジェクト内部設定ファイルの読み込み順序

Spring Bootプログラム起動時、以下の位置から設定ファイルが読み込まれます:

  1. file:./config/: 現在プロジェクトの/configディレクトリ下
  2. file:./ : 現在プロジェクトのルートディレクトリ
  3. classpath:/config/: classpathの/configディレクトリ
  4. classpath:/ : classpathのルートディレクトリ(一般的にはresources)

読み込み順序は上記の順序で、高優先度の設定プロパティが有効になります

整合

Junitとの統合

  1. Spring Bootプロジェクトの構築
  2. starter-testスターターデペンデンシの導入
  3. テストクラスの作成
  4. テスト関連アノテーションの追加
    • @RunWith(SpringRunner.class)
    • @SpringBootTest(classes = 起動クラス.class)
  5. テストメソッドの作成

Redisとの統合

  • Spring Bootプロジェクトの構築
  • Redisスターターデペンデンシの導入
  • Redis関連属性の設定
  • RedisTemplateテンプレートの注入
@Autowired
private RedisTemplate redisTemplate;

@Test
public void setData(){
    // データの保存
    redisTemplate.boundValueOps("username").set("yamada");
}
@Test
public void getData(){
    // オブジェクトの取得
    Object username = redisTemplate.boundValueOps("username").get();
    System.out.println(username);
}
  • テストメソッドの作成とテスト

MyBatisとの統合

  • コア設定:データベース接続情報(何に接続する?誰に接続する?どんな権限?)
  • マッピング設定:SQLマッピング(XML/アノテーション)

手順:

  1. 新しいSpring Bootプロジェクトを作成

  2. 現在のモジュールで使用する技術セットを選択(MyBatis、MySQL)

  3. データソースパラメータの設定

  4. データ層インターフェースとマッピング設定の定義

  5. テスト

MyBatis統合時のよくある問題

タイムゾーン設定問題

  1. MySQLにタイムゾーンを設定(urlの後にserverTimezone=UTCを追加)

MyBatis-Plusとの統合

  • MyBatis-PlusとMyBatisの違い
    • コーディネートのインポートが異なる
    • データ層の実装が簡略化

クイックスタート

Alibaba Cloudを使用してspring-bootを作成

本番環境(公式)

  1. Spring BootとMyBatis-Plus統合のコーディネートを手動で追加(mvnrepositoryから取得可能)

  2. データ層インターフェースとマッピングを定義し、BaseMapperを継承

Druidとの統合

  1. コーディネートのインポート

  2. 設定情報の追加

spring:
  datasource:
    druid:
      url:
      username:
      password:

第三方技術との統合

  • 対応するstarterをインポート
  • 対応する設定を設定するか、デフォルト設定を使用する

統合ケーススタディ

モジュールの構築

  1. SpringMVCとMySQLコーディネートにチェック

  2. 設定ファイルをyml形式に変更

  3. ポート番号の設定

エンティティクラスの開発

  • Lombok:Javaクラスライブラリで、POJOエンティティクラスの開発を簡略化する一連のアノテーションを提供
<!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
  • lombokのバージョンはSpring Bootが提供しており、バージョン指定は不要
  • 主なアノテーション:@Data
@Data
public class Product {
    private Integer id;
    private String category;
    private String title;
    private String detail;
}
  • 現在のエンティティクラスにコンパイル時に対応するget/setメソッド、toStringメソッド、hashCodeメソッド、equalsメソッドなどを設定

データ層の標準開発

  • 技術実装案

    • MyBatisPlus
    • Druid
  • MyBatisとDruid対応のstarterをインポート

<dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.2</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.8</version>
    </dependency>
  • データソースとMyBatisPlus対応の基本設定(ID生成戦略はデータベースの自動増加戦略を使用)
spring:
  datasource:
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/product_db?serverTimezone=UTC
      username: root
      password: root
mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_
      id-type: auto
  • BaseMapperを継承し、ジェネリクスを指定
@Mapper
public interface ProductDao extends BaseMapper<Product> {
}

MPログの開始

  • デバッグを容易にするために、MyBatisPlusのログを有効にできます
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

データ層-ページング機能

  • ページング操作では、ページングオブジェクトIPageを設定する必要があります
@Test
void testGetPage(){
    IPage page = new Page(1,5);
    productDao.selectPage(page,null);
}
  • IPageオブジェクトには、ページング操作のすべてのデータがカプセル化されています

    • データ
    • 現在のページ番号値
    • 各ページのデータ総量
    • 最大ページ番号値
    • データ総量
  • ページング操作は、MyBatisPlusの通常操作に基づいて強化されたものです。内部では動的にSQL文が作成されるため、対応する機能を強化する必要があります。MyBatisPlusインターセプタを使用して実現します

@Configuration
public class MPConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

データ層-条件付きクエリ

  • QueryWrapperオブジェクトを使用してクエリ条件をカプセル化し、LambdaQueryWrapperオブジェクトの使用を推奨します。すべてのクエリ操作はメソッド呼び出しにカプセル化されます
@Test
void testGetByCondition(){
    IPage page = new Page(1,10);
    LambdaQueryWrapper<Product> lqw = new LambdaQueryWrapper<Product>();
    lqw.like(Product::getTitle,"Spring");
    productDao.selectPage(page,lqw);
}
@Test
void testGetByCondition(){
    QueryWrapper<Product> qw = new QueryWrapper<Product>();
    qw.like("title","Spring");
    productDao.selectList(qw);
}
  • 動的クエリ条件のサポート
@Test
void testGetByCondition(){
    String title = "Spring";
    IPage page = new Page(1,10);
    LambdaQueryWrapper<Product> lqw = new LambdaQueryWrapper<Product>();
    lqw.like(Strings.isNotEmpty(title),Product::getTitle,"Spring");
    productDao.selectPage(page,lqw);
}

業務層の開発

  • Service層インターフェースの定義はデータ層インターフェースの定義と大きな違いがあり、乱用しないでください

    • selectByUserNameAndPassword(String username,String password);
    • login(String username,String password);
  • インターフェース定義

public interface ProductService {
    Boolean save(Product product);
    Boolean update(Product product);
    Boolean delete(Integer id);
    Product getById(Integer id);
    List<Product> getAll();
}
  • 実装クラス定義
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductDao productDao;

    @Override
    public Boolean save(Product product) {
        return productDao.insert(product)>0;
    }

    @Override
    public Boolean update(Product product) {
        return productDao.updateById(product)>0;
    }

    @Override
    public Boolean delete(Integer id) {
        return productDao.deleteById(id)>0;
    }

    @Override
    public Product getById(Integer id) {
        return productDao.selectById(id);
    }

    @Override
    public List<Product> getAll() {
        return productDao.selectList(null);
    }
}

業務層–迅速な開発

  • 迅速な開発案

    • MyBatisPlusが提供する業務層汎用インターフェース(IService)と業務層汎用実装クラス(ServiceImpl)(Mはデータ層インターフェース、Tはエンティティクラス)
    • 汎用クラスをベースに機能のオーバーロードまたは追加
    • オーバーロード元の操作を上書きしないように注意してください
  • インターフェース定義

public interface IProductService extends IService<Product>{
    
}
public interface IProductService extends IService<Product>{
    // 追加操作と元の操作を名前で区別
    Boolean remove(Integer id);
    
    Boolean create(Product product);
    
    Boolean modify(Product product);
    
    Product fetch(Integer id);
}
  • 実装クラス定義
@Service
public class ProductServiceImpl2 extends ServiceImpl<ProductDao,Product> implements IProductService{
    
}

表現層

  • Restfulに基づいた表現層インターフェースの作成
    • 新規追加:POST
    • 削除:DELETE
    • 変更:PUT
    • クエリ:GET
  • パラメータの受け取り:
    • エンティティデータ:@RequestBody
    • パス変数:@PathVariable

表現層メッセージの一貫性処理

  • 表現層返却結果のモデルクラスを設計し、バックエンドとフロントエンドのデータ形式を統一します。これをフロントエンドとバックエンドのデータプロトコルとも呼びます
@Data
public class ApiResponse {
    private Boolean success;
    private Object result;

    public ApiResponse(){

    }
    public ApiResponse(Boolean success){
        this.success=success;
    }

    public ApiResponse(Boolean success,Object result){
        this.success=success;
        this.result=result;
    }
}
@RestController
@RequestMapping("/api/products")
public class ProductController {
    @Autowired
    private IProductService productService;

    @GetMapping
    public ApiResponse getAll(){
        return new ApiResponse(true,productService.list());
    }

    @PostMapping
    public ApiResponse save(@RequestBody Product product){
        return new ApiResponse(productService.save(product));
    }

    @PutMapping
    public ApiResponse update(@RequestBody Product product){
        return new ApiResponse(productService.saveOrUpdate(product));
    }

    @DeleteMapping("{id}")
    public ApiResponse delete(@PathVariable Integer id){
        return new ApiResponse(productService.removeById(id));
    }

    @GetMapping("{id}")
    public ApiResponse getById(@PathVariable Integer id){
        return new ApiResponse(true,productService.getById(id));
    }

    @GetMapping("{currentPage}/{pageSize}")
    public ApiResponse getPage(int currentPage,int pageSize){
        return new ApiResponse(true,productService.getPage(currentPage,pageSize));
    }

}
  • 統一された返却値結果タイプはフロントエンド開発のデータ読み取りを容易にします
  • 返却値結果タイプはニーズに応じて自由に設定でき、固定形式はありません
  • 返却値結果モデルクラスはバックエンドとフロントエンドのデータ形式を統一するために使用され、フロントエンドとバックエンドのデータプロトコルとも呼ばれます

フロントエンドとバックエンドのプロトコル連携

  • フロントエンドとバックエンドの分離構造設計では、ページはフロントエンドサーバーに属します
  • モノリスプロジェクトでは、ページはresourcesディレクトリ下のstaticディレクトリに配置されます(cleanの実行を推奨)

例外処理

  • 例外を一貫して処理し、例外発生時に指定された情報を返します
// springmvcの例外ハンドラーとして
@RestControllerAdvice
public class GlobalExceptionHandler {
    // すべての例外情報をインターセプト
    @ExceptionHandler(Exception.class)
    public ApiResponse handleException(Exception ex){
        // ログを記録
        // 運営にメッセージを送信
        // 開発者にメールを送信、exオブジェクトを開発者に送信
        ex.printStackTrace();
        return new ApiResponse("サーバーエラー、しばらくお待ちください");
    }
}
@Data
public class ApiResponse {
    private Boolean success;
    private Object result;
    private String message;

    public ApiResponse(){

    }
    public ApiResponse(Boolean success){
        this.success=success;
    }

    public ApiResponse(Boolean success,Object result){
        this.success=success;
        this.result=result;
    }

    public ApiResponse(String message){
        this.success=false;
        this.message=message;
    }
}

タグ: Spring Boot Java Maven YAML MyBatis

6月19日 16:07 投稿