javascript
java 从一个容器获取对象,如何从 Spring IoC 容器中获取对象?
前面幾篇文章主要分析了 Spring IoC 容器如何初始化,以及解析和注冊我們定義的 bean 信息。
其中,「Spring 中的 IoC 容器」對 Spring 中的容器做了一個概述,「Spring IoC 容器初始化」和「Spring IoC 容器初始化(2)」分析了 Spring 如何初始化 IoC 容器,「Spring 是如何解析 標簽的? 」分析了 Spring 如何解析 標簽及其子標簽,并注冊到 BeanFactory。
主要流程如下:
IoC 容器已經建立,而且把我們定義的 bean 信息放入了容器,那么如何從容器中獲取對象呢?
本文繼續分析。
配置及測試代碼
為便于查看,這里再貼一下 bean 配置文件和測試代碼。
配置文件 application-ioc.xml
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
測試代碼
public class IocTests {
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-ioc.xml");
System.out.println(context.getBean("person"));
System.out.println(context.getBean("dog"));
}
}
/*
* 輸出結果:
* Person{id=12, name='Jack-12'}
* Dog{age=1}
*/
如何從容器獲取對象?
從容器中獲取對象是通過 BeanFactory#getBean 方法,它有多個重載的方法,但最終都是通過
AbstractBeanFactory#doGetBean 方法來實現的。doGetBean 方法代碼如下:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
// ...
protected T doGetBean(
String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// 從緩存中獲取單例 bean 對象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// ...
// 處理 FactoryBean 的場景
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 緩存中不存在 bean 對象
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// bean 對象在父容器中,則從父容器中獲取 bean 對象
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 是否只做類型檢查
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 獲取 BeanDefinition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 獲取依賴的 bean 對象
// 若創建一個 bean 對象時依賴其他對象,則先創建被依賴對象
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
// ...
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
// ...
}
}
}
// 創建 scope 為 singleton(單例)的對象
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ...
}
});
// 處理 FactoryBean 的場景
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 創建 scope 為 prototype 的對象
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
// 處理 FactoryBean 的場景
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 創建其他類型對象
else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ′" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
// 處理 FactoryBean 的場景
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
// ...
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 類型檢查
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
// ...
}
}
return (T) bean;
}
}
獲取 bean 對象主要就是通過這個 doGetBean 方法實現的。
該方法雖然看起來稍微有點長,但是呢,它內部的實現更長、更復雜。不過也是有跡可循的,莫慌。
本文先看下這個方法的整體流程,內部邏輯后面再慢慢研究。先上流程圖:
代碼雖然有點長,但梳理下來其實也沒那么復雜了。
這個方法主要做了什么呢?
當從容器中獲取 bean 對象時,首先從緩存中獲取。如果緩存中存在,處理 FactoryBean 的場景。
BeanFactory 和 FactoryBean,這哥倆長得很像,也有個別面試題可能會問到。
嗯……以后有機會單獨分析?
如果緩存中沒有,先去父容器獲取,前面創建 BeanFactory 時可以指定 parent 參數,就是那個。
不在父容器中,若 bean 對象依賴了其他對象,則先創建被依賴的 bean 對象,再根據 標簽的 scope 屬性去創建相應的 bean 對象。
是不是有點像我們平時寫查詢接口時、先從緩存查詢,緩存中沒的話再查詢 DB?
道理是一樣的,空間換時間。
小結
先整體,后細節。
本文先從整體上分析了如何從 Spring IoC 容器中獲取 bean 對象,內容不多,后文再詳細分解吧。
總結
以上是生活随笔為你收集整理的java 从一个容器获取对象,如何从 Spring IoC 容器中获取对象?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: app 应用商店系统php,GitHub
- 下一篇: gradle idea java ssm