知识回顾
解析完Bean
信息的合并,可以知道Spring
在实例化Bean
之后,属性填充前,对Bean
进行了Bean
的合并操作,这里的操作主要做了对Bean
对象标记了@Autowired
、@Value
、@Resource
、@PostConstruct
、@PreDestroy
注解的字段或者方法进行解析,主要涉及到类都是BeanPostProcessor
的实现,可见BeanPostProcessor
接口的重要性。
这里再次回顾下BeanPostProcessor
接口有哪些子接口:
InstantiationAwareBeanPostProcessor
Spring
给机会提前进行实例化,可用通过代理进行对象的创建SmartInstantiationAwareBeanPostProcessor
用于预测
Bean
的类型,决定Bean
的构造函数,用于实例化MergedBeanDefinitionPostProcessor
用于合并
Bean
的信息,即解析Bean对象方法上或者字段上标记的注解,比如@Resource
、@Autowired
、@PostConstruct
等。DestructionAwareBeanPostProcessor
用于销毁
Bean
时调用的,比如执行标有@PreDestroy
注解的方法
这些子接口的实现类比较多,比如:
AutowiredAnnotationBeanPostProcessor
ComonAnnotationBeanPostProcessor
InitDestroyAnnotationBeanPostProcessor
AnnotationAwareAspectJAutoProxyCreator
ScheduledAnnotationBeanPostProcessor
当然还不止这里列出来的,还有其他的就不列了,接下来分析Spring
源码接下来做了什么?
对象的提前暴露
看源码:
// 省略代码....
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 提前暴露对象,用于解决循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 添加一个lambda表达式到三级缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
// 省略代码....
源码这里就添加了一个lambda
表达式到一个Map
中,然后结束了,并且明确说明了提前暴露是为了解决循环依赖问题。
什么是循环依赖?
循环依赖顾名思义,就是你中有我,我中有你,打个比方现在有个对象A
,他有个属性b
,这个属性b是对象B
的,然后对象B
中有个属性a
,属性a
是对象A
的。
现在开始创建对象,按照Spring
的标准创建流程getBean
-->doGetBean
-->createBean
-->doCreateBean
,先实例化,然后属性填充,然后执行aware
方法,然后执行BeanPostProcessor
的before
方法,然后执行init-method
,然后执行BeanPostProcessor
的after
方法。那么在执行属性填充时必然会去查找a
或者b
属性对应的对象,如果找不到就会去创建,那么就会出现下图的样子:
这样必然就出现了循环依赖,你我紧紧相拥,不想放开,死也要在一起的情形。
那么Spring
为什么解决循环依赖需要进行提前暴露对象呢?
所以这个问题就很简单了,我们都知道Bean
的创建是将实例化和初始化分开的,实例化之后的对象在JVM
堆中已经开辟了内存空间地址,这个地址是不会变的,除非山崩地裂,海枯石烂,也就是应用重启了。
因此可以将已经实例化的对象放在另外一个Map
中,一般来说都称之为半成品,当填充属性时,可以将先设置半成品对象,等到对象创建完之后在将半成品换成成品,这样的话对象进行属性填充时就可以直接先使用半成品填充,等到开始初始化时再将对象创建出来即可。
这样看来循环依赖只需要二级缓存就够了,但是在Spring
中,存在一种特殊的对象,就是代理对象。也就是说在放入的半成品我们现在多了一种对象,那就是代理对象,这个时候就会出现使用代理对象还是普通对象呢?所以干脆在搞一个Map
专门存放代理对象,这样就区分出来了,然后在使用的时候先判断下我们创建的对象是需要代理还是不需要代理,如果需要代理,那么就创建一个代理对象放在map
中,否则直接使用普通对象就可以了。
Spring中的实现方式
Spring
是怎么处理的呢?Spring
是将所有的对象都放在三级缓存中,也就是lambda
表达式中:
// 添加一个lambda表达式到三级缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
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);
}
}
}
在属性填充的时候,会执行到getBean
,然后从缓存中获取getSingleton
:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 从一级缓存中获取bean实例
Object singletonObject = this.singletonObjects.get(beanName);
// 如果一级缓存中没有数据并且没有正在创建的Bean直接返回
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果有正在创建的Bean,那么冲二级缓存中获取,早期的单例对象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// 二次检查一级缓存中是否有单例对象
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 二次判断二级缓存中是否存在单例对象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 从三级缓存中获取Bean
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 如果三级缓存中 单例工厂中有对象,那么就将该对象放在二级缓存中,并且清掉三级缓存
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
在获取单例对象时,会执行到三级缓存,然后执行getObject
方法,最终就会触发getEarlyBeanReference
方法的调用:
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;
}
这里会判断,如果这个BeanDefinition
是否满足条件,如果不满足,那么直接返回了,否则就会执行到for
循环中的代码,而getEarlyBeanReference
方法在Spring
中只有AbstractAutoProxyCreator
类进行了实质的实现:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 早期代理对象的引用集合
this.earlyProxyReferences.put(cacheKey, bean);
// 创建代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
点进去:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
首先进行了判断,如果不满足创建代理的条件,都是直接返回这个对象,否则进入创建代理的方法,创建出代理对象,最终放入缓存中。点入到最后会发现使用了两种代理创建方式:
源码中的提前暴露对象牵扯出很多东西,循环依赖,三级缓存,aop等,这里解析了个大概,接下来继续主流程中的属性填充populaeBean
方法。