Spring筆記之裝配bean

原文鏈接地址:http://tantanit.com/springinacton-di-si-ban-di-er-zhang-zhuang-pei-bean-du-shu-bi-ji/

Spring in Action(Spring實戰)的第二章講述了如何裝配bean。本文是對這章的讀書筆記,講解Spring裝配bean的三種主要方式。

# Spring中裝配的主要方式

* XML顯式配置

* Java顯式配置

* 使用Java隱式掃描bean並自動裝配

三種方式各有好處,作者認為選哪種只是口味問題,但強烈建議使用第三種(使用Java隱式掃描bean並自動裝配),並且建議即使要用顯式配置,也盡量使用Java配置,因為Java更好用(powerful),具有類型安全檢查,並且更好重構。只有在XML有命名空間可以很方便使用,而JavaConfig中沒有的時候才使用XML顯式配置。

Advertisements

# 使用Java隱式掃描bean並自動裝配

Spring的這種方案實在是太好用了,應該盡量利用這種機制。這個方案中,我們要做的事情包括兩個方面:讓bean可以被掃描以及在使用bean的地方聲明注入bean。這樣,Spring就可以掃描並生成bean,並且在要使用的地方注入bean了。

## 讓bean可以被掃描到

1. 在類上加@Component標籤,標記這個類可以作為組件供其它類使用。

2. 寫一個Java配置類,加上@Configuration和@ComponentScan標籤,(也可以用xml實現,略)

在啟動項目時,Spring就會在這個配置類所在的目錄下查找有加@Component標籤的類,並且自動生成bean了。

Advertisements

在類上加@Component標籤:

```java

package soundsystem;

import org.springframework.stereotype.Component;

@Component

public class SgtPeppers implements CompactDisc {

private String title = "Sgt. Pepper's Lonely Hearts Club Band";

private String artist = "The Beatles";

public void play() {

System.out.print("Playing " + title + " by " + artist);

}

}

```

配置類加加上@Configuration和@ComponentScan標籤:

```java

package soundsystem;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

@Configuration

@ComponentScan

public class CDPlayerConfig {

}

```

## 標註被掃描bean的位置

有時候,配置類和要掃描的類不在同一個目錄下,此時要標明要掃描的類所在的包或者直接標明要掃描的類。

```java

@Configuration

@ComponentScan(basePackages={"soundsystem", "video"})

public class CDPlayerConfig {}

```

```java

@Configuration

@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})

public class CDPlayerConfig {}

```

## 為bean定義id

Spring會默認將類的第一個字母改為小寫,作為bean的id,如果一個類有多個bean,或者只是想改用其它id,可以通過以下兩種方式,定義bean的id:

```java

@Component("lonelyHeartsClub")

public class SgtPeppers implements CompactDisc {

...

}

```

```java

package soundsystem;

import javax.inject.Named;

@Named("lonelyHeartsClub")

public class SgtPeppers implements CompactDisc {

...

}

```

## 在使用bean的地方聲明注入bean

```java

package soundsystem;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

@Component

public class CDPlayer implements MediaPlayer {

private CompactDisc cd;

@Autowired

public CDPlayer(CompactDisc cd) {

this.cd = cd;

}

public void play() {

cd.play();

}

}

```

要使用bean的地方(類成員變數,構造函數或普通方法都可以),加 @Autowired或 @Inject標籤,注入bean,就可以開始使用了。從Autowired或和Inject的名稱可以看出,其實wire(裝配)和inject(注入)的意思差不多,都是表示將bean連接起來。其實wire的意思就是連接。

因為只有SgtPeppers實現了CompactDisc,所以上面的例子會自動注入一個SgtPeppers實例,有時,有多個bean都實現了CompactDisc介面,那麼在注入時,需要聲明注入的bean的id。代碼如下:

```java

@Component

public class CDPlayer implements MediaPlayer {

private CompactDisc cd;

@Autowired

@Qualifier("lonelyHeartsClub")

public CDPlayer(CompactDisc cd) {

this.cd = cd;

}

public void play() {

cd.play();

}

}

```

## 驗證自動化注入

```java

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = CDPlayerConfig.class)

public class CDPlayerTest {

@Rule

public final StandardOutputStreamLog log = new StandardOutputStreamLog();

@Autowired

private MediaPlayer player;

@Autowired

private CompactDisc cd;

@Test

public void cdShouldNotBeNull() {

assertNotNull(cd);

}

@Test

public void play() {

player.play();

assertEquals(

"Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles",

log.getLog());

}

}

```

為了避免不同操作系統換行符的不同,我將原代碼中的換行符去掉了,相應的,將SgtPeppers的play方法的println也改成print了。

# 在Java配置文件中顯式註冊bean

## 定義配置文件

```Java

@Configuration

public class CDPlayerConfig {

@Bean

public CompactDisc sgtPeppers() {

return new SgtPeppers();

}

}

```

CDPlayerConfig加上Configuration標籤,標註為配置文件,但是去掉了ComponentScan標籤,所以要顯式定義bean。

## 使用Bean標籤

Bean標籤表示該方法返回的bean會被註冊到Spring容器里,並且id和方法名稱一樣,也可以使用name參數定義id。

```java

@Bean(name="lonelyHeartsClubBand")

public CompactDisc sgtPeppers() {

return new SgtPeppers();

}

```

下面的例子通過構造函數將CompactDisc注入CDPlayer:

```java

@Configuration

public class CDPlayerConfig {

@Bean

public CompactDisc sgtPeppers() {

return new SgtPeppers();

}

@Bean

public CDPlayer cdPlayer() {

return new CDPlayer(sgtPeppers());

}

@Bean

public CDPlayer anotherCDPlayer() {

return new CDPlayer(sgtPeppers());

}

}

```

Config文件中,兩個方法內調用相同的Bean方法,是否會生成兩個sgtPeppers實例?如果沒有使用Spring的話,是會有兩個實例,使用Spring機制之後,CDPlayer並不是在調用sgtPeppers(),而是從Spring容器中獲取bean,所以只有一個bean。這個調用過程有些讓人困惑,所以,更好的方式是直接引用bean:

```java

@Configuration

public class CDPlayerConfig {

@Bean

public CompactDisc compactDisc() {

return new SgtPeppers();

}

@Bean

public CDPlayer cdPlayer(CompactDisc compactDisc) {

return new CDPlayer(compactDisc);

}

@Bean

public CDPlayer anotherCdPlayer(CompactDisc compactDisc) {

return new CDPlayer(compactDisc);

}

}

```

在這裡,cdPlayer和anotherCdPlayer這兩個方法,因為在Java配置文件中,並且加了@Bean標籤,都會嘗試自動注入CompactDisc類型的bean。Spring會通過掃描目錄,查找Java配置文件中帶Bean標籤的方法,甚至查找XML配置文件來找到需要的bean。在這裡例子中,是通過在帶有Configuration的Java配置文件CDPlayerConfig(這裡正好是同一個文件)中查找帶有Bean標籤的compactDisc()方法,找到對應的bean的,這個bean是一個SgtPeppers實例。

## 驗證使用Java配置文件顯示註冊bean

```java

public class SgtPeppers implements CompactDisc {

private String title = "Sgt. Pepper's Lonely Hearts Club Band";

private String artist = "The Beatles";

private int times = 0;

public void play() {

times++;

System.out.print("Playing " + title + " by " + artist + "," + times);

}

}

```

以下是測試代碼:

```java

package soundsystem;

import static org.junit.Assert.*;

import org.junit.Rule;

import org.junit.Test;

import org.junit.contrib.java.lang.system.StandardOutputStreamLog;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = CDPlayerConfig.class)

public class CDPlayerTest {

@Rule

public final StandardOutputStreamLog log = new StandardOutputStreamLog();

@Autowired

@Qualifier("cdPlayer")

private MediaPlayer player;

@Autowired

@Qualifier("anotherCdPlayer")

private MediaPlayer anotherPlayer;

@Test

public void play() {

player.play();

assertEquals(

"Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles,1",

log.getLog());

log.clear();

anotherPlayer.play();

assertEquals(

"Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles,2",

log.getLog());

}

}

```

筆者註:我在SgtPeppers類中加了個成員變數times,這個變數不是static的,而是對象級別的,所以通過驗證times是否遞增,可以驗證兩個player是否使用的是同一個bean。測試通過,說明使用的確實是同一個bean。另外,為了避免不同操作系統換行符的不同,我將原代碼中的換行符去掉了,相應的,將SgtPeppers的play方法的println也改成print了。

# 在XML配置文件中顯式註冊bean

# 混用Java和XML配置文件顯式註冊bean

Advertisements

你可能會喜歡