2017年12月20日 星期三

Java Spring - 使用Java configuration或XML設置config


  • Wiring bean with Java
    • 建議使用@Component及@Autowired
    • 問題
      • 將3rd party library的component做autowired到application,這種時候就無法在3rd-party的class上加上@Component及@Autowired
    • 解法
      • 採用顯示configuration。可以使用Java或XML
  • 使用Java設置configuration
    • 不該包含業務邏輯
    • 僅是一個configuration file
    • 通常會放到單獨的package內,藉此與其他的application logic分開,對他的用途不會造成困擾
  • 建立Configuration class
    • 必須在class內新增一annotation @Configuration例:
    • package soundsystem;
      import org.springframework.context.annotation.Configuration;
      @Configuration // key word
      public class CDPlayerConfig {
       }
      
    • 注意此java config class應該要包含bean的create detail
    • 若沒有@ConponentScan annotation時,將不會找到component,create bean會失敗
    • 解法:在JavaConfig中declare bean
    • @Bean // 告知會return object,且要註冊為Spring application context的bean
      public CompactDisc sgtPeppers() { //default bean id和method name一致
        return new SgtPeppers(); // 會創建所需class instance
      }
      
    • 若要重新命名,在跨括號內寫名字 @Bean(name="lonelyHeartsClubBand")
    • 有injection時:
    • @Bean
      public CDPlayer cdPlayer() { // ID: cdPlayer
          return new CDPlayer(sgtPeppers()); // Spring直接return創建的bean,而不是每次都呼叫
      }
      
    • 與原本Java method的差異
      • 使用method: 每次都會有回傳一個新的sgtPeppers instance
      • 使用@Bean:每次呼叫sgtPeppers()instance都會是相同的
    • Spring中的bean都是singletons,因此Spring直接return創建的bean,而不是每次都呼叫。
    • 有injection時也可以寫成
    • @Bean 
      public CDPlayer cdPlayer(CompactDisc compactDisc) { //這種寫法會autowired CimpactDisc,不用明確的引用@Bean CompactDisc
        return new CDPlayer(compactDisc);
      }
      
  • 使用XML設置configuration - 一種舊式的配置方式
  • 建立Configuration
    • 以<beans>為root,如
    • <?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
             http://www.springframework.org/schema/context">
             xmlns:c="http://www.springframework.org/schema/c" <!-- c namespace使用-->
             xmlns:p="http://www.springframework.org/schema/p" <!-- p namespace使用-->
            xmlns:util="http://www.springframework.org/schema/util" <!-- util-namespace使用-->
              </beans>
      
    • 可以藉由STS的File>New>Spring Bean Configuration File create
    • 要declare一個bean,需要使用<bean>,就像是JavaConfig中的@Bean annotation,如:
    • <bean id="compactDisc" class="soundsystem.SgtPeppers" /> <!-- 必須是fully qualified class name-->
    • 自動產生的ID為fully qualified class name#,如上:soundsystem.SgtPeppers#0,但如果有寫上id attribute,則ID就會是id key的value。當然只需要對需要ID引用的bean來確切命名即可
    • 和Java configuration的差異
      1. 不需要負責create SgtPeppers instance,XML檔發現bean element會直接call SgtPeppers的constructor以create bean,也就是JavaConfig內可以使用任何方式來new bean instance
      2. 需要注意xml內的class是以String帶入,不是type-safe,可能會打錯字(可以靠強大的IDE解決,幫忙檢查)
  • 使用constructor injection實體化bean
    • 在XML declare DI有兩種基本方式
      1. <constructor-arg> :較冗長
      2. c-namespace:<constructor-arg> 能做到的c-namespace不一定能做到
    • 範例,當Spring遇到<bean>,會create CDPlayer instance,然後看到<constructor-arg,就會將compactDisc bean reference傳入constructor
    • <bean id="cdPlayer" class="soundsystem.CDPlayer">
          <constructor-arg ref="compactDisc" /> //透過ID使用SgtPeppers
      </bean>
      
    • 使用c-namespace 優點簡潔
    • <bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc" />
      
      • c : prefix
      • cd : constructor argument name
      • ref: injecting a bean,告知Spring是一個bean reference
      • compactDisc: injecting bean ID
    • 如果不想使用constructor argument name,也可以使用parameter的位置表達
    • <bean id="cdPlayer" class="soundsystem.CDPlayer" c:_0-ref="compactDisc" /> <--!表示argument第0的index-->
      
      • 如果只有一個argument,也可以不用加index。如c:_-ref="compactDisc"
    • Injecting constructors withe literal value
      • 範例:假如有一BlankDisc實作了CompactDisc
      • package soundsystem;
        public class BlankDisc implements CompactDisc {
          private String title;
          private String artist;
          public BlankDisc(String title, String artist) { //兩個arguments
            this.title = title;
            this.artist = artist;
          }
          public void play() {
            System.out.println("Playing " + title + " by " + artist);
          } 
        }
        
      • 使用XML:
        1. <bean id="compactDisc" class="soundsystem.BlankDisc">
            <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" /> // argument1
            <constructor-arg value="The Beatles" /> // argument2
          </bean>
          
      • 使用c-namespace,注意底線後為constructor的argument name
        • <bean id="compactDisc"
                class="soundsystem.BlankDisc"
                c:_title="Sgt. Pepper's Lonely Hearts Club Band"
                c:_artist="The Beatles" /> 
          
        • 也可以直接用index表達
        • <bean id="compactDisc"
                class="soundsystem.BlankDisc"
                c:_0="Sgt. Pepper's Lonely Hearts Club Band"
                c:_1="The Beatles" /> 
      • 若constructor內只有一個argument也可以使用c:_
    • wiring collections :c-namespace無法做到的事
      • 例如有一個constructor必須傳入collection List
      • public BlankDisc(String title, String artist, List<String> tracks) {
            this.title = title;
            this.artist = artist;
            this.tracks = tracks;
        }
        
        • 則此時可以使用XML: <list> tag式<constructor-arg>的sub element,表達內的value會傳到constructor
        • <bean id="compactDisc" class="soundsystem.BlankDisc">
            <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
            <constructor-arg value="The Beatles" />
            <constructor-arg>
              <list> 
                <value>Sgt. Pepper's Lonely Hearts Club Band</value>
                <value>With a Little Help from My Friends</value>
                <value>Lucy in the Sky with Diamonds</value>
                <value>Getting Better</value>
                <value>Fixing a Hole</value>
                <!-- ...other tracks omitted for brevity... -->
              </list>
            </constructor-arg>
          </bean>
          
        • 也可以傳入bean ,淡入ref tag,且是set collection
        • <constructor-arg>
              <set>
                <ref bean="sgtPeppers" />
                <ref bean="whiteAlbum" />
                <ref bean="hardDaysNight" />
                <ref bean="revolver" />
                ...
              </set>
          </constructor-arg>
          
        • c-namespace無法實現wiring collection的功能
    • Property injection
      • 例:沒有constructor,但在setter有使用到bean
      • public class CDPlayer implements MediaPlayer {
          private CompactDisc compactDisc;
          @Autowired
          public void setCompactDisc(CompactDisc compactDisc) {
            this.compactDisc = compactDisc;
          }
          public void play() {
            compactDisc.play();
        } }
        
        • 對牆依賴使用constructor injection
        • 對可選擇的dependency使用paremeter injection,將其injects至parameter中
      • <bean id="cdPlayer" class="soundsystem.CDPlayer">
          <property name="compactDisc" ref="compactDisc" />
        </bean>
        
        • 也可以使用p-namespace
        • <bean id="cdPlayer" class="soundsystem.CDPlayer" p:compactDisc-ref="compactDisc" />
        • compactDisc: parameter name
      • 因為c-namespace不能夠使用collection,但可以藉由spring util-namespace
      • 步驟:
        1. 使用util-namespace建立一個list的bean
        2. <util:list id="trackList">
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friends</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omitted for brevity... -->
          </util:list>
          
        3. 將list bean injects至parameter中
        4. <bean id="compactDisc"
                class="soundsystem.BlankDisc"
                p:title="Sgt. Pepper's Lonely Hearts Club Band"
                p:artist="The Beatles"
                p:tracks-ref="trackList" />
      • 其他Spring util-namespace
        • util:constant, ref某個type的public static field,並將之轉為bean
        • util:list, java.util.List value or ref
        • util:map, java.util.Map  value or ref
        • util:properties, java.util.Properties create java.util.properties type bean
        • util:property-path: ref一個bean的property,並將之轉為bean
        • util:set: java.util.Set value or ref

    沒有留言:

    張貼留言