Spring原始碼解析-getBean如何獲取Ioc容器中的bean
在Spring框架中,主要是要IOC容器和AOP切面兩塊,然而從IOC容器中如何獲取bean也對我們而言也是可以來學習解析的。
一、解讀getBean的背景
在工作中,一次通過從IOC容器中獲取通過繼承FactoryBean介面的類,發現在對注入後的類名新增&與不新增&的區別很大,在新增&的情況下,獲取的是當前繼承FactoryBean介面的類;在不新增&的情況下,獲取的是當前繼承FactoryBean介面的類中通過getObject()返回的類物件。
因此引起了我對getBean的原始碼探討,看看Spring框架中究竟是如何來獲取繼承FactoryBean介面的類的bean。
二、簡單程式碼示例
由於工作中的程式碼太過繁瑣,同時也涉及到一些非必要的東西(呵呵,大家應該懂得),所以我通過簡單的示例來通過繼承FactoryBean介面完成IOC的注入,然後在後面的原始碼解讀中來理解getBean方法(本篇文章主要是講解getBean方法,由於getBean依賴通過繼承FactoryBean介面完成IOC的注入的bean來獲取,所以就在這部分來簡單示例)。
1、pojo類
作為後面注入到IOC容器,需要通過getBean獲取的物件
package com.cyw.demo.bean;
public class Girl {
}
2、基於FactoryBean介面繼承的類
通過繼承FactoryBean介面,實現getObject()獲取Girl的物件。
package com.cyw.demo.config;
import org.springframework.beans.factory.FactoryBean;
import com.cyw.demo.bean.Girl;
public class DemoFactoryBean implements FactoryBean<Girl> {
public Girl getObject() throws Exception {
return new Girl();
}
public Class<?> getObjectType() {
return Girl.class;
}
}
3、配置類
通過配置來注入DemoFactoryBean類。
package com.cyw.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DemoConfig {
@Bean
public DemoFactoryBean demoFactoryBean() {
return new DemoFactoryBean();
}
}
4、測試類
package maven_spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.cyw.demo.config.DemoConfig;
public class DemoTest {
@Test
public void test() {
ApplicationContext app = new AnnotationConfigApplicationContext(DemoConfig.class);
Object bean1 = app.getBean("demoFactoryBean");
System.out.println(bean1);
Object bean2 = app.getBean("&demoFactoryBean");
System.out.println(bean2);
}
}
通過只是test()方法,輸出bean1和bean2變數的值。
發現bean1是Gril的例項物件,bean2是DemoFactoryBean的例項物件。
三、getBean()的原始碼解析
現在應該是激動人心的時刻了,因為研究完getBean()的原始碼,就能很清楚的認識到上述示例中為什麼在有沒有新增&,輸出的兩個物件是不同了。
1、先上時序圖
原始碼是基於5.0.6.RELEASE版本,大家可以隨自己的意願來選擇
2、解析時序圖
1)client為我們訪問IOC容器,獲取Bean
public class DemoTest {
@Test
public void test() {
ApplicationContext app = new AnnotationConfigApplicationContext(DemoConfig.class);
Object bean1 = app.getBean("demoFactoryBean");
System.out.println(bean1);
Object bean2 = app.getBean("&demoFactoryBean");
System.out.println(bean2);
}
}
2)通過getBean來進入呼叫的類AbstractBeanFactory.class
此處的原始碼只基於Object bean2 = app.getBean(“&demoFactoryBean”);來講解,如果大家有興趣,可以研究下Object bean1 = app.getBean(“demoFactoryBean”);
現在繼續進入正題:
在AbstractBeanFactory.class類中getBean方法
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
3)doGetBean(name, null, null, false);方法
呼叫 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
4)進入AbstractAutowireCapableBeanFactory.class
@Override
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
String currentlyCreatedBean = this.currentlyCreatedBean.get();
if (currentlyCreatedBean != null) {
registerDependentBean(beanName, currentlyCreatedBean);
}
return super.getObjectForBeanInstance(beanInstance, name, beanName, mbd);
}
5)調會AbstractBeanFactory.class的getObjectForBeanInstance方法
主要看這段程式碼 判斷當前類是否繼承FactoryBean或者beanName是否有&字首
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
6)isFactoryDereference在BeanFactoryUtils.class類中,判斷當前beanName是否有&字首
public static boolean isFactoryDereference(@Nullable String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
通過上述程式碼的呼叫後會返回true,這樣就會直接返回當前的beanInstance(在第5步)
關於Object bean1 = app.getBean(“demoFactoryBean”);這段對應的原始碼,是在上述的基礎上繼續深入即可。
掃描關注:全棧工程師成長記
一個可以交流的平臺,目的是為了做一個影響最有影響力的人的平臺。