spring源码学习笔记,要点归纳和代码理解
前言
经过了三节的的铺垫,终于到了对象创建的核心部分了,bean的整个加载过程代码量非常大,逻辑错综复杂,因此这部分我的原则是以理解和把控关键过程和逻辑为基本,不局限于细节实现,以AbstractBeanFactory的doGetBean
方法为骨架阅读抽象出的方法代码,对于重点逻辑详细解析.
bean加载的整体流程
首先贴出精简了异常处理逻辑的doGetBean
方法代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 转换beanName,将alias或id或className统一转换为beanName
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 从缓存中尝试获取实例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 缓存中取出的不一定是创建好的实例,有可能是提早曝光加入到缓存的factoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// 当前bean如果在创建过程中,则发生循环依赖,抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// 检测父类工厂,此处为了保证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);
}
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
getBean(dep);
}
}
// Create bean instance.
if (mbd.isSingleton()) { // 创建单例bean
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
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);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
}
// Check if required type matches the type of the actual bean instance.
// 类型检查
if (requiredType != null && !requiredType.isInstance(bean)) {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
return (T) bean;
}
整个加载过程的步骤大致如下:
- 转换对应的beanName
- 尝试从缓存中加载实例
- bean的实例化
- prototype的依赖检查
- 检测parentBeanFactory
- 将GernericBeanDefinition 转换为RootBeanDefinition
- 寻找依赖
- 针对不同scope创建bean
- 类型转换
循环依赖的处理
- 首先解释下什么是循环依赖:
对于两个class A和B,以及他们对应的bean a和b,A中有类型为B的属性b,B中有类型为A的属性a,两个对象都通过spring管理并且属性都通过spring自动注入,此时便形成了循环依赖. - spring对于循环依赖的解决:
整体思路是:
- 使用factoryBean封装创建对象的逻辑
- 将正在创建的bean对应的factoryBean暴露到缓存中
- 创建bean时先尝试从几个缓存中取出bean或bean的factoryBean
主要应用到四个对象和两个方法:
- singletonObjects: 保存创建的bean和beanName的关系
- singletonFactories: 保运BeanName和创建bean的工厂之间的关系
- earlySingletonObjects: 保存beanName和bean实例之间的关系,与singletonObjects的区别是,当一个单例bean被放到这里后,那么即使bean还在创建过程中,也可以通过getBean方法从这个缓存中获取
- registeredSingletons: 用来保存的当前所有已注册的bean
DefaultSingletonBeanRegistry中的两个方法
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
- 具体步骤:
- spring容器创建单例TestBeanA, 根据无参构造器创建,并通过ObjectFactory接口暴露getEarlyBeanReference()方法,将beanName和封装该方法的ObjectFactory添加到singletonFactory缓存中
- 在执行A的populateBean方法,即装配A的属性时,发现B的依赖,执行getBean(B)方法
- 创建单例B,将B的ObjectFactory加入到singletonFactory,之后继续执行B的populateBean,
- 发现依赖注入中存在TestBeanA,调用getBean(A),这时会通过getSingle(A)的方法调用到之前暴露的getEarlyBeanReference方法,这个方法如下,会返回a的实例.
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
这里附一张调用栈的截图,一目了然.
- 需要注意的:
spring只会针对singleton的setter注入解决循环依赖,对于构造器循环依赖和prototype的循环依赖会直接抛出异常,这是因为spring只缓存single scope的bean,也就是说上述代码逻辑对prototype不适用.而spring将在创建过程中的bean放入一个threadLocal的prototypesCurrentlyInCreation对象中,通过构造器注入的对象会无法通过此处校验而抛出异常.
可以通过((XmlBeanFactory) bf).setAllowCircularReferences(false);
来禁用循环引用
创建bean
创建bean的实现通过lambda表达式将AbstractBeanFactory的doGetBean方法封装到factoryBean中进行的.
- 主要步骤:
- 创建bean的实例
a. 确定构造函数
b. 判断beanDefinition中的getMethodOverrides,决定直接反射构造bean还是通过动态代理的方式构造bean,是AOP实现的切入点 - 记录创建bean的ObjectFactory, 循环依赖的解决
- 属性注入
- 初始化bean
a. 激活aware方法 -- 为实现Aware接口的的bean对象注入对应的对象
b. 处理器的应用 -- 为BeanFactory增加BeanPostProcessor,一种增强器,在执行初始化方法之前和之后执行,可以添加业务逻辑
c. 激活自定义的init方法 -- 配置文件的init-method配置 - 注册DisposableBean
后记
bean的加载可以说是spring的两大核心之一了,也是aop实现的切入点,这部分代码多多阅读,可以把控spring的整体运行原理,对于一些定制的需求也可以更灵活的实现.
对于复杂的逻辑进行分解,分成N个小函数的嵌套,每一层都是对下一层逻辑的总结和概要,这样使得每一层逻辑会变得简单容易理解.
附 测试代码
- bean加载的各个情况
public class MyTestBean {
private String name = "testName";
private Integer id;
public MyTestBean(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "MyTestBean{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
public MyTestBean() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private void init() {
System.out.println("测试bean被初始化了");
}
}
public abstract class LookupTestBean {
public void doSomething() {
System.out.println(getTestBean());
}
public abstract MyTestBean getTestBean();
}
<?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 name="myTestBean" class="com.pctf.beans.MyTestBean" init-method="init">
<property name="name" value="测试对象"></property>
<constructor-arg index="0" value="10"></constructor-arg>
</bean>
<bean name="lookupTestBean" class="com.pctf.beans.LookupTestBean">
<lookup-method name="getTestBean" bean="myTestBean"></lookup-method>
</bean>
<beans profile="dev">
</beans>
</beans>
public class AppTestLaunch {
public static void main(String[] args) {
XmlBeanFactory bf = new XmlBeanFactory( new ClassPathResource("spring-config.xml"));
bf.addBeanPostProcessor(new PostProcessorTest());
MyTestBean myTestBean = (MyTestBean) bf.getBean("myTestBean");
LookupTestBean lookupTestBean = (LookupTestBean) bf.getBean("lookupTestBean");
lookupTestBean.doSomething();
System.out.println(myTestBean.getName());
}
}
- 循环依赖的测试代码
<?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 class="com.pctf.beans.TestBeanA" name="testBeanA">
<property name="name" value="测试BeanA"></property>
<!--<property name="testBeanB" ref="testBeanB"></property>-->
<constructor-arg value="testBeanB" index="0"></constructor-arg>
</bean>
<bean class="com.pctf.beans.TestBeanB" name="testBeanB">
<property name="name" value="测试BeanB"></property>
<!--<property name="testBeanA" ref="testBeanA"></property>-->
<constructor-arg value="testBeanA" index="0"></constructor-arg>
</bean>
</beans>
import com.pctf.beans.TestBeanA;
import com.pctf.beans.TestBeanB;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class BeanLoadingTest {
public static void main(String[] args) {
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("bean-loading-config.xml"));
//((XmlBeanFactory) bf).setAllowCircularReferences(false);
TestBeanA testBeanA = (TestBeanA) bf.getBean("testBeanA");
TestBeanB testBeanB = bf.getBean(TestBeanB.class);
System.out.println(testBeanA.getName());
System.out.println(testBeanB.getName());
}
}
网友评论