03-IOC容器初始化之Bean注入详解 一文中详解分析了Spring是注入Bean的,以后的文章都会在该文的基础上进行知识面的扩展。废话不多说,本文的结构如下:
- Spring注入Bean的几种方式
- 什么是Bean的循环依赖
- Spring “三级缓存”
- 源码方式分析Spring是如何利用 “三级缓存” 解决Bean的循环依赖
Step1:Spring注入Bean的几种方式
Spring有如下几种方式注入Bean:
- 构造器注入
- 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循环依赖做铺垫。