Set注入法的缺點是,它無法清晰的表示出哪些屬性是必須的,哪些是可選的。而構造函數注入法的優勢是通過構造函數來強制依賴關系。
使用Set注入時,我們通過<property>元素來注入屬性的值。構造函數注入也一樣,只不過是通過<bean>元素下的<constructor-arg>元素來指定實例化這個bean的時候需要傳遞的參數。constructor-arg沒有name屬性。
解決構造函數參數的不確定性:
有2種方法可以用來處理構造函數的不確定性:通過序號和類型。<constructor-arg>元素有一個可選的index屬性,可以用它來指定構造函數的順序。
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg index="1">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg index="0">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
另一種方法是使用type屬性。可以通過type屬性確定參數的類型
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg type="java.lang.String">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg type="java.net.URL">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
使用構造函數注入的理由:
1、構造函數注入強制使用依賴契約。就是如果沒有提供所有需要的依賴,一個Bean就無法被實例化。
2、由于Bean的依賴都通過它的構造函數設置了,所以沒有必要再寫多余的Set方法。
3、因為只能通過構造函數設置類的屬性,這樣你有效的保證了屬性的不可變性。
Set注入的依據:
1、如果Bean有很多依賴,那么構造函數的參數列表會很長。
2、構造函數只能通過參數的個數和類型來區分。
3、如果構造函數的參數中有2個以上是相同類型的,那么很難確定每個參數的用途。
4、構造函數注入不利于自身的繼承。
自動裝配:
你可以讓Spring自動裝配,只要設置需要自動裝配的<bean>中的autowire屬性。
<bean id="foo" class="com.springinaction.Foo" autowire="autowire type"/>
byName-視圖在容器中尋找和需要自動裝配的屬性名相同的Bean.
byType-視圖在容器中尋找一個與需要自動配置的屬性類型相同的Bean.
constructor-視圖在容器中查找與需要自動裝配的Bean的構造參數一致的一個或多個Bean.
autodetect-首先嘗試使用congstructor來自動裝配,然后使用byType方式。
使用Spring的特殊Bean
編寫一個后處理Bean,Spring為你提供了2次機會,讓你切入到Bean的生命周期中,檢查或者修改它的配置,這叫做后處理,后處理實在Bean實例化以及裝配完成之后發生的。postProcessBeforeInitialization()方法在Bean初始化之前被調用。postProcessAfterInitialization()方法在初始化之后馬上被調用。
package com.wyq.hibernate;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class Fuddifier implements BeanPostProcessor {
/**
* postProcessAfterInitialization()方法循環Bean的所有屬性,尋找java.lang.String類型的屬性。對于每個String屬性,把它傳遞給
* fuddify()方法,這個方法將String將變成嘮叨用語。
*/
public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
try{
for(int i=0;i<fields.length;i++){
if(fields[i].getType().equals(java.lang.String.class)){
fields[i].setAccessible(true);
String original = (String)fields[i].get(bean);
fields[i].set(bean,fuddify(original));
}
}
}catch(IllegalAccessException e){
e.printStackTrace();
}
return bean;
}
/**
* postProcessBeforeInitialization()方法沒有做任何有意義的工作,它只是簡單的返回沒有修改過的Bean.
*/
public Object postProcessBeforeInitialization(Object bean, String name)
throws BeansException {
return bean;
}
private String fuddify(String orig){
if(orig==null)return orig;
return orig.replaceAll("(r|l)", "w").replaceAll("(R|L)", "W");
}
}
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class Fuddifier implements BeanPostProcessor {
/**
* postProcessAfterInitialization()方法循環Bean的所有屬性,尋找java.lang.String類型的屬性。對于每個String屬性,把它傳遞給
* fuddify()方法,這個方法將String將變成嘮叨用語。
*/
public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
try{
for(int i=0;i<fields.length;i++){
if(fields[i].getType().equals(java.lang.String.class)){
fields[i].setAccessible(true);
String original = (String)fields[i].get(bean);
fields[i].set(bean,fuddify(original));
}
}
}catch(IllegalAccessException e){
e.printStackTrace();
}
return bean;
}
/**
* postProcessBeforeInitialization()方法沒有做任何有意義的工作,它只是簡單的返回沒有修改過的Bean.
*/
public Object postProcessBeforeInitialization(Object bean, String name)
throws BeansException {
return bean;
}
private String fuddify(String orig){
if(orig==null)return orig;
return orig.replaceAll("(r|l)", "w").replaceAll("(R|L)", "W");
}
}
注冊后處理Bean:如果你的應用系統運行在Bean工廠中,你需要調用工廠的addBeanPostProcessor()方法來注冊BeanPostProcessor.
BeanPostProcessor fuddifier = new Fuddifier();
factory.addBeanPostProcessor(fuddifier);
如果你是使用應用上下文,你只需要像注冊其他Bean那樣注冊后處理Bean.