2017年12月20日 星期三

Java Spring - Automatically wiring beans

  • 創造application中DI本質的行為稱為wiring
  • Spring configuration options
    • Spring container負責創建application中的bean。並且通過DI協調之間的關西
    • 需要告訴Spring哪些bean如何將其wiring在一起
    • wiring mechanisms(選擇方式可以互相搭配)
      1. 顯式在XML中配置 (最末)
      2. 顯式在JAVA中配置 (次之)
      3. 隱式的bean自動wiring及discovery (盡可能使用)
  • Automatically wiring beans 自動裝配bean
    • 實現automatically wiring beans有兩種方法,且能夠搭配使用
      1. component scanning: Spring自動發現application context建立的bean
      2. autowiring: Spring自動滿足bean間的依賴
    • 範例:
      • 若要實現CD player依賴CD,可使用以下範例
      • package soundsystem;
        public interface CompactDisc { // 一個interface將CD player的implements和CD的coupling降至最低
          void play();
        }
        
      • 建立一個具體的object
      • package soundsystem;
        import org.springframework.stereotype.Component;
        @Component // 表明是component,Spring會為這個class建立bean
        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.println("Playing " + title + " by " + artist);
        } }
        
      • 但是Component scanning預設並沒有啟用,所以需要寫明configuration,如此的話可以命令去找帶有@Component的class,即能建立bean
      • package soundsystem;
        import org.springframework.context.annotation.ComponentScan;
        import org.springframework.context.annotation.Configuration;
        @Configuration 
        @ComponentScan// 默認會找和這個config相同的package(這邊是soundsystem及package底下的class,找有@Component註解的class
        public class CDPlayerConfig {
        }
        
      • XML也能夠啟用ComponentScan
      • <?xml version="1.0" encoding="UTF-8"?>
                <beans xmlns="http://www.springframework.org/schema/beans
        ...
        <context:component-scan base-package="soundsystem" /> </beans>
      • 接著即可unit test進行測試
      • package soundsystem;
        import static org.junit.Assert.*;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.test.context.ContextConfiguration;
        import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
        @RunWith(SpringJUnit4ClassRunner.class) // 測試開始自動創建Spring application context
        @ContextConfiguration(classes=CDPlayerConfig.class) //使用的
        configuration class
        public class CDPlayerTest {
          @Autowired // 將compact disc bean inject至測試code,這樣就不用寫new
          private CompactDisc cd;
          @Test
          public void cdShouldNotBeNull() {
            assertNotNull(cd); // Spring context中創建bean並inject至此
          }
        }
        
        • 所有帶有@Component annotation的class都會創建bean
    • 為component-scanned bean命名
      • Spring application context會給所有的bean一個ID,若沒有明確給定ID,Spring會根據class name指定一個ID。而這個ID就是class name的小寫
      • 給定方法:
        1. 在@Component括號內加上值如: @Component("lonelyHeartsClub")
        2. 使用@Named annotation: @Named("lonelyHeartsClub")
    • 設定component scanning的base package 
      • Component scanning 默認會找和這個config class相同的package當作base package來做component scanning
      • 問:
        • 若config class的package和想要做component scanning 的package不同時,就不能使用default base package
      • 解:
        • 在ComponentScan括號內加上值如:
        • @Configuration
          @ComponentScan("soundsystem")
          public class CDPlayerConfig {}
          
        • 也可以設定這個value是屬於base package,甚至可以設定多筆
        • @Configuration
          @ComponentScan(basePackages={"soundsystem", "video"})
          public class CDPlayerConfig {}
          
        • 也可以設定class符合type-safe,這些class所在的base package將成為component scan的base packages
        • @Configuration
          @ComponentScan(basePackageClasses={CSPlayer.class, DVDPlayer.class)
          public class CDPlayerConfig {}
    • Automatically wired
      • 讓Spring自動尋找application context內來滿足bean依賴其他bean的方法
      • 進行auto wired,使用@Autowired annotation
      • 如下:
      • package soundsystem;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Component;
        @Component
        public class CDPlayer implements MediaPlayer {
          private CompactDisc cd;
          @Autowired // 自動進行initialize CompactDisc並inject一個CompactDisc bean
          public CDPlayer(CompactDisc cd) {
            this.cd = cd;
          }
          public void play() {
            cd.play();
          }
          @Autowired // 也能用在setter method上,或任何method上
          public void setCompactDisc(CompactDisc cd)() {
            this.cd = cd;
          }
        }
        
      • Spring會嘗試滿足method上parameter所declare的dependency
        • 如果只有一個bean符合,那麼將是這個bean傳入
        • 如果沒有符合的,Spring會throw exception
          • 解決: @Autowired(required=false)
          • 但這時候這個object就會是null
        • 如果有多個bean符合,Spring也會throw exception
      • 可以使用@Inject代替@Autowired

沒有留言:

張貼留言