學(xué)習(xí)和了解下spring中g(shù)enBean函數(shù)具體的細節(jié),其中包含了@value
是如何和配置文件中的鍵值對關(guān)聯(lián)上以及實例類的引用巾乳,這兩部分打算分為上下兩篇文章去詳細介紹下主穗。本文先介紹注解的值是如何和bean綁定并且填充的茎刚。
接下來使用如下的小demo具體學(xué)習(xí)和了解下。
public class Student {
@Value("${name}")
private String className;
// 使用了@value注解谎僻,其中名稱是name
// 我們本章就是具體學(xué)習(xí)這個值是怎么樣的方式娄柳,在什么時候注入進去的
@Override
public String toString() {
return "Student{" +
"className='" + className + '\'' +
'}';
}
}
public class Bootstrap {
public static void main(String[] args){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("context.xml");
Student student = (Student)applicationContext.getBean("student");
// getBean 入口
System.out.println(student.toString());
}
}
name = CS-2
// 配置了name這個鍵值對,最后被填入Student類中
在具體分析源碼之前想象下如果我們是這個項目的開發(fā)者艘绍,我們需要考慮到哪些問題赤拒?
- 現(xiàn)在已知的存在不同類型(scope)的bean被存儲在容器中
- 把獲取的bean列表 循環(huán)一遍依次實例化、填充數(shù)據(jù)
- 如果存在引用的則需要在實例化之前先行實例化引用類
- 各種不同類型的容器诱鞠,例如實例化好的挎挖,正在實例化的、正在被銷毀的航夺、已經(jīng)消耗的
- 填充數(shù)據(jù)這部分操作得支持
@value
等類似操作 - 對外提供各種接口蕉朵,便于用戶可以自定義操作bean(例如BeanPostProcess)
- 相互引用的情況,A引用B阳掐,B引用A該如何解決
- 各種緩存容器始衅,提高效率
- 還有可能存在的并發(fā)問題
接下來就看看具體的源碼細節(jié),在哪些地方會解決我們的疑惑
AbstractBeanFactory 類的doGetBean函數(shù)
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
// 根據(jù)傳入的bean的名稱獲取實際的beanName
// 在這里其實就是看起是否為工廠bean還是bean工廠
// 后續(xù)會有一篇重點分析下工廠bean和bean工廠的差異
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
// 從已經(jīng)創(chuàng)建好的singletonObjects集合中驗證是否已經(jīng)存在該實例缭保,已經(jīng)存在了就沒必要再調(diào)用生成了汛闸,不過需要注意到單例才是這樣的,針對非單例的bean不是這樣的
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 + "'");
}
}
// 已經(jīng)存在了這樣的實例數(shù)據(jù)了
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 如果是當(dāng)前處理的beanName已經(jīng)在prototype容器中(同一個線程)被處理艺骂,則拋出異常
// 看源碼會發(fā)現(xiàn)是存儲在ThreadLocal中的
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 如果父類存在而且當(dāng)前需要處理的beanName還不在容器中
// 需要由父類去調(diào)用生成相關(guān)的實例對象
String nameToLookup = originalBeanName(name);
// 這個originalBeanName就是去掉name中存在的多于的&诸老,至多只保留一個&
if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// 沒有參數(shù)
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
// 傳入的默認typeCheckOnly是false,把當(dāng)前的beanName加入到準(zhǔn)備實例化的alreadyCreated的容器中彻亲,存在并發(fā)的情況孕锄,源碼使用了synchronized
markBeanAsCreated(beanName);
}
// 上述步驟完成,對一個普通的bean而言就是加入到準(zhǔn)備實例化的容器中
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 綜合beanName本身映射的beandefinition和可能存在的父類beandefinition的屬性
// 存儲到一個全新的對象RootBeanDefinition中
checkMergedBeanDefinition(mbd, beanName, args);
// 當(dāng)前bean引用的其他bean苞尝,那很明顯需要先實例化依賴的bean畸肆,再實例化本身
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
// 如果當(dāng)前beanName和dep存在相互依賴的情況,拋出異常
// 可是有個問題宙址,不知道大家想到?jīng)]有轴脐?
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
// 往當(dāng)前的dependentBeanMap和dependenciesForBeanMap填入依賴關(guān)系
// 其中dependentBeanMap 是存儲的當(dāng)前對象依賴什么
// dependenciesForBeanMap 是被誰依賴被當(dāng)前對象所依賴
getBean(dep);
// 每一個依賴的bean實例化
}
}
// 以上完成了bean的依賴問題,那就開始實例化本身了
if (mbd.isSingleton()) {
// 是單例類型
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 這個才是真正的創(chuàng)建bean對象的步驟B丈啊4笤邸!
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
....
在上述代碼中有提一個疑問注益,關(guān)于循環(huán)依賴的碴巾。循環(huán)依賴是A引用B的同時B也引用A,看上去形成了循環(huán)依賴丑搔,又如現(xiàn)場互斥一般死鎖了厦瓢,不過有一點需要注意到提揍。
- 構(gòu)造器注入的時候才會真正形成循環(huán)依賴,會提示錯誤
- 如果是setting注入則不會報錯的煮仇,上面好像沒說具體的依賴類型劳跃,只是有個mbd.getDependsOn
回到過去看下如何取到getDependsOn數(shù)據(jù)的
BeanDefinitionParseDelegate 文件
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele,
String beanName,BeanDefinition containingBean, AbstractBeanDefinition bd) {
.....
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
// 支持多個數(shù)據(jù),用逗號分開
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
很明顯上述代碼可以知道getDependsOn是bean的另一種屬性DEPENDS_ON_ATTRIBUTE設(shè)置的值浙垫,這是一種比ref更強制性依賴的配置刨仑,從spring3.0開始有的,會提前初始化夹姥,看代碼分析也確實是如何杉武,但是和我們理解的循環(huán)依賴貌似不是同一件事情,這點后面再說佃声。
繼續(xù)看如何生成object的
AbstractAutowireCapableBeanFactory 文件
protected Object createBean(String beanName, RootBeanDefinition mbd,
Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 獲得當(dāng)前bean的的class艺智,并且為了嚴(yán)格加載當(dāng)前bean,生成一個新的對象mbdToUse
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
try {
mbdToUse.prepareMethodOverrides();
// 校驗當(dāng)前的bean使用overrides 重載方法是否合法(未重載則提示錯誤)
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 可以產(chǎn)生一個新的proxy代理去完成某些功能圾亏,也是一個接口
// 需要去實現(xiàn)InstantiationAwareBeanPostProcessor類
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
// 來到了真正去創(chuàng)建一個普通的bean的地方了
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
上面的代碼中介紹了InstantiationAwareBeanPostProcessor這個類,這個類也是BeanPostProcessor的一種封拧,代碼中這樣描述這個功能的Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.志鹃。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// 初始化一個bean的包裝類(貌似框架級別的包裝類都喜歡使用wrapper)
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 生成bean實例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
.....
}
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 確認當(dāng)前處理的bean是需要被處理的bean
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());
}
if (mbd.getFactoryMethodName() != null) {
// 存在工廠方法,用工廠方法去實現(xiàn)實例化的功能
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);
}
}
// SmartInstantiationAwareBeanPostProcessor
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);
}
至此已經(jīng)生成了一個對象泽西,但是其并沒有相關(guān)屬性曹铃,需要填充數(shù)據(jù)了,繼續(xù)看doCreateBean方法
BeanWrapper instanceWrapper = ......
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
// 依舊是對beanpostprocessor接口的對外處理捧杉,接口是MergedBeanDefinitionPostProcessor
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 已經(jīng)緩存的實例用來解決循環(huán)引用
if (earlySingletonExposure) {
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// expose 對外輸出的對象
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
// 填充數(shù)據(jù)陕见,這一步就是完成數(shù)據(jù)從配置文件到對象屬性的設(shè)置
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
如下圖,最后是使用了AutowireAnnotationBeanPostProcessor 類去實現(xiàn)指的注入味抖,圈住的地方恰好可以把值"CS-2"塞入"className"中
最后可能需要對獲取到的object轉(zhuǎn)一下格式评甜,就完成了一整個的getBean的操作。
在這里有一點需要注意到仔涩,在解析xml生成beandefinition和調(diào)用getBean之間并沒有絕對的順序之分忍坷,并不是解析xml完成,再去生成對應(yīng)的實例的熔脂,在解析的時候如果需要具體的實例佩研,則會立即調(diào)用getBean
上面提的幾個問題有些答案或許已經(jīng)清楚了還有些可能不是非常清楚,下一篇文章再從比較宏觀的角度出發(fā)去了解下getBean是如何解決各種問題的以及注解的值具體是如何被掛載上去的霞揉。