《深入理解Spring原理》 04-Spring利用 “三级缓存” 解决循环依赖

     03-IOC容器初始化之Bean注入详解   一文中详解分析了Spring是注入Bean的,以后的文章都会在该文的基础上进行知识面的扩展。废话不多说,本文的结构如下:

  1. Spring注入Bean的几种方式
  2. 什么是Bean的循环依赖
  3. Spring “三级缓存”
  4. 源码方式分析Spring是如何利用 “三级缓存” 解决Bean的循环依赖

 

Step1:Spring注入Bean的几种方式

     Spring有如下几种方式注入Bean:

  1.  构造器注入
  2.  Field属性注入
PS: 笔者在此处只列举了两种方式,因为这两种比较有代表性,其他方式不再本章范围内。感兴趣的读者可以自己研究下

   NO1:构造器注入

    如下图所示分别使用注解和xml配置方式向Spring注入了Bean。由于注入的Demo这个Bean是一个空的类,所以该Bean只有一个默认的构造器。

@Component
punlic class Demo {}

xml中定义Bean:
<bean id="demo" class="com.test.Demo"/>

  不管使用下图中哪种方式注入,最终都会调用如下所示 createBeanInstance 方法,通过源码可以看到对于Demo这样的只有构造器的Bean,SPring最终会通过确定Bean的构造器进而去进行反射创建Bean然后注入到IOC中

//创建Bean实例
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // Make sure bean class is actually resolved at this point.
    Class < ?>beanClass = resolveBeanClass(mbd, beanName);

    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }

    Supplier < ?>instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // Shortcut when re-creating the same bean...
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized(mbd.constructorArgumentLock) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    if (resolved) {
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        } else {
            return instantiateBean(beanName, mbd);
        }
    }

    //确定最优构造器
    Constructor < ?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // 使用默认构造器进行初始化
    return instantiateBean(beanName, mbd);
}

 

NO2:Field属性注入

  如下图所示演示了Filed Bean的注入:

public class Test {
    private Demo d;

    public void setDemo(Demo demo) {this.d = demo}
}

<bean id="test" class="com.test.Test">
    <property name="d" ref="demo"/>
</bean>

  由于在xml中配置了Test类中 d 属性的引用,所以Spring在注入Test的时候,会判断类中需要注入的属性。由于在xml中指定了将demo指向了d属性,所以Spring会先注入demo,然后将demo的引用赋值给d.通过debug发现Spring最终调用了如下方法判断需要被赋值引用是否是‘可写’的,即 ph.isWritable()这个方法。该方法主要其实就是判断了一下 d 属性 是否有setter方法。拖Test类中没有setDemo()这个方法,则Spring注入会直接报错。

private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
    PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
    // 判断属性是否是‘可写’的
    if (ph == null || !ph.isWritable()) {
        if (pv.isOptional()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignoring optional value for property '" + tokens.actualName + "' - property not found on bean class [" + getRootClass().getName() + "]");
            }
            return;
        } else {
            throw createNotWritablePropertyException(tokens.canonicalName);
        }
    }

    Object oldValue = null;
    try {
        Object originalValue = pv.getValue();
        Object valueToApply = originalValue;
        if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
            if (pv.isConverted()) {
                valueToApply = pv.getConvertedValue();
            } else {
                if (isExtractOldValueForEditor() && ph.isReadable()) {
                    try {
                        oldValue = ph.getValue();
                    } catch(Exception ex) {
                        if (ex instanceof PrivilegedActionException) {
                            ex = ((PrivilegedActionException) ex).getException();
                        }
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not read previous value of property '" + this.nestedPath + tokens.canonicalName + "'", ex);
                        }
                    }
                }
                valueToApply = convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
            }
            pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
        }
        ph.setValue(valueToApply);
    } catch(TypeMismatchException ex) {
        throw ex;
    } catch(InvocationTargetException ex) {
        PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
        if (ex.getTargetException() instanceof ClassCastException) {
            throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
        } else {
            Throwable cause = ex.getTargetException();
            if (cause instanceof UndeclaredThrowableException) {
                // May happen e.g. with Groovy-generated methods
                cause = cause.getCause();
            }
            throw new MethodInvocationException(propertyChangeEvent, cause);
        }
    } catch(Exception ex) {
        PropertyChangeEvent pce = new PropertyChangeEvent(getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
        throw new MethodInvocationException(pce, ex);
    }
}

 

  综上所述介绍了两种代表性的注入方式,讲解这个的目的是为了为下面要将的 Bean循环依赖做铺垫。

 

Step2

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值