美文网首页
spring源码分析(五)依赖注入

spring源码分析(五)依赖注入

作者: 编程易行 | 来源:发表于2018-08-19 19:07 被阅读89次

    在分析初始化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

    同样的对于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的依赖注入还是很复杂的,这里只是分析了下大概的流程。并且简单说明了String是如何注入的。

    大概流程是:
    1)doGetBean里发现有依赖需要注入,初始化这些依赖
    2)populateBean里对依赖进行处理(字符串的处理,依赖的对象的处理)
    3)通过反射方式,为对象设置值

    问题

    本篇文章只是进行了下粗略的分析,关于很多细节都没有深入。
    比如 autowireMode 又比如 使用注解的方式是如何进行依赖注入的(注解方式是通过PostProcessor方式进行的注入)这些会在下面几篇博客里深入的分析。

    相关文章

      网友评论

          本文标题:spring源码分析(五)依赖注入

          本文链接:https://www.haomeiwen.com/subject/evzqiftx.html