BeanとDIについて

***
- 依存性とDI
	- 依存性とは
	- DI(依存性の注入)とは
	- 「依存している状態」とは
	- 依存状態の問題点
	- 依存性の解決
- Bean構成ファイル(XML)による解決
	- ファイルの作成
	- ファイルの編集
	- タグの編集内容
	- クラスの編集
	- ApplicationContextとは
- インターフェース化する
	- インターフェースの作成
	- 作成/変更されたクラス
	- 別のBeanクラスの設定
- Bean構成ファイル(XML)を用いない解決
	- Bean構成クラスの作成
	- クラスの編集
	- AnnotationConfigApplicationContextとは
	- アノテーションについて	

***

##依存性とDI

###依存性とは
* それぞれのコンポーネントの固有の設定などを意味する

> ある程度の規模の開発を行う場合、汎用的な部品をコンポーネント化して利用することになります。このとき、特定のコンポーネントを作成し、設定する処理がコード内に直接書かれていると、後々面倒なことになります。

* 特定のデータや設定が本来汎用的であるコンポーネントと密接な関係を持つことで、コンポーネント自体の柔軟性が失われてしまうことが問題

###DI(依存性の注入)とは
* DI = Dependency Injection  

> Dependency Injection (依存性注入) とは、あるクラスが別のクラスをインスタンス変数に持つなどして利用 (依存) している場合に、インスタンス変数の設定 (依存性の解決) をクラス内で行うのではなく、外部から設定 (注入) するという考え方。
> 
>設定を利用から分離する（the principle of separating configuration from use）」という考え方をコンポーネント設計のための1つの原則としている。

* 上記で説明した「あるクラスの処理が、他の特定クラスに依存している状態を」解消するのがDIの目的

![DIコンテナ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/a4152f05-25c9-71e2-d4c6-04a31b673dd5.jpeg)
![DIコンテナ2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/8a7c720c-995c-d25b-091f-e495eb8527fe.jpeg)


###「依存している状態」とは

* SampleBean.java

```java
public class SampleBean {

  private Date date;
  private String message;
  
  public SampleBean() {
    this.date = new Date();
  }
  
  public SampleBean(String message) {
    this();
    this.message = message;
  }
  
  public Date getDate() {
    return date;
  }
  
  public String getMessage() {
    return message;
  }
  
  public void setMessage(String message) {
    this.message = message;
  }
  
  @Override
  public String toString() {
    return "Date = " + date + " & Message = " + message;
  }
} 
```

* App.java

```java
public class App {

  public static void main(String[] args) {
    SampleBean bean = new SampleBean("Hello World!");
    System.out.println(bean);
  }
}
```

* 実行結果

```
Date = Wed Sep 25 15:00:00 JST 2019 & Message = Hello World!
```

###依存状態の問題点

* messageに設定する値を変更する場合はソースコードを直接書き換え、再コンパイルする必要がある  

* UTについても、クラス内で設定された値をテストクラスに記述する必要がある

→ 「そのBeanのためだけにしか役に立たないコード」を記述することで柔軟性が失われている

###依存性の解決
* 以下2つの処理をApp内から切り離す

1. SampleBeanのインスタンスを生成する処理
2. messageに値を設定する処理

***

##Bean構成ファイル(XML)による解決
* XMLの情報を元にインスタンスを自動生成し、プログラム内で利用できるようにする

###ファイルの作成

1. STSで ＜File＞ - ＜New＞ - ＜Spring Bean ConfigurationFile＞を選択
![スクリーンショット 2019-09-27 0.45.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/fc96f2be-7130-86ba-7644-8352d8ab23fb.png)

2. ファイルの保存場所とファイル名を指定
![スクリーンショット 2019-09-27 0.49.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/6a6d0640-463f-0b39-7b0c-5903286ae780.png)

3. XSD(XMLスキーマ定義)の名前空間を設定 = Bean構成ファイルで記述する内容のためのXSDを設定
![スクリーンショット 2019-09-27 0.59.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/b117e0e6-164b-2beb-f26d-b51e1d33e459.png)

###ファイルの編集

1.初期状態

```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd">


</beans>
```

2.SampleBeanクラスのインスタンス設定をbean.xmlに追加

```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="sample" class="jp.spring.sample.SampleBean">
    <property name="message" value="this is sample bean!">
    </property>
  </bean>
</beans>
```

もしくは

```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="sample" class="jp.spring.sample.SampleBean">
    <constructor-arg type="String" name="message" value="this is sample bean!">
    </constructor-arg>
  </bean>
</beans>
```

###タグの編集内容
* beanタグ

```xml
<bean id="インスタンス名" class="クラスのパス">
```

* propertyタグ

```xml
<property name="プロパティ名" value="値">
```

* constructor-argタグ

```xml
<constructor-arf type="タイプ名" name="引数名" value="値">
```

###クラスの編集
* App.java

```java
public class App {

  private static ApplicationContext app;

  public static void main(String[] args) {
    app = new ClassPathXmlApplicationContext("bean.xml");
    SampleBean bean = (SampleBean)app.getBean("sample");
    System.out.println(bean);
  }
}
```

* 実行結果

```
Date = Wed Sep 25 15:00:00 JST 2019 & Message = this is sample bean!
```

###ApplicationContextとは

```java
private static ApplicationContext app;
```

* 従来のBeanFactoryを拡張したもの  
* メモリの使用量に厳しい制限がある場合を除き BeanFactory よりも ApplicationContext を使用することが推奨されている

> このApplicationContextというのは、アプリケーション全体で使われる各種の**コンテキスト(※1)**を管理するクラスです。  

> ApplicationContextクラスは**IoCコンテナ(※2)**だといえます。このApplicationContextで各種のBeanが管理され、取得されたBeanによってアプリケーションの様々な情報が得られるようになっているのです。アプリケーションで利用するBeanは、すべてこのApplicationContextを使って取得します。

(※1) アプリケーションで使われる設定や各種の情報のこと。SpringにおいてはBeanのことを指す。  
(※2)Inversion of Control(制御の反転)のこと。
> 通常のプログラムでは、私たちがコンポーネントを作成し、その中から必要なライブラリの機能などを呼び出していきます。が、DIの世界では、コンポーネントを呼び出すのはIoCコンテナの側です。(制御の反転)
> 
> この制御の反転を行うためのコンテナが「IoCコンテナ」です。

```java
app = new ClassPathXmlApplicationContext("bean.xml");
```

* ClassPathXmlApplicationContextはApplicationContextのサブクラス
* bean.xmlを利用するApplicationContextインスタンスを生成

```java
SampleBean bean = (SampleBean)app.getBean("sample");
```
* Bean名(=beanタグ内のid)を指定する

```java
SampleBean bean = app.getBean(SampleBean.class);
```
* クラス名(=beanタグのclass)を指定する
* キャストの必要がない

***

##インターフェース化
* Bean構成ファイルによって、messageの値が変更された場合もbean.xmlを修正するだけで済むようになった。(再コンパイルする必要も無くなった)
* 取得したBeanのインスタンスをSampleBean以外のクラスに差し替えることができない

###インターフェースの作成

1. ＜SampleBean.java＞ - ＜Refactor＞ - ＜Extract Interface...＞を選択  
![スクリーンショット 2019-09-27 4.40.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/69fb7071-5aad-9f8d-4199-428bc1947489.png)


2. インターフェース名と移行するメソッドを設定
![スクリーンショット 2019-09-27 4.42.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/ffbb1c65-9ba5-c029-571f-431363d44a51.png)


###作成/変更されたクラス

* SampleBeanInterface.java

```java
public interface SampleBeanInterface {

  public abstract Date getDate();
  
  public abstract String getMessage();
  
  public abstract void setMessage(String message);
  
}
```

* SampleBean.java

```java
public class SampleBean implements SampleBeanInterface {

(省略)

}
```

* App.java

```java
public class App {

  private static ApplicationContext app;

  public static void main(String[] args) {
    app = new ClassPathXmlApplicationContext("bean.xml");
    SampleBeanInterface bean = (SampleBeanInterface)app.getBean("sample");
    System.out.println(bean);
  }
}
```

###別のBeanクラスの設定

* SampleBean2.java

```java
public class SampleBean2 implements SampleBeanInterface {

  private Date date;
  private String message;
  
  public SampleBean2() {
    this.date = new Date();
  }
  
  public SampleBean2(String message) {
    this();
    this.message = message;
  }
  
  public Date getDate() {
    return date;
  }
  
  public String getMessage() {
    return message;
  }
  
  public void setMessage(String message) {
    this.message = message;
  }
  
  @Override
  public String toString() {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    return "message + "(" + format.format(date) + ")";
  }
} 
```

* bean.xml

```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="sample" class="jp.spring.sample.SampleBean2">
    <property name="message" value="this is new bean!">
    </property>
  </bean>
</beans>
```

* 実行結果

```
this is new bean!(2019-09-25)
```

***

##Bean構成ファイル(XML)を用いない解決
* XMLによる設定は面倒(という人もいる)
* 設定ファイルを使わないフレームワークが増えている

###Bean構成クラスの作成

1. ＜File＞ - ＜New＞ - ＜Class＞を選択
![スクリーンショット 2019-09-27 6.03.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/89a889de-a437-193b-a63a-3c45215ff9d2.png)

2. クラス名を設定
![スクリーンショット 2019-09-27 6.04.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504173/80e97cc8-0ca9-7392-720d-bf2b201a5092.png)

###クラスの編集
* SampleBeanConfig.java

```java
@Configuration
public class SampleBeanConfig {

  @Bean
  public SampleBeanInterface sampleBean(){
    return new SampleBean("This is Bean Config!");
  }
}
```

* App.java

```java
public class App {

  private static ApplicationContext app;

  public static void main(String[] args) {
    app = new AnnotationConfigApplicationContext("SampleBeanConfig.class");
    SampleBeanInterface bean = app.getBean(SampleBeanInterface.class);
    System.out.println(bean);
  }
}
```

###AnnotationConfigApplicationContextとは

* アノテーションによる設定情報を利用するためのApplicationContextのサブクラス

###アノテーションについて

@Configuration

* Beanの構成を定義したファイルであることを示す

@Bean

* Bean構成クラスにおいて、Beanインスタンスを定義するためのメソッドであることを示す
* Bean構成ファイル(=bean.xml)におけるbeanタグと同じ