よく使うSpring Boot の アノテーションまとめ

Java/Kotlinのフレームワークとして有名な、「Spring Boot」を仕事で使っています。
Spring Bootを使っていると、アノテーションと呼ばれる＠マークで始まるタグのような目印が出てきて、最初は面食らいました。
よく使うアノテーションは比較的限られているので、それをまとめてみました。

# Component アノテーション

## `@Component`

[Component (Spring Framework 5.3.18 API) - Javadoc](https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Component.html)

> アノテーション付きクラスが「コンポーネント」であることを示します。このようなクラスは、アノテーションベースの構成およびクラスパススキャンを使用する場合の自動検出の候補と見なされます。
他のクラスレベルのアノテーションも、コンポーネント、通常は特別な種類のコンポーネントを識別するものと見なされます
> 

`@Autowired` （これについては後述）されたときに、自動的にDIされる対象となる、コンポーネントクラスであることを示すアノテーションです。

Classに対して付与することができる。

以下のアノテーション群も `@Component` として機能するようになっていて、 `@Component` の特殊表現というイメージです。

- [@Repository](https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Repository.html)
- [@Service](https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Service.html)
- [@Controller](https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Controller.html)
- `@ControllAdvice`
- `@ManagedBean`
- `@Named`

## `@Component` / `@Service` / `@Repository` の違い

**結論**： **違いはない。** ただの目印。（ただし、 `@Repository` は若干の差異あり。）

[こちらの記事](https://www.baeldung.com/spring-component-repository-service)によると、
> *@Service* and *@Repository* are special cases of *@Component*. They are technically the same, but we use them for the different purposes.

ということで、技術的には同じとのこと。


### `@Repository` の違い

> **`@Repository`’s job is to catch persistence-specific exceptions and re-throw them as one of Spring’s unified unchecked exceptions**.
For this, Spring provides *PersistenceExceptionTranslationPostProcessor*, which we are required to add in our application context (already included if we're using Spring Boot)

と、 `@Component`, `@Service` と若干の違いがあるようですが、ほとんど意識しなくてもよさそうです。

[](https://www.baeldung.com/spring-component-repository-service)

# `@Autowired`

このアノテーションをつけた変数の型を満たすComponentを探して、自動的にインスタンス作成とDIをしてくれる。

インジェクトする方法は３種類

- フィールド・インジェクション（メンバー変数の宣言に `@Autowired` 付ける。省略不可）
- コンストラクタ・インジェクション（`@Autowired` 省略可能）
- セッター・インジェクション（`@Autowired` 省略不可）

## フィールド・インジェクション

```kotlin
@Component
class DemoComponent {
    fun run() {
	    println("RUN")
    }
}

class DemoApp {
    @Autowired
	lateinit var component: DemoComponent
		
	fun exec() {
		component.run()
	}
}
```

## コンストラクタ・インジェクション

コンストラクタを利用することで、 `@Autowired` を省略することができる。

```kotlin
class DemoApp(var component: DemoComponent) {		
	fun exec() {
		component.run()
	}
}
```

上記のケースでは、 `@Autowired` が書かれていないが、DIが自動的に行われている。（コンストラクタ・インジェクション）

ただし、コンストラクタ・インジェクションで `@Autowired` が省略できるようになったのは Spring v5.0.0以降みたいなので注意。

[Autowired (Spring Framework 5.3.18 API) - Javadoc](https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html)

## セッター・インジェクション

あんまり存在意義が分からないが、こんな書き方もある。

```kotlin
class DemoApp {		
	@Autowired
	fun setComponent(component: DemoComponent) {
		this.component = component
	}

	fun exec() {
		component.run()
	}
}
```

## 注意

`@Autowired` されたメンバー変数を持ったClassに対して、手動で `new` してインスタンスを作る場合、 `@Autowired` されたメンバー変数は自動的にDIされるわけではないので、手動でDIする必要がある。

以下、例。

```kotlin
class DemoApp {
	@Autowired
	lateinit var component: DemoComponent
	
	fun exec() {
		component.run()
	}
}

val demoApp = DemoApp()
demoApp.exec() // -> RUN TIME ERROR: kotlin.UninitializedPropertyAccessException: lateinit property component has not been initialized
```

これを防ぐためにも、メンバー変数に対して `@Autowired` するよりも、コンストラクタ・インジェクションした方が安全。

```kotlin
class DemoApp(var component: DemoComponent) {
	fun exec() {
		component.run()
	}
}

val demoApp = DemoApp() // -> BUILD ERROR: one argument is required!!
demoApp.exec()
```

## `@Autowired` を満たすクラスが複数あるとエラーになる

以下のケースでは実行時エラーとなる。

```kotlin
interface DemoComponent {
	fun run()
}

@Component
class DemoComponentA: DemoComponent {
    override fun run() {
		println("RUN - A")
    }
}

@Component
class DemoComponentB: DemoComponent {
    override fun run() {
		println("RUN - B")
    }
}

class DemoApp {
	@Autowired
	lateinit var component: DemoComponent
		
	fun exec() {
		component.run()
	}
}
```

上記のケースでは、 `@Autowired` された `lateinit var component: DemoComponent` を満たすクラスが、 `DemoComponentA` と`DemoComponentB` の二つあるため、以下のエラーになる。

```kotlin
(略) required a single bean, but 2 were found:
```

Autowiredによって、自動的に `DemoComponent` のクラスをDIしてくれるのだが、今回はこれはクラスではなく、Interfaceのため、このInterfaceを実装するクラスが複数あると上記のエラーでクラッシュする。

 対応策として、優先的にDIに使うクラスに `@Primary` を付ける。

```kotlin
interface DemoComponent {
	fun run()
}

@Component
@Primary
class DemoComponentA {
    override fun run() {
		println("RUN - A")
    }
}

@Component
class DemoComponentB {
    override fun run() {
		println("RUN - B")
    }
}

class DemoApp {
	@Autowired
	lateinit var component: DemoComponent
	
	fun exec() {
		component.run() // -> RUN - A
	}
}
```

# `@Bean`

Bean設定したMethod内で、DI時に注入するインスタンスを生成するようにすることで、 `@Component` 指定したClassに自動的にDIすることができる。

```kotlin
@Component
class DemoComponent {
	@Autowired
    lateinit var demoBean: DemoBean

    fun run() {
        demoBean.run()
    }
}

class DemoBean(var message: String) {
    fun run() {
        println(message)
    }
}

@Configuration
class DemoConfig {
	@Bean
	fun createBean() {
		return DemoBean("HELLO!!")
	}
}
```

[こちら](https://dev.classmethod.jp/articles/springboot-what-is-bean/)参考にした。

`@Configuration` を使って、わざわざ回りくどくBeanをDIしている理由は、 `@Configuration` Class内で何らかの処理をしてからDIしたいとき（文字通りConfiguration設定をしたいときとか？）に使うイメージだろうか。


# Rest API関係のアノテーション
Spring BootをRest APIサーバーとしてホストするときに利用するであろうアノテーションです。
[こちらの記事](https://spring.pleiades.io/spring-framework/docs/current/reference/html/web.html#mvc-controller)が参考になります。

実際にサンプルコードを見た方がわかりやすいと思うので、コードを出します。
ざっくり以下のような感じで簡単にRest APIを実装することができます。
```kotlin
@RestController
@RequestMapping("/api/demo")
class DemoController {
    @Autowired
    lateinit var component: DemoComponentInterface

    // GET - /api/demo?param1=xxx
    @GetMapping
    fun get(@RequestParam("param1") param1: String?): String {
        component.run()

        return """
            ECHO:
            param1: $param1
        """
    }

    // GET - /api/demo/{id}
    @GetMapping("/{id}")
    fun getWithId(@PathVariable id: String): String {
        return """
            ECHO:
            id: $id
        """
    }

    // GET - /api/demo/{id1}/{id2}
    @GetMapping("/{id1}/{id2}")
    fun getWithId(@PathVariable id1: String, @PathVariable id2: String): String {
        return """
            ECHO:
            id1: $id1
            id2: $id2
        """
    }

    // POST - /api/demo/raw-string
    @PostMapping("/raw-string")
    fun postWithRawString(@RequestBody requestBody: String): String {
        return """
            ECHO:
            request body: $requestBody
        """
    }
}
```
以下、具体的なアノテーションの解説です。

## @RestController
`@RestController` 設定することで、Rest APIのリクエストを受け取るControllerをアサインできる。
APIエンドポイントの設定はこのアノテーションで行う。


## @RequestMapping

リクエストを、コントローラーClassやMethodに対して紐づけることができます。

リクエストのエンドポイントを

`@RequestMapping` でも、GET/POST/PUT/DELETE/PATCHといったHTTPリクエストの対応ができますが、以下のようなアノテーションが用意されているのでこちらを使えば簡単にHTTP Methodが実装できます。

- `@GetMapping`
- `@PostMapping`
- `@PutMapping`
- `@DeleteMapping`
- `@PatchMapping`

以上。