天天看點

spring源碼分析(五)依賴注入

在分析初始化bean時候,我跳過了非常重要的一步,關于spring的依賴注入。這篇部落格會分析spring是如何幫我們實作依賴注入的。

開始之前

在分析spring依賴注入之前,我先想下,如果不用spring我是如何進行注入的。

public class A{
    private B b;
    //省略get和set方法
}

public class B{
}

public static void main(String[] args){
    A a=new A();
    B b=new B();
    a.setB(b);
}
           

很簡單,如果我自己注入屬性,我會:

1)初始化被依賴的對象

2)調用set方法(當然也可以通過反射注入)

有了這個前提,我們分析spring源碼思路就會很清晰。先找到初始化的被依賴對象初始化的地方,再找到注入的地方。

初始化依賴的bean分析

再次回到doGetBean方法

protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {

    //....

    try {
        final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        //如果要初始化的是一個抽象類,就抛異常
        checkMergedBeanDefinition(mbd, beanName, args);

        // Guarantee initialization of beans that the current bean depends on.
        String[] dependsOn = mbd.getDependsOn();
        if (dependsOn != null) {
            for (String dependsOnBean : dependsOn) {
                //查下是不是真的依賴這個類(包括直接依賴和間接依賴)
                if (isDependent(beanName, dependsOnBean)) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
                }
                registerDependentBean(dependsOnBean, beanName);
                getBean(dependsOnBean);
            }
        }
    //...
    return (T) bean;
}
           

依舊是省略了與依賴注入無關的代碼

簡單解釋下:

1)spring在調用doGetBean時候,檢測到這個bean依賴了其他的bean,首先會檢測該bean是否真的依賴了這個bean

直接依賴和間接依賴都算

比如檢測a是否依賴c,a -> c,a -> b -> c都算

2)記錄依賴關系(比如記錄了a依賴了c,c被a依賴了)

3)初始化依賴的bean

依賴的注入

分析完了依賴的初始化後,接下來就到了spring是如何進行注入的。

首先說下位置,

spring是在建立bean的過程中,注入bean的。具體位置是在建立bean之後(可以了解為new 了對象之後)

具體方法是在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean方法裡

populateBean方法

同樣,對于bean的populateBean也去掉了各種擴充邏輯(主要是BeanPostProcessor)

//核心的一步
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

//當beanDefinition配置的政策是AUTOWIRE_BY_NAME或者AUTOWIRE_BY_TYPE時候,預設是0(也就是不會走進去)
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
        mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
    MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

    // Add property values based on autowire by name if applicable.
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
        autowireByName(beanName, mbd, bw, newPvs);
    }

    // Add property values based on autowire by type if applicable.
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        autowireByType(beanName, mbd, bw, newPvs);
    }

    pvs = newPvs;
}

//省略後置處理

applyPropertyValues(beanName, mbd, bw, pvs);
           

如果是使用的xml配置方式,比如:

<bean id="car" class="com.hdj.learn.spring.demo.Car"/>
<bean id="person" class="com.hdj.learn.spring.demo.Person">
    <property name="name" value="duanji"></property>
    <property name="car" ref="car"></property>
</bean>
<alias name="person" alias="p"/>
           

那麼,在分析xml時候,就會将PropertyValues設定到BeanDefinition中。是以,這種方式注入邏輯就簡化成這樣:

PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
applyPropertyValues(beanName, mbd, bw, pvs);
           

這樣是不是就超簡單了,核心邏輯就在applyPropertyValue中

AbstractAutowireCapableBeanFactory.applyPropertyValues

spring對于注入的處理還是挺複雜的,這裡先隻對最簡單的資料類型(比如string)進行分析。

先看下方法的簽名

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs);

這裡要注意的是PropertyValues屬性,之前也說過,PropertyValues就像是一個map,對于上述配置的xml,PropertyValues可以說,把我們注入需要的東西都準備好了。

name 需要注入 我們配的value “duanji”

car需要注入引用 car

spring源碼分析(五)依賴注入

同樣的對于applyPropertyValues這裡省略了其他邏輯(隻分析最簡單的String的注入)

String propertyName = pv.getName();  //注入的field的變量名,也就是property配置的name
Object originalValue = pv.getValue(); // 注入的值 TypedStringValue
// 1.1 resolveValueIfNessary
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));
}
           

resolveValueIfNessary

并不是我們往xml配置的值就直接可以使用的

比如這樣的配置:

<bean id="car" class="com.hdj.learn.spring.demo.Car">
    <property name="cname" value="寶馬"/>
</bean>
<bean id="person" class="com.hdj.learn.spring.demo.Person">
    <property name="name" value="duanji"></property>
    <property name="car" ref="car"></property>
    <property name="carName" value="#{car.cname}"/>
</bean>
           

這個時候,我們希望往carName裡注入的值,就不是

#{car.cname}

而是

寶馬

resolveValueIfNessary就是做這件事的。這裡我們依舊截取很小一段,隻是分析String類型

BeanDefinitionValueResolver.resolveValueIfNessary

else if (value instanceof TypedStringValue) {
    // Convert value to target type here.
    TypedStringValue typedStringValue = (TypedStringValue) value;
    //轉換以及替換邏輯
    Object valueObject = evaluate(typedStringValue);
    try {
        Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
        if (resolvedTargetType != null) {
            return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
        }
        else {
            return valueObject;
        }
    }
    catch (Throwable ex) {
        // Improve the message by showing the context.
        throw new BeanCreationException(
                this.beanDefinition.getResourceDescription(), this.beanName,
                "Error converting typed String value for " + argName, ex);
    }
}
           

evaluate的具體細節這裡就不分析了,大概就是判斷下字元串裡的#{} ,然後執行spel表達式。

setPropertyValues

到了這一步,就剩下最後的設定值了

具體的流程比較長,這裡就貼出最後執行set方法的地方(核心邏輯在這裡org.springframework.beans.BeanWrapperImpl.BeanPropertyHandler#setValue)

spring源碼分析(五)依賴注入

很簡單,就是反射。反射往對象設定對應的值。

總結

spring的依賴注入還是很複雜的,這裡隻是分析了下大概的流程。并且簡單說明了String是如何注入的。

大概流程是:

1)doGetBean裡發現有依賴需要注入,初始化這些依賴

2)populateBean裡對依賴進行處理(字元串的處理,依賴的對象的處理)

3)通過反射方式,為對象設定值

問題

本篇文章隻是進行了下粗略的分析,關于很多細節都沒有深入。

比如 autowireMode 又比如 使用注解的方式是如何進行依賴注入的(注解方式是通過PostProcessor方式進行的注入)這些會在下面幾篇部落格裡深入的分析。