1、時序圖
IOC容器的依賴注入是建立在數據BeanDefinition準備好的前提之下的。依賴注入的發生有兩種情況:系統第一次向容器索要bean的時候;bean在配置的時候設置了Lazy-init屬性,該屬性會讓容器完成預實例化,預實例化就是一個依賴注入的過程。Bean的依賴注入看下DefaultListableBeanFactory的基類AbstractBeanFactory的getBean裏面的實現來看下bean依賴注入的全流程。IOC依賴注入的時序圖如下所示:
圖10、spring ioc容器bean注入時序圖
在時序圖中可以看到依賴注入主要有兩個階段:instantiate即bean的實例化;populateBean即對bean所依賴的引用以及屬性進行初始化。
2、instantiate
<pre name="code" class="java">public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (beanDefinition.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse;
synchronized (beanDefinition.constructorArgumentLock) {
constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class clazz = beanDefinition.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
public Constructor run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
}
else {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
}
beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Exception ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(beanDefinition, beanName, owner);
}
}
代碼層次比較清晰,beanDefinition是從之前載入的BeanDefinition中獲取的根節點數據,一種是使用BeanUtils進行實例化,其實就是反射機制進行實例化,找出類的構造函數,再使用構造函數ctor.newInstance(args)實例化一個對象。另外一種就是使用CGLIB進行實例化,CGLIB構造一個實例的基本實現流程如下:
public Object instantiate(Constructor ctor, Object[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.beanDefinition.getBeanClass());
enhancer.setCallbackFilter(new CallbackFilterImpl());
enhancer.setCallbacks(new Callback[] {
NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(),
new ReplaceOverrideMethodInterceptor()
});
return (ctor == null) ?
enhancer.create() :
enhancer.create(ctor.getParameterTypes(), args);
}
首先生成一個Enhancer對象,在使用enhancer對基類、回調方法進行設置,最後使用cglib的create生成一個bean對象。這裏簡單闡述一下反射進行實例化的一個流程。
ClassInstance ci05 = null;
//額外的思考 在第二種類實例化的方式中有沒有一種方法實現有參數的構造方式
//獲得類的構造信息
Constructor[] ctor = Class.forName("ClassInstance").getDeclaredConstructors();
//找到我們需要的構造方法
for(int i=0;i<ctor.length;i++ ){
Class[] cl = ctor[i].getParameterTypes();
if(cl.length == 1){
//實例化對象
ci05 = (ClassInstance) Class.forName("ClassInstance").getConstructor(cl).newInstance(new Object[]{"05"});
}
}
ci05.fun();
基本流程就是上述。類實例化都是在jvm裏面進行的,所以實例化的步驟是如下的:
1、 類加載到jvm;
2、 Jvm對類進行鏈接;
3、 Jvm實例化對象。
上述代碼流程裏面Class.forName("ClassInstance")這個的作用就是前兩步功能;第三步就是ctor. newInstance(args),這樣就是完成了一個類實例化的過程,一般我們在系統裏面new關鍵字就已經將上述三個步驟全部涵蓋了。
在Spring IOC實例化的過程中還有一個地方需要注意的:
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
....
}
值得注意的地方就是if (sharedInstance != null && args == null) 這段代碼的判斷是看這個bean是從beanFactory裏面獲取(false)還是從factoryBean來生產(true)FactoryBean.getObject(),FactoryBean是一個工廠模式。Beanfactory是所有bean的出處,即使FactoryBean也是從Beanfactory來的,那麼FactoryBean是做什麼的呢?我們看下它類的繼承關係就比較容易理解:
圖11、FacotryBean的類關係圖
也就是說FactoryBean其實是各類特定類型beanfactory的工廠,說起來有些拗口,其實就是裏面出來的都是一個factory,例如RMI,JNDI,PROXY等等類型的factory。就可以由其生產。它的設計模式是一個工廠方法。
3、populateBean
populateBean就是bean相關屬性以及依賴類的加載,這裏會觸發相關bean的依賴注入。
在這個過程中也有兩個階段,如下代碼所示:List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
boolean resolveNecessary = false;
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// Possibly store converted value in merged bean definition,
// in order to avoid re-conversion for every created bean instance.
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
// Set our (possibly massaged) deep copy.
try {
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
第一個階段就是resolveValueIfNecessary,這個就是將改類相關的依賴類找出來,這個有很多類型,list,set,map以及object等等,最終模式都是迴歸到resolveReference上來,這個功能就是最終使用beanFacotry.getBean()來完成bean的依賴實例化。
第二個階段就是setPropertyValues,這個過程就是將bean的set和get方法讀出來,將這些類set進去或是read出來。對於在xml中手動配置的bean ref這個比較容易理解,而對於系統自動依賴注入的方式這裏簡單介紹下:
Spring不但支持自己定義的@Autowired註解,還支持幾個由JSR-250規範定義的註解,它們分別是@Resource、@PostConstruct以及@PreDestroy。
@Resource的作用相當於@Autowired,只不過@Autowired按byType自動注入,而@Resource默認按 byName自動注入罷了。@Resource有兩個屬性是比較重要的,分是name和type,Spring將@Resource註解的name屬性解析爲bean的名字,而type屬性則解析爲bean的類型。所以如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不指定name也不指定type屬性,這時將通過反射機制使用byName自動注入策略。
@Resource裝配順序
1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退爲一個原始類型進行匹配,如果匹配則自動裝配;
@Autowired 與@Resource的區別:
1、 @Autowired與@Resource都可以用來裝配bean. 都可以寫在字段上,或寫在setter方法上。
2、 @Autowired默認按類型裝配(這個註解是屬業spring的),默認情況下必須要求依賴對象必須存在,如果要允許null值,可以設置它的required屬性爲false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結合@Qualifier註解進行使用,如下:
@Autowired()
@Qualifier("baseDao")
private BaseDaobaseDao;
3、@Resource(這個註解屬於J2EE的),默認安裝名稱進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,當註解寫在字段上時,默認取字段名進行安裝名稱查找,如果註解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
@Resource(name="baseDao")
private BaseDao baseDao;
4、IOC依賴注入設計模式
在上面講到FactoryBean採用的是工廠方法,我們在這裏講述下改設計模式:
在FactoryBean的設計中Create就是FactoryBean,而ConcreateCreate就相當於HttpInvokerProxyFactoryBean,FactoryMethod就是getObject(),獲取到的object其實就是一個產品,例如ProxyFactory,而這個proxyFacory就可以生產中各類的Proxy出來,這個ProxyFactory就相當於ConcreateProduct了。
工廠方法的優點:
首先,良好的封裝性,代碼結構清晰。一個對象創建是有條件約束的,如一個調用者需要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就可以了,不用知道創建對象的艱辛過程,減少模塊間的耦合。
其次,工廠方法模式的擴展性非常優秀。在增加產品類的情況下,只要適當地修改具體的工廠類或擴展一個工廠類,就可以完成“擁抱變化”。
再次,屏蔽產品類。這一特點非常重要,產品類的實現如何變化,調用者都不需要關心,它只需要關心產品的接口,只要接口保持不表,系統中的上層模塊就不要發生變化,因爲產品類的實例化工作是由工廠類負責,一個產品對象具體由哪一個產品生成是由工廠類決定的。在數據庫開發中,大家應該能夠深刻體會到工廠方法模式的好處:如果使用JDBC連接數據庫,數據庫從MySql切換到Oracle,需要改動地方就是切換一下驅動名稱(前提條件是SQL語句是標準語句),其他的都不需要修改,這是工廠方法模式靈活性的一個直接案例。
最後,工廠方法模式是典型的解耦框架。高層模塊值需要知道產品的抽象類,其他的實現類都不用關心,符合迪米特原則,我不需要的就不要去交流;也符合依賴倒轉原則,只依賴產品類的抽象;當然也符合里氏替換原則,使用產品子類替換產品父類。