《Spring實戰》-第二章:Bean的裝配(3)-自動化裝配

慢慢來比較快,虛心學技術

前言:創建應用對象之間協作關係的行爲通常稱爲裝配( wiring ),這也是依賴注入( DI )的本質

Spring提供三種Bean裝配機制:

  1. 在 XML 中進行顯式配置。
  2. 在 Java 中進行顯式配置
  3. 隱式的 bean 發現機制和自動裝配

一、什麼是自動化裝配?

很顯然,通過前兩篇文章的描述,我們可以發現,Spring的配置方式可以很簡潔,也可以也很複雜,JavaConfig配置方式使用大量的註解替代了XML中的配置,那麼,基於JavaConfig的基礎之上,是否可以再次封裝或簡化配置呢?

Spring實現了純註解配置的自動化隱式裝配,所謂的隱式裝配就是不需要像XML和JavaConfig一樣去爲每一個Bean創建<bean>節點或再配置類中爲每個Bean做註解,而是通過一些特殊的註解實現控制

其中包含兩個概念:

  • 組件掃描( component scanning ): Spring 會自動發現應用上下文中所創建的 bean 。
  • 自動裝配( autowiring ): Spring 自動滿足 bean 之間的依賴。【DI】

二、如何實現自動化裝配

ⅰ.四個基本註解

@Autowired :標記於屬性,方法等,自動裝配的關鍵註解,依賴注入的表現,該註解可以自動尋找並從Spring容器中提取使用該註解的bean並注入到對應的屬性中去

@Component :標記於類,標明當前類是一個可被掃描的組件

@ComponentScan :標記於配置類,開啓組件註解掃描

@Configuration :標記於配置類,標明當前類是一個配置類

ⅱ.基本實現:

①定義基本接口CDPlayer

public interface CDPlayer {

    /**
     * 定義方法播放CD
     *  @param
     *       
     * @return void
     *       
     * @author lai.guanfu 2019/2/27
     * @version 1.0
     **/
    void playCD();

}

②定義基本類CDBean

@Data//Data是lobok的註解,自動添加setter和getter方法等
@Component//將當前類定義爲可掃描的組件
public class CDBean {
    /**
     * 定義CD名
     */
    private String title="The World!";

    /**
    * 定義CD作者
    */
    private String author="Mr.D";
}

③定義實現類CDPlayerImpl,並將CDBean作爲屬性注入

@Component//將當前類定義爲可掃描的組件
public class CDPlayerImpl implements CDPlayer {
    /**
     * 從Spring容器注入CDBean-----依賴注入
     */
    @Autowired
    private CDBean cdBean;

    @Override
    public void playCD() {
        System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
    }
}

④定義配置類CDConfig,並開啓註解掃描

@ComponentScan()//開啓註解掃描
@Configuration//指定當前類爲配置類
public class CDConfig {}

⑤編寫測試

此處使用Spring提供的測試類SpringJUnit4ClassRunner幫助測試,以便在測試開始的時候自動創建 Spring 的應用上下文

而@ContextConfiguration可以指定創建上下文的加載方式以及配置的位置等

@RunWith(SpringJUnit4ClassRunner.class)//輔助創建Spring應用上下文
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {CDConfig.class})//指定創建上下文的加載方式以及配置的位置
public class AppTest 
{
    /**
     * 從Spring容器注入CdPlayer-----依賴注入
     */
    @Autowired
    private CDPlayer cdPlayer;

    /**
     * Rigorous Test :-)
     */
    @Test
    public void play()
    {
        //調用playCD方法測試是否自動裝配及依賴注入成功
        this.cdPlayer.playCD();
    }
}

⑥測試結果,自動裝配成功,依賴注入成功

正在播放:The World! by Mr.D

ⅲ.關注點:

① 提供另一種測試方式:通過上一篇文章中提及的AnnotationConfigApplicationContext上下文實現進行測試

public static void main(String[] args) {
    /**
     * 註解配置實現,同時指定配置類的位置,否則無法讀取配置進行掃描,默認只有Spring自帶的組件
     */
     ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CDConfig.class);
     /**
      *獲取應用上下文中的所有Bean名稱
      */
     String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
     for (String className : beanDefinitionNames){
       System.out.println(className);
     }
     /**
      *從上下文(容器)中獲取對應的Bean
      */
    CDPlayer cdPlayer = applicationContext.getBean(CDPlayer.class);
    /**
     *調用具體方法測試是否成功
     */
    cdPlayer.playCD();
}

測試結果:自動裝配成功,依賴注入成功

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
CDConfig
app//當前測試類的名字
CDBean
CDPlayerImpl
正在播放:The World! by Mr.D

爲裝配的Bean命名

Spring 應用上下文中所有的 bean 都會給定一個 ID,默認是類名的首字母小寫命名,我們可以爲裝配的Bean裝配一個不一樣的ID,在 @Component註解中有一個屬性value,該值可指定bean在Spring容器中的ID,其在@Component註解的源碼定義如下:

public @interface Component {

   /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any (or empty String otherwise)
    */
   String value() default "";

}

我們可以給CDPlayerImpl指定一個別的ID:cdPlayer

@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {

    @Autowired
    private CDBean cdBean;

    @Override
    public void playCD() {
        System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
    }
}

此時執行上一測試代碼結果中,CDPlayerImpl在容器中的ID類名將變成cdPlayer

另:可以使用@Name代替@Component註解爲Bean命名,但是爲了避免概念模糊,建議使用@Component註解

設置組件掃描的基礎包

上述代碼中,配置類CDConfig僅能掃描自身所在包及其子包的組件,能否指定掃描某個或多個包及其子包中的組件呢?

我們看到@ComponentScan註解的源碼中定義的幾個屬性:

public @interface ComponentScan {

   /**
    * Alias for {@link #basePackages}.
    * <p>Allows for more concise annotation declarations if no other attributes
    * are needed &mdash; for example, {@code @ComponentScan("org.my.pkg")}
    * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
    */
   @AliasFor("basePackages")
   String[] value() default {};

   /**
    * Base packages to scan for annotated components.
    * <p>{@link #value} is an alias for (and mutually exclusive with) this
    * attribute.
    * <p>Use {@link #basePackageClasses} for a type-safe alternative to
    * String-based package names.
    */
    @AliasFor("value")
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages} for specifying the packages
     * to scan for annotated components. The package of each class specified will be scanned.
     * <p>Consider creating a special no-op marker class or interface in each package
     * that serves no purpose other than being referenced by this attribute.
     */
    Class<?>[] basePackageClasses() default {};
} 

從源碼得知,我們可以通過配置basePackages和basePackageClasses屬性來指定配置掃描的範圍,其中:

basePackages指定組件掃描的包路徑(使用{}包含的數組)

@ComponentScan(basePackages = {"com.my.spring","com.my.test"})//指定掃描com.my.spring包和com.my.test包及其子包下的組件
@Configuration
public class CDConfig {}

basePackageClasses:指定以該類數組所在包及其子包爲組件掃描範圍

@ComponentScan(basePackageClasses = {App.class})//指定掃描App.class所在包及其子包下的組件
@Configuration
public class CDConfig {}

注:通常,爲了防止因包路徑更改以及業務實體類更改等因耦合而產生的問題,我們通常會使用一個不具備任何業務意義的空接口作爲掃描包的類

自動注入註解@Autowired的使用

可以用在屬性,構造函數,setter以及任何一個普通方法中

@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {
    //1.在屬性注入
    @Autowired
    private CDBean cdBean;

    public CDPlayerImpl(){super()};

    //2.在構造函數中注入
    @Autowired
    public CDPlayerImpl(CDBean cdBean){super()};

    //3.在setter方法中注入
     @Autowired
    public void setCDBean(CDBean cdBean){this.cdBean = cdBean};

    //4.在普通方法中使用
    @Autowired
    @Override
    public void playCD(CDBean cdBean) {
        System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
    }
}

總結

1.自動化裝配使用全註解方式替代XML和JavaConfig顯式裝配方式,方便簡潔

2.自動化裝配依賴於幾個特殊註解:@Autowired,@Component,@ComponentScan和@Configuration

3.@Component註解可以將類定義爲裝配的組件,同時可以爲改組件另起別名(ID)

4.@Configuration將使用該註解的當前類標註爲配置類,@ComponentScan開啓自動掃描,默認掃描當前配置類所在的包及其子包中含有或使用了@Component註解的類,可通過屬性指定掃描範圍

5.@Autowired註解可以用在屬性,構造函數,setter以及任何一個普通方法中,是Spring依賴注入的核心註解

參考文檔

【1】《Spring 實戰(第 4 版)》·Craig Walls

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章