前情回顾
前面几篇文章主要分析了 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 对象,内容不多,后文再详细分解吧。
休息会~
转载:https://blog.csdn.net/u010551118/article/details/114312307