Spring 如何从 IoC 容器中获取对象?
前情回顾 前面几篇文章主要分析了 Spring IoC 容器如何初始化,以及解析和注册我们定义的 bean 信息。其中,「Spring 中的 IoC 容器」对 Spring 中的容器做了一...
前情回顾
前面几篇文章主要分析了 Spring IoC 容器如何初始化,以及解析和注册我们定义的 bean 信息。
其中,「Spring 中的 IoC 容器」对 Spring 中的容器做了一个概述,「Spring IoC 容器初始化」和「Spring IoC 容器初始化(2)」分析了 Spring 如何初始化 IoC 容器,「Spring 是如何解析 <bean> 标签的?」分析了 Spring 如何解析 <bean> 标签及其子标签,并注册到 BeanFactory。
主要流程如下:
IoC 容器已经建立,而且把我们定义的 bean 信息放入了容器,那么如何从容器中获取对象呢?
本文继续分析。
配置及测试代码
为便于查看,这里再贴一下 bean 配置文件和测试代码。
配置文件 application-ioc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="person" class="com.jaxer.spring.ioc.Person">
<property name="pet" ref="dog"/>
</bean>
<bean id="dog" class="com.jaxer.spring.ioc.Dog">
<property name="age" value="1"/>
<property name="owner" ref="person"/>
</bean>
</beans>
测试代码
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> T doGetBean(
String name, @Nullable Class<T> 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 对象,再根据 <bean> 标签的 scope 属性去创建相应的 bean 对象。
是不是有点像我们平时写查询接口时、先从缓存查询,缓存中没的话再查询 DB?
道理是一样的,空间换时间。
小结
先整体,后细节。
本文先从整体上分析了如何从 Spring IoC 容器中获取 bean 对象,内容不多,后文再详细分解吧。
休息会~
更多推荐
所有评论(0)