2011-02-18

Spring IoC Container 3.0.5 之三 - Annotation

  • Annotation 與 XML 可以並存,而且 Annotation 先於 XML 執行,所以 XML 可以覆蓋 Annotation 的設定。
  • XML 的優點:
    • 設定集中在幾個檔案,方便管理。
    • Java class 是單純的 POJO,沒有和 Spring 綁在一起。
    • 可快速修改,不用重新編譯。
  • Annotaion 的優點:
    • 設定值貼近 Java class,一致性高。
    • Spring annotaion 歷程:
      • Spring 2.0
        • @Required
      • Spring 2.5
        • @Autowired
        • @Resource
        • @PostConstruct
        • @PreDestroy
      • Spring 3.0
        • @Inject
        • @Qualifier
        • @Named
        • @Provider
    • 註冊 Annotaion:
      <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-3.0.xsd">
         <context:annotation-config/>
      </beans>
      • 以上的設定註冊了這些 BeaPostProcessor:
        • AutowiredAnnotationBeanPostProcessor
        • CommonAnnotationBeanPostProcessor
        • PersistenceAnnotationBeanPostProcessor
        • RequiredAnnotationBeanPostProcessor
      •  <context:annotation-config/> 只會掃描目前 application context 所在的 annotation bean,所以如果把 <context:annotation-config/> 放在 web application context 裡的話,只會偵測到 controller,而略過 service 和 dao。
    • @Required
      • 只能放在 setXXX() 上才有效。
        @Required
        public void setTitle(String title) { ... }
      • @Autowired
        • JSR 330 的 @Inject 有一樣的功能,差別在於 @Inject 預設為 optional,而 @Autowired 為 required。
          • 要使用 JSR 330 必須下載 google 的 guice,這是 JSR 330 目前唯一的實做。
        • 可以放在任意 method 上,method 名稱與參數數量都沒限制,當然至少一個參數。
          @Autowired
          public void setUserDTO(UserDTO userDTO) { ... }
        • 也可以放在 constructor 或 field 上。
        • 至於參數型別限制上,array、typed collection 以及 typed map 都可以,但是 map 的 key 必須為 String 型別。
        • 預設為 required,可以設定為 false。
          @Autowired(required = false)
        • 每個 class 只有一個 constructor 可以為 required @Autowired,optional @Autorwired 的 constructor 則可以有多個;如果有一個 required @Autowired constructor ,那要其他的 optional @Autorwired constructor 做啥?如果同時有多個 construct 符合時,遵行「貪婪模式」,以最多參數符合的中籤。
        • @Autowired 的 required 和 @Required 的意義不太一樣,前者是指在 autowired  的前提下,可有可無的情況,而後者則明確強調一定要有。
        • 可以用在以下的 Spring interfaces,用來取得對應的 Spring 功能:
          • BeanFactory
          • ApplicationContext
          • ResourceLoader
          • ApplicationEventPublisher
          • MessageSource
      • @Qualifier
        • 因為 @Autowired 是 byType,當同樣的 class bean 有多筆時,就會出錯。
        • 可以加上 @Qualifier 來明確指定特定的 bean。
          @Autowired
          @Qualifier("one")
          public void setUserDTO(UserDTO userDTO) { ... }

          <bean ...><qualifier name="one"/></bean>
        • 可以使用的位置:
          • method 上
          • method 的參數上
          • constructor 的參數上
          • 不可以放在 constructor 上
          • required=false 的 method 也要指定
          • required=false 的 constructor 不用指定
        • Spring 的 @Qualifier 功能比 JSR 330 的 @Qualifier 強。
        • Bean id 為預設的 qualifier value,那如果沒有 bean id,也沒有 qualifier value時會怎樣?想太多。
        • 當 qualifier value 所表示的 bean 的型別與 autowired 的型別不同時,會出錯。
        • 可以用於篩選 typed collection,所有符合的 qualifier 都獲選,也就是說 qualidier value 不必是唯一的。
      • @Resource
        • 以 bean id 做 injection,可以放在 field 或者 setter 上。
          @Resource(name="book")
          public void setBookerDTO(BookDTO bookerDTO) { ... }
        • 如果沒有指定 name 屬性,則預設為 field name 或者 setter 去 set 為 name。
        • 可以用在以下的 Spring interfaces,用來取得對應的 Spring 功能,不用指定 namr 屬性:
          • BeanFactory
          • ApplicationContext
          • ResourceLoader
          • ApplicationEventPublisher
          • MessageSource
      • @PostConstruct 與 @PreDestroy
      • @Component
        • 為 Spring bean 的原型,衍生出 @Repository、@Service、@Controller。
        • 使用 @Component 以及衍生自 @Component 的 annotation,則不用再去 XML 裡定義 bean,只要在 XML 設定 annotation component 所在的目錄即可,base-package 可以使用逗號同時指定多個目錄。
          <beans ...>
          <context:component-scan base-package="..."/>
          </beans>
        • 使用上述的設定,會自動註冊兩個 BeanPostProcessor:
          • AutowiredAnnotationBeanPostProcessor
          • CommonAnnotationBeanPostProcessor
        • 如果不想連帶註冊上述兩個 BeanPostProcessor,可以在 context:component-scan 加上 annotaion-config="false" 的屬性。
        • 可以加入 include-filter 或 exclude-filter 來自訂 component scan 行為,filter type 有五種屬性:annotaion, assignable, aspectj, regex, custom。
          <context:component-scan base-package="...">
          <context:include-filter type="regex" expression="..."/>
          <context:exclude-filter type="annotation" expression="..."/>
          </context:component-scan>
        • 如果連 @Component、@Repository、@Service、@Controller 都想要過濾掉,可以在 context:component-scan 加上 use-default-filters="false" 的屬性。
        • 如果 @Component、@Repository、@Service、@Controller 沒有指定 value 屬性的話,Spring 以預設的 BeanNameGenerator strategy 來產生 bean id,就是 class name 的第一個字母改為小寫,其餘不變。
          @Service(value="beanId") 或 @Service("beanId")
        • 如果不想使用預設的 BeanNameGenerator strategy 來產生 bean id,可以實做 BeanNameGenerator。
          <context:component-scan ... name-generator="...MyNameGenerator"/>
      • @Scope
        @Scope("prototype")
        @Repository
        public class ...
        • 如果想自訂 scope 的設定方式,可以實做 ScopeMetadataResolver。
          <context:component-scan ... scope-resolver="...MyScopeMetadataResolver"/>
        • 某些非 singleton 的 scope需要產生 proxy,參考 Spring IoC Container 3.0.5 之二  的「Bean scopes」,可用的值有 no、interfaces、targetClass,interfaces 使用 JDK interface proxy,targetClass 使用 CGLIB proxy。
          <context:component-scan ... scope-proxy="interfaces"/>
      • @Qualifier
        @Repository
        @Qualifier("one")
        public class ...
        • 詳情參考上方的 @Qualifier。
        • 終於發現 annotation 完全比不上 XML 的一點,XML 可以定義同一個 class 多次,annotaion 只能定義一次。
      • Factory method
        @Bean @Qualifier("...")
        public Abean createAbean() { ... }
        @Bean @Scope(...)
        public Abean createAbean() { ... }
        @Bean @Lazy
        public Abean createAbean() { ... }
      • @Repository
        • 處理 exception 轉換,將各家的 exception 轉換成 Spring 專屬的 exception。

          沒有留言:

          張貼留言