概要
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プログラム起動時、以下の位置から設定ファイルが読み込まれます:
- file:./config/: 現在プロジェクトの/configディレクトリ下
- file:./ : 現在プロジェクトのルートディレクトリ
- classpath:/config/: classpathの/configディレクトリ
- classpath:/ : classpathのルートディレクトリ(一般的にはresources)
読み込み順序は上記の順序で、高優先度の設定プロパティが有効になります
整合
Junitとの統合
- Spring Bootプロジェクトの構築
- starter-testスターターデペンデンシの導入
- テストクラスの作成
- テスト関連アノテーションの追加
- @RunWith(SpringRunner.class)
- @SpringBootTest(classes = 起動クラス.class)
- テストメソッドの作成
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/アノテーション)
手順:
-
新しいSpring Bootプロジェクトを作成
-
現在のモジュールで使用する技術セットを選択(MyBatis、MySQL)
-
データソースパラメータの設定
-
データ層インターフェースとマッピング設定の定義
-
テスト
MyBatis統合時のよくある問題
タイムゾーン設定問題
- MySQLにタイムゾーンを設定(urlの後にserverTimezone=UTCを追加)
MyBatis-Plusとの統合
- MyBatis-PlusとMyBatisの違い
- コーディネートのインポートが異なる
- データ層の実装が簡略化
クイックスタート
Alibaba Cloudを使用してspring-bootを作成
本番環境(公式)
-
Spring BootとMyBatis-Plus統合のコーディネートを手動で追加(mvnrepositoryから取得可能)
-
データ層インターフェースとマッピングを定義し、BaseMapperを継承
Druidとの統合
-
コーディネートのインポート
-
設定情報の追加
spring:
datasource:
druid:
url:
username:
password:
第三方技術との統合
- 対応するstarterをインポート
- 対応する設定を設定するか、デフォルト設定を使用する
統合ケーススタディ
モジュールの構築
-
SpringMVCとMySQLコーディネートにチェック
-
設定ファイルをyml形式に変更
-
ポート番号の設定
エンティティクラスの開発
- 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;
}
}