Spring IoC Container 3.0.5 之三 - Annotation
- Annotation 與 XML 可以並存,而且 Annotation 先於 XML 執行,所以 XML 可以覆蓋 Annotation 的設定。
- XML 的優點:
- 設定集中在幾個檔案,方便管理。
- Java class 是單純的 POJO,沒有和 Spring 綁在一起。
- 可快速修改,不用重新編譯。
- Annotaion 的優點:
- Spring annotaion 歷程:
- Spring 2.0
- 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。
沒有留言:
張貼留言