讲解内容:
- spring的循环依赖---属性注入--自动注入
- spring bean的生命周期
- spring bean实例化的过程
- spring循环依赖
讲解模式
- 打断点,一步一步走过spring bean循环依赖解决过程。由于spring 创建bean过程繁杂,重要的代码会讲解,不重要的忽略。自动注入也是个重点,后面文章会再次讲解到。
- 本笔记是个人学习子路老师的课程所做的,有兴趣的可以去看看他的课,讲的更好
带着问题学习
问题1:spring当中的循环依赖如何解决的
问题2:spring当中怎么支持循环依赖的
回答:
spring中的AbstractAutowireBeanFactory类里面有个变量allowCircularRefrence=true这是设置是否支持循环依赖的参数,在么个bean创建的过程中,都会进行判断。可以通过启动类中的
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
AbstractAutowireCapableBeanFactory acb = ac.getDefaultListableBeanFactory();
acb.setAllowCircularReferences(false);
ac.register(Test.class);
ac.refresh();
ac.getBean(IndexService.class).getService();
}
或者修改源码,在AnnotationConfigApplicationContext类中直接
public AnnotationConfigApplicationContext(Class... annotatedClasses) {
this();
setAllowCircularReference(false);
register(annotatedClasses);
refresh();
}
问题3:如何证明spring默认支持循环依赖(源码细节)
问题4:对spring源码有什么了解吗?(可以回答一个IOC或者AOP即可)
问题5:对spring源码深入研究,可以进行二次扩展吗?(直接继承BeanFactoryPostProcess类)
public class ZbBeanFactoryPostProcess implements BeanFactoryPostProcessor {
//这里的factory中调用的所有对象都是二级缓存中的,在加载到三级缓存中之前会调用用户自己写的扩展类。
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// GenericBeanDefinition是BeanDefinition的子类,子类具有更多的实现,rootBeanDefinition也是BeanDefinition的子类
GenericBeanDefinition userService = (GenericBeanDefinition)configurableListableBeanFactory.getBeanDefinition("userService");
userService.setScope("");
userService.setBeanClassName("XXX");
}
}
问题6:如何关闭单例循环依赖
启动类中AnnotationConfigApplicationContext.java的构造方法中添加setAllowCircularReference(false即可)
public AnnotationConfigApplicationContext(Class... annotatedClasses) {
this();
setAllowCircularReference(false);
register(annotatedClasses);
refresh();
}
问题7:@Autowired和@Source有什么不一样
回答:在bea初始化的时候bean注入各种属性的时候,处理他们的后置处理器不一样
问题:如何解决循环依赖>>>依赖注入功能>>>初始化完成依赖注入>{
1、初始化bean ------ b
}
问题:循环依赖的过程
回答:
getBean() ------index object --------注入UserService-----getBean(UserService) ---从容器中拿(三级缓存)-----拿不到----判断是否需要第三个缓存中拿------开始new Userservice-----object UserService --------注入index----从容器中拿getBean(Index)-----index不等于nulll,注入半成品index---创建userservice .
问题:spring bean声明周期如何完成依赖注入的呢?
问题:spring bean的产生过程,bean是如何产生的
现有class--beanDefinition(类)------object(bean)
spring懒加载,在类上面直接使用注解@Lazy(true),表明这个类在初始化的时候不加载,到用到的时候才会去加载
注意:
bean----------spring bean
java对象-----对象
spring bean创建过程简单概述
先过过眼,具体内容后面详细讲解,这里有个大概印象
getBean() ------index object --------注入UserService-----getBean(UserService) ---从容器中拿(三级缓存)-----拿不到----判断是否需要第三个缓存中拿------开始new Userservice-----object UserService --------注入index----从容器中拿getBean(Index)-----index不等于nulll,注入半成品index---创建userservice .
文字描述
- 实例化一个ApplicationContext的对象;
- 调用bean工厂后置处理器完成扫描;
- 循环解析扫描出来的类信息;
- 实例化一个BeanDefinition对象来存储解析出来的信息;
- 把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,以便后面实例化bean;
- 再次调用bean工厂后置处理器;
- 当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等等;
- 如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
- 推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;
- spring处理合并后的beanDefinition(合并?是spring当中非常重要的一块内容,后面的文章我会分析);
- 判断是否支持循环依赖,如果支持则提前把一个工厂存入singletonFactories——map;
- 判断是否需要完成属性注入
- 如果需要完成属性注入,则开始注入属性
- 判断bean的类型回调Aware接口
- 调用生命周期回调方法
- 如果需要代理则完成代理
- put到单例池——bean完成——存在spring容器当中
对象的创建过程
1、scan扫描类
2、parse解析,利用for循环对所有遍历的类进行解析,设置类的名称,父类,单例还是原型等等。
for (){
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClassName("XXX");
genericBeanDefinition.setBeanClass("XXXX.class");
genericBeanDefinition.setScope("prototytype"
.......
//放入到一级缓存中
map.put("XXXX");
list.add(xxxx)
}
3、调用扩展,
遍历map validate校验类中是否符合各种规则
然后符合就直接new了。
开启spring循环依赖讲解
循环依赖样例
UserService.java
package test.service;/**
* Created by zhanbei on 2020/12/6.
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserService {
@Autowired
private IndexService indexService;
public UserService(){
System.out.println("UserService start");
}
}
IndexService.java
package test.service;/**
* Created by zhanbei on 2020/12/6.
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class IndexService {
@Autowired
private UserService userService;
public IndexService(){
System.out.println("IndexServicde started");
}
public String getService(){
return "IndexService";
}
}
bean的实例化生命周期
1、启动类中调用的AnnotationConfigApplicationContext类
@Lazy(true)
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Test.class);
ac.getBean(IndexService.class).getService();
}
}
在这里可以做很多事情,可以关闭循环依赖等等,只要在ac.refresh()方法执行之前,可以修改很多spring内部的东西。如下
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
AbstractAutowireCapableBeanFactory acb = ac.getDefaultListableBeanFactory();
//可以在这里关闭循环依赖
acb.setAllowCircularReferences(false);
ac.register(Test.class);
ac.refresh();
ac.getBean(IndexService.class).getService();
}
2、AnnotationConfigApplicationContext类中主要代码,主要构件bean生命周期的是refresh()方法
public AnnotationConfigApplicationContext(Class... annotatedClasses) {
this();
//也可以在这关闭循环依赖,这里是我个人手动修改的spring源码
setAllowCircularReference(false);
register(annotatedClasses);
refresh();
}
3、refresh方法里才是整个bean生命周期开始的地方,准确的开始地方,后面会讲解(refresh在spring-context-5.1.13.RELEASE.jar.org.springframework.context.support.AbstractApplicationContext.class中)
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//调用finishBeanFactoryInitialization方法实例化所有扫描出来的类
//spring在AbstractApplicationContext#finishBeanFactoryInitialization方法中完成了bean的实例化。这点需要记住
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
3.1、finishBeanFactoryInitialization方法中调用preInstantiateSingletons初始化扫描出来的类。
**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*/
rotected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
3.2、preInstantiateSingletons方法会经过一系列的判断之后,调用getBean方法区实例化扫描出来的类
preInstantiateSingletons位于(spring-beans-5.1.13.RELEASE.jar.org.springframework.beans.factory.support.DefaultListableBeanFactory.class)
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean factory = (FactoryBean) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction)
((SmartFactoryBean) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction
3.3、getBean仅为一个空壳,调用了doGetBean方法(代码较长,大致过一样即可,下面会继续针对这个有详细讲解)
public AbstractBeanFactory() {
}
public AbstractBeanFactory(@Nullable BeanFactory parentBeanFactory) {
this.parentBeanFactory = parentBeanFactory;
}
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
public T getBean(String name, Class requiredType) throws BeansException {
return this.doGetBean(name, requiredType, (Object[])null, false);
}
public Object getBean(String name, Object... args) throws BeansException {
return this.doGetBean(name, (Class)null, args, false);
}
public T getBean(String name, @Nullable Class requiredType, @Nullable Object... args) throws BeansException {
return this.doGetBean(name, requiredType, args, false);
}
protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly
String beanName = this.transformedBeanName(name);
Object sharedInstance = this.getSingleton(beanName);
Object bean;
if(sharedInstance != null && args == null) {
if(this.logger.isTraceEnabled()) {
if(this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not
} else {
this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
if(this.isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = this.getParentBeanFactory();
if(parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
String nameToLookup = this.originalBeanName(name);
if(parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckO
}
if(args != null) {
return parentBeanFactory.getBean(nameToLookup, args);
}
if(requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
return parentBeanFactory.getBean(nameToLookup);
}
if(!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
try {
RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
String[] var11;
if(dependsOn != null) {
var11 = dependsOn;
int var12 = dependsOn.length;
for(int var13 = 0; var13 < var12; ++var13) {
String dep = var11[var13];
if(this.isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on r
}
this.registerDependentBean(dep, beanName);
try {
this.getBean(dep);
} catch (NoSuchBeanDefinitionException var24) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' de
}
}
}
if(mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if(mbd.isPrototype()) {
var11 = null;
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
Scope 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, () -> {
this.beforePrototypeCreation(beanName);
Object var4;
try {
var4 = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
return var4;
});
bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException var23) {
throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current
}
}
} catch (BeansException var26) {
this.cleanupAfterBeanCreationFailure(beanName);
throw var26;
}
}
if(requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);
if(convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
} else {
return convertedBean;
}
} catch (TypeMismatchException var25) {
if(this.logger.isTraceEnabled()) {
this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualified
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
} else {
return bean;
}
}
3.4、refresh()方法中在经过一堆的校验之后,开始调用createBean创建bean
()createBean位于org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java)
/**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
* @see #doCreateBean
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
Class resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
doGetBean方法内容很多,这个方法非常重要,不仅仅针对循环依赖。甚至整个springbean生命周期也有举足轻重的地位,下面将会细致讲解
package test.service;
/**
* Created by zhanbei on 2020/12/6.
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @ClassName UserService
* @Description TODO
* @Author zhanbei
* @Date 2020/12/6 21:41
* @Version 1.0
**/
@
Component
public class UserService {@
Autowired
private IndexService indexService;
public UserService() {
System.out.println("UserService start");
}
protected < T > T doGetBean(final String name, @Nullable final Class < T > requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//这个方法非常重要,但是和笔者今天要分析的循环依赖没什么很大的关系//读者可以简单的认为就是对beanName做一个校验特殊字符串的功能//我会在下次更新博客的时候重点讨论这个方法
//transformedBeanName(name)这里的name就是bean的名字
final String beanName = transformedBeanName(name);
//定义了一个对象,服存将来返回出来的bean
Object bean;
// Eagerly check singleton cache for manu ally registered singletons./**
/**注意这是第一次调用getSingleton方法,下面spring还会调用一次
* 但是两次调用的不是同一个方法;属于方法重载
* 第一次getSingleton(beanName) 也是循环依赖最重要的方法
* 关于getSingleton(beanName) 具体代码分析可以参考笔者后面的分析
*于循环弓|用需要在创建bean的过程中去获取被引用的那个类
*而被弓用的这个类如果没有创建,则会调用createBean来创建这个bean
*在创建这个被引用的bean的过程中会判断这个bean的对象有没有实例化
* bean的对象?什么意思呢?
*为了方便阅读,读者-定记住两个概念; 什么是bean,什么是对象
*笔者以为-个对象和bean是有区别的
*对象:只要类被实例化就可以称为对象
* bean:先得是一个对象, 然后这个对象需要经历一系列的bean生 命周期
*后把这个对象put到单例池才能算一个bean
*读者千万注意,笔者下文中如果写到bean和写到对象不是随意写的是和这里的解释有关系的;重点-定要注意; -定;一定; 訖就是spring先new-个对象,继而对这个对象进行生命周期回调
*接着对这个对象进行属性填流,也是大家说的自动注入
*然后在进行AOP判断等等;这一些操作简称---spring生 命周期.
*所以一个bean是一个经历了spring周期的对象,和一个对象有区别
*回到前面说的循环引用,首先spring扫描到一 个 要被实例化的类A
* 于是spring就去创建A; A=new A-a;new A的过程会调用getBean("a"));
*所谓的getBean方法--核心也就是笔者现在写注释的这个getSingleton(beanName)
*所谓的getBean方法-核心也就是笔者现在写注释的这个getSingleton(beanName)
*这个时候get出来肯定为空?为什么是空呢?我写这么多注释就是为了解释这个问题
*可能有的读者会认为getBean就是去容器中获取,所以肯定为空,实不然接着往下看
*如果getA等于空; spring就会实例化A;也就是上面的new A
*但是在实例化A的时候会再次调用一下
* getSingleton(String beanName, ObjectFactory singletonFactory)*笔者上面说过现在写的注释给getSingleton(beanName)
*也即是第- -次调用getSingleton(beanName)
*实例化一共会调用两次getSingleton方法; 但是是重载
*第二次调用getSingleton方法的时候spring会在一 个set集合当中记录一 下这个类正在被创建
*这个定要记住, 在调用完成第一次getSingleton完成之后* spring判读这个类没有创建, 然后调用第二 次getSingleton
*在第二次getSingleton里面记录了一下自己己经开始实例化这个类*这是循环依赖做的最牛逼的地方,两次getSingleton的调用
*也是回答面试时候关于循环依赖必须要回答道的地方; .*要说明的spring实例化一 个对象底层用的是反射;
*spring实例化一个对象的过程非常复杂, 要推断构造方等等;
*这里笔者先不讨论这个过程,以后有机会更新-下
*读者可以理解spring直接通过new关键字来实例化一个对象
*但是这个时候对象a仅仅是一 个对象,还不是一 个完整的bean
*接着让这个对象完成spring的bean的生命周期
*过程中spring会判断容器是否允许循环弓用,判断循环引用的代码笔者下面会分析
*前面说过spring默认是支持循环引|用的, 笔者后面解析这个判断的源码也是spring默认支持循环引|用的证据
*如果允许循环依赖,spring会把这 个对象(还不是bean)临时存起来,放到一个map当中
*注意这个map和单例池是两个map,在spring源码中单例池的map叫做singletonObjects
*而这个存放临时对象的map叫做singletonFactories
*当然spring还有一个存放临时对象的map叫做earlySingletonObjects*所以一共是三个map,有些博客或者书籍人也叫做三级缓存
*为什么需要三个map呢?先来了解这三个map到底都缓存了什么
*第一个map singletonObjects存放的单例的bean
*第二个map singletonFactories存放的临时对象(没有完整springBean生命周期的对象)
*第三个map earlySingletonObjects存放的临时对象(没有完整springBean生命周期的对象)
*笔者为了让大家不懵逼这里吧第二个和第三个map功能写成了一模一样
*实第二个和第三个map会有不一样的地方,但这里不方便展开讲,下文分析*读者姑且认为这两个map是一样的
*第一个map主要为了直接缓存创建好的bean;方便程员去getBean;很好理解*第二个和第三个主要为了循环引用;为什么为了方便循环引用,接着往下看
*把对象a缓存到第二个map之后, 会接着完善生命周期;
*当然spring bean的生命周期很有很多步骤;本文先怀详细讨论;
*后面的博客者再更新;
*当进行到对象a的属性填充的这一周期的时候,发觉a依赖了一个B类
*所以spring就会去判断这个B类到底有没有bean在容器当中
*这里的判断就是从第一个map即单例池当中去拿一 个bean
*后面我会通过源码来证明是从第-个map中拿一个bean的
*假设没有,那么spring会先去调用createBean创建这 个bean
*于是又回到和创建A-样的流程,在创建B的时候同样也会去getBean("B");* getBean核心也就是笔者现在写注释的这个getSingleton(beanName)方法
*下面我重申一下:因为是重点
*这个时候get出来肯定为空?为什么是空呢?我写这么多注释就是为了解释这个问题?
*可能有的读者会认为qetBean就是去容器中获取:
*可能有的读者会认为getBean就是去容器中获取;
*所以肯定为空,期不然,接着往下看; .
*第一 -次调用完getSingleton完成之后会调用第二次getSingleton
*第二次调用getSingleton同样会在set集合当中去记录B正在被创建*笔者记住这个时候set集合至少有两个记录了A和B;
*如果为空就b=new B0;创建一 个b对象;
*再次说明一一下关于实例化一 个对象,spring做的很复杂, 下次讨论
*创建完B的对象之后,接着完善B的生命周期
*同样也会判断是否允许循环依赖,如果允许则把对象b存到第二 个map当中;*提醒- -下笔者这个时候第二个map当中至少有两个对象了, a和b
*接着继续生命周期;当进行到b对象的属性填充的时候发觉b需要依赖A*于是就去容器看看A有没有创建,说白了就是从第一个map当中去找a
*有人会说不上A在前面创建了a嘛?注意那只是个对象,不是bean;
*还不在第一 个map当中对所以b判定A没有创建,于是就是去创建A;*那么又再次回到了原点,创建A的过程中;首先调用getBean("a")
*上文说到getBean("a")的核心就是getSingleton(beanName)
* . 上文也说了get出来a= =null;但是这次却不等于空了
*这次能拿出一个a对象;注意是对象不是bean
*为什么两次不同?原因在于getSingleton(beanName)的源码
* getSingleton(beanName)首先从第一个map当中获取bean
*这里就是获取a;但是获取不到;然后判断a是不是等于空
*如果等于空则在判断a是不是正在创建?什么叫做正在创建?
*就是判断a那个set集合当中有没有记录A;
*如果这个集合当中包含了A则直接把a对象从map当中get出来并且返回*所以这一次就不等于空了,于是B就可以自动注入这个a对象了
*这个时候a还只是对象,a这个对象里面依赖的B还没有注入
*当b对象注入完成a之后,把B的周期走完,存到容器当中
*院之后继续返回,返回到a注入b哪里?
*因为b的创建时因为a需要注入b;于是去get b
*当b创建完成一个bean之后,返回b(b已经是一个bean了)
*要说明的b是一个bean意味着bE 经注入完成了a;这点上面已经说明了*于返迥了一个b,故而a也能注入b了;
*接着a对象继续完成生命周期,当完之后a他在容器中了 到齜循环依赖搞定
*需要说明一下上坟提到的正在创建这种说法并没有防支持
*要说明一下文提到的正在创建这种说法并没有方支持
*自己的认为;各位读者可以自行给他取名字
*笔者是因为存放那些记录的set集合的名字叫做singletonsCurrentlyInCreation*顾名思义,当前正在创建的单例对象。。。。。
*还有上文提到的对象和bean的概念;也没有官方支持
*也是笔为了让读者更好的理解spring源码而提出的个人概念
*但是如果你觉得这种方法确实能让你更好的理解spring源码
*那么请姑且相信笔者对spring源码的理解,假设10个人相信就会有100个人相信*继而会有更多人相信,就会成为官方说法,哈哈。
*以上是循环依赖的整个过程,其中getSingleton(beanName)
*这个方法的存在至关重要
*后说明- - -下getSingleton(beanName)的源码分析,下文分析
*/
Object sharedInstance = getSingleton(beanName);
/**
*如果sharedInstance不等于空直接返回
*当然这里没有直接返回而是调用了getObjectForBeanInstance
*关于这方法以后解释,读者可以认为这里可以理解为
* bean =sharedInstance;然后方法最下面会返回bean
*什么时候不等于空?
*再容器初始化完成之后.
*程序员直接调用getbean的时候不等于空
*什么时候等于空?
*上已经解释过了,创建对象的时候调用就会等于空
*/
if(sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
/**
*判断这个类是不是在创建过程中
*. 上文说了,一个类是否在创建的过程中是第二次调用getSingleton中决定的*这里还没有执行到,如果就在创建过程中则出异常
**/
//prototypesCurrentlyInCreation要联系getSingleton方法
if(isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
else {
/**
*要说明的笔者删了很多和本文无用的代码
*意思就是源码中执行到这个if的时候有很多其他代码
*但是都是一 些判断,很本文要讨论的问题关联不大
*这个if就是判断当前需要实例化的类是不是单例的
* spring默认都是单例的,故而一般都成立的
*接下来便是调用第二_次 getSingleton
*第二次会把当前正在创建的类记录到set集合
*然后反射创建这个实例,組起生命绸期
*第二次调用getSingleton的源码分析会在下文
* **/
if(mbd.isSingleton0) {
sharedInstance = getSingleton(beanName, () - > {
try {
//完成了目标对象的创建
//如果要代理,还完成了代理
return createBean(beanName, mbd, args);
}
catch(BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put threre
// eagerly by the creation process, to allow for circular referencere solution
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ех;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
return(T) bean;
}
}
}
//第一次调用getSingleton99#Object sharedInstance = getSingleton(beanName);
//空壳方法
public Object getSingleton(String beanName) { //重点,-定要记住这里传的是一个true, 面试会考
return getSingleton(beanName, true);
}
/**
. 上面说的true对应这里的第二 个参数boolean allowEarlyReference
顾名思义叫做允许循环引用,而spring在内部调用这个方法的时候传的true
这也能说明spring默认是支持循环引用的,这也是需要讲过面试官的
但是你不能只讲这一点,后面我会总结,这里先记着这个true
这个allowEarlyReference也是支持spring默认支持循环引用的其中一个原因
**/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
/**首先spring会去第一个map当中去获取一 个bean;说白了就是从容器中获取
说明我们如果在容器初始化后调用getBean其实就从map中去获取一个bean
假设是初始化A的时候那么这个时候肯定等于空,前前分析过这个map的意义
**/
Object singletonObject = this.singletonObjects.get(beanName);
/**
我们这里的场景是初始化对象A第一次调用这个方法
这段代码非常重要,首先从容器中拿,如果拿不到,再判断这个对象是不是在set集合
这里的set集合前前E经解释过了,就是判断a是不是正在创建
假设现在a不在创建过程,那么直接返回一个空, 第一次getSingleton返回
**/
if(singletonObject == null && isSingletonCurrentlylnCreation(beanName)) lsynchronized(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;
}
//第二次调用getSingleton sharedInstance = getSingleton(beanName, 0) ->代码我做了删减,删了一些本本文无关的代码
public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
synchronized(this singletonObjects) {
//首先也是从第一个map即容器中获取
//再次证明如果我们在容器初始化后调用getBean其实就是从map当中获取一个bean//我们这里的场景是初始化对象A第一次调用这个方法
//那么肯定为空
Object singletonObject = this.singletonObjects.get(beanName);
if(singletonObject == null) {
/**注意这行代码,就是A的名字添加到set集合当中
也就是笔者说的标识A正在创建过程当中
这个方法比较简单我就不单独分析了,直接在这里给出
这个方法比较简单我就不单独分析了,直接在这里给出
singletonsCurrentlyInCreation.add就是放到set集合当中
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName)&&
!this. singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
**/
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
//这里便是创建一个bean的入口乃
//spring会首先实例化一个对象, 然后生命周期
//走生命周期的时候前面说过会判断是否允许循环依赖
//如果允许则会把创建出来的这个对象放到第二个map当中//然后接着生命周期当他走到属性填充的时候
//会去get- -下B,因为要填充B,也就是大家认为的自动注入//这些代码下文分析,如果完了生命周期
singletonObject = singletonFactory.getObject);
newSingleton = true;
}
}
return singletonObject;
}
}
// 如果允许则会把创建出来的这个对象放到第个map当中
//
// AbstractAutowireCapableBeanFactory#doCreateBean() ZßS31t45
//
// 由于这个方法内容过去多,我删减了 - 些无用代码 上面说的singletonObject =
//
// singletonFactory.getObject();
//
// Ft @JbeaniAbstractAutowireCapableBeanFactory#doCreateBean(EêJãbean;
//
// 下面分析这个方法.
protected Object doCreateBean(final String beanName, final RootBeanDefinitionmbd, final@ Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if(mbd.isSingleton()) {
//如果你bean指定需要通过factoryMethod来创建则会在这里被创建//如果读者不知道上面factoryMethod那你就忽略这行代码
//你可以认为你的A是一个普通类, 不会再这里创建
instanceWrapper = this.factoryBeanlnstanceCache.remove(beanName);
if(instanceWrapper == null)(
//这里就通过反射创建一个对象, 注意是对象不是bean
//iX↑createBeanlnstanceJiiFER, 4XTtGtT//以后如果有更新再来分析这个代码
//读者可以理解这里就是new了一个A对象
instanceWrapper = createBeanlnstance(beanName, mbd, args);
//得到new出来的A,为什么需要得到呢?因为Anew出来之后存到一个对象的属性当中
final Object bean = instanceWrapper.getWrappedlnstance);
//厘点:面试会考.
//这里就是判断是不是支持循环引|用和是否单例以及bean是否在创建过程中
//判断循环弓|用的是&& this. allowCircularReferences
//allowCircularReferences在spring源码当中默认就是true
// private boolean allowCircularReferences = true; 这是spring源码中的定义//并且这个属性上面spring写了-行徘常重要的注释
// Whether to automatically try to resolve circular references between beans//读者自行翻译,这是支持spring默认循环引用最核心的证据.
//读者-要讲给面试官,关于怎么讲,我后面会总结
boolean earlySingletonExposure = (mbd.isSingleton0 && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
//如果是单例,粗正在创建,瓶没有关闭循环引|用则执行
//所以spring原形是不支持循环弓|用的这是证据,但是其实可以解决
//怎么解决原形的循环依赖,笔者下次更新吧
if(earlySingletonExposure) {
//这里就是这个创建出来的A对象a放到第二个map当中//注意这里addSingletonFactory就是往map当中put//需要说明的是他的value并不是-个a对象
/而是一段表达式,但是包含了这个对象的
//所以上文说的第二个map和第 三个map的有点不同
//第三个map是直接放的a对象(下文会讲到第三个map的),//第二个放的是一个表达式包含 了a对象
//为什么要放一个表达式?下文分析吧
addSingletonFactory(beanName, () - > getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.Object exposedObject = bean;try {
//填充属性,也就是所谓的自动注入//这个代码我同一张图来说明
//这个代码我同一张图来说明
poupetean(beaNname mbd, insteWrap exposedObject = ininlean(benlame eposedobject, mbd);
}
return exposedObject;
}
/**创建B的流程和创建A差不多,把B放到set集合,标识B正在创建,继而实例化b对象,然后执行生命周期流程,把创建的这个b对象放到第二个map当中, 这个时候map当中已经有了a,b两个对象。
* 然后执行b对象的属性填充或者叫自动注入时候发觉需要依赖a,于重复上面的getbean步骤,
*调用getSingleton方法;只不过现在a对象已经可以获取了故而把获取出来的a对象、临时对象注入给b对象,然后走完b的生命周期流程后返回b所表示bean,跟着把这个b所表示的bean注入给a对象,
* 最后走完a对象的其他生命周期流程;循环依赖流程全部走完;但是好像没有说到第3三个map,第三个map到底充当了什么角色呢?
* 这个知识点非常的重要,关于这个知识不少书籍和博客都说错了,这也是写这篇文章的意义;笔者说每次读spring源码都不-样的收获, 这次最大的收获便是这里了;我们先来看-下代码;
* 场景是这样的,spring创建A,记住第一次创建A, 过程中发觉需要依赖B,于是创建B,创建B的时候发觉需要依赖A,于是再一-次创建- 第二次创建A,
* 下面代码就是基于第二次创建的A来分析;第二次创建A的时候依然会调用getSingleton,先获取一下a
**/
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //先从第一个map获取a这个bean, 也就是单例池获取
Object singletonObject = this singletonObjects.get(beanName);
if(singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
if(singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
//然后从第三个map当中获取a这个对象
singletonObject = this.earlySingletonObjects get(beanName);
//如果第三个map获取不到a对象,再看是否允许了循环引用
// 而这里的allowEarlyReference是true
//
// 1 为什么是true,.文说了这个方法是spring自 己调用的,他默认传了true
if(singletonObject == null && allowEarlyReference) {
//然后从第二个map中获取一个达式
//这要非常注意第二个map当中存的不是一 个 单纯的对象
//前面说了第二个map当中存的是一个表达式, 你可以理解为存了一个厂//或者理解存了一个方法,方法里面有个参数就是这个对象
//安装spring的命名来分析应该理解为一个厂singletonFactory
//一个能够生成a对象的工厂
//那么他为什么需要这么一个厂
//这里我先大概说一下,为了过工厂来改变这个对象
//至于为什么要改变对象,下文我会分析
//当然大部分情况下是不要改变这个对象的
//读者先可以考虑不需要改变这个对象,
//那么这个map里面存的工厂就生产就是这个原对象,那么和第三个map功能-样
ObjectFactory singletonFactory = this.singletonFactories get(beanName);
if(singletonFactory != nul) {
//调用表达式,说白了就是调用工厂的方法,然后改变对象
//我们假设对象不需要改变的情况那么返回了原对象就是a
//需要改变的情况我们文再分享
singletonObject = singletonFactory.getObject0;
//然后把这个对象放到第三个map当中
this.earlySingletonObjects.put(beanName, singletonObject);
//把这个对象、或者表达式、或者厂从第二个map中移除
this.singletonFactories.remove(beanName);
//厘点:面试会考--为什么要放到第三三个?为什么要移除第二个?
//首先我们通过分析做一个总结:
// spring首先从第一个map中拿 a这个bean
// 不到,从第三个map当中拿a这个对象
//
// 不到,从第二个map拿a这个对象或者工厂
//
// 拿到之后放到第三个map,移除第_一个map里面的表达式、或者厂
//
// 如果对象需要改变,当改变完成之后就把他放到第三个里面
//
// 这里的情况是b需要a而进行的步骤,试想- -下以后如果还有C需要依赖a
//
// 就不需要重复第二个map的工作 了,也就是改变对象的工作了。
//
// 因为改变完成之后的a对象已经在第三个map中了。不知道读者能不能懂笔者的意思如果对象不需要改变道理是一样的, 也同样在第三个map取就是了;
//
// 于为什么需要移除第二个map里面的工厂、 或者表达式就更好理解 了
//
// 他E经对a做完了改变,改变之后的对象已经在第三个map了,为为方便gc啊下面对为什么需要改变对象做分
}
}
return singletonObject;
}
}
}
}
// 为什么需要改变对象?那个表达式、或者说工厂主要干什么事呢?那个工厂、或者表达式主要是调用了下面这个方法
//关于后置处理器笔者其实要说话很多很多
//现在市面上可见的资料或者书籍对后置处理器的说法笔者-般都不同
//我在B站上传过一个4个小时的视频, 其中对spring后处理器做了详细的分析//也提出了-自己的理解和主流理解不同的地方,有兴趣同学可以去看看
//其实简单说-这个方法作用主要是为了来处理aop的;
、
/当然还有其他功能,但是一般的读者 最熟悉的就是aop
//这里我说明一下,aop的原理或者流程有很多书籍说到过
//但是笔者今天亲测了,现在市面可见的资料和书籍对aop的说法都不全
//很多资料提到aop是在spring bean的生命周期里面填充属性之后的代理期完成的/而这个代理周期甚至是在执行生命周期回调方法之后的一个周期
//那么问题来了?什么叫spring生命周期回调方法周期呢?
//首先spring bean生命周期和spring生命周期回调方法周期是两个概念
//spring生命周期回调方法是spring bean生命周期的一部分、或者说一个周期
//简单理解就是spring bean的生命的某个过程会去执行spring的生命周期的回调方法
//此如你在某个bean的方法上面写一个加@ PostConstruct的方法(一 般称bean初始化方法)
//那么这个方法会在spring实例化一个对象之 后,琉属性之后执行这个加注解的方法//我这里叫做spring生命周期回调方法的生命周期,不是我胡说,有官方文档可以参考的
//在执行完spring生命周期回调方法的生命周期之后才会执行代理生命周期
//在代理这个生命周期当中如果有需要会完成aop的功能
//以上是现在主流的说法,也是一般书籍或者"某些大师”的说法
//但是在循环引用的时候就不一样了,循环弓l用的情况下这个周期这里就完成了aop的代理//这个周期严格意义上是在填充属性之前(填充属性也是一个生命周期阶段)
//填充属性的周期甚至在生命周期回调方法之前,更在代理这个周期之前了
//简单来说主流说法代理的生命周期比如在第8个周期或者第八步吧
//但是笔者这里得出的结论,如果- -个bean是循环引用的则代理的周期可能在第3步就完成了//那么为什么需要在第三步就完成呢?
//试想一下A、 B两个类,现在对A类做aop处理,也就是需要对A代理
//
// 不考虑循环引用spring实例化A,然后生命周期确实在第8个周期完成的代理关于这个结论可以去看b站我讲的spring aop源码分析
//
// 但是如果是循环依赖就会有问题
//
// 比如spring实例化A然后发现需要注入B这个时候A还没有走到8步
//
// 还没有完成代理,发觉需要注入B,便去创建B,创建B的时候
//
// 发觉需要注入A,于是创建A,创建的过程中通过getSingleton
//
// 得到了a对象,注意是对象,-个没有完成代理的对象
//
// 然后把这个a注入给B?这样做合适吗?注入的a根本没有aop功能;显然不合适因为b中注入的a需要是一个代理对象
//
// 而这个时候a存在第二个map中; 不是一个代理对象;
//
// 于是我在第二个map中就不能单纯的存一个对象, 要存一个厂
//
// 这个厂在特殊的时候需要对a对象做改变,比如这里说的代理(需要aop功能的情况)这也是三个map存在的必要性,不知道读者能不能get到点
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if(!mbd.isSynthetic( && haslnstantiationAwareBeanPostProcessors) {
for(BeanPostProcessor bp: getBeanPostProcessors()) {
if(bp instanceof SmartlnstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartlnstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
// 总结关于循环引用,如何回答面试:
//
// 首先spring在单例的情况下是默认支持循环引用的(当然原形也有办法,今天先不讨论);
//
// 在不做任何配置的情况下,两个bean相互依赖是能初始化成功的; spring源码中在创建bean的时候先创建这个bean的对象,创建对象完成之后通过判断容器对象的
//
// allowCircularReferences属性决定是否允许缓存这个临时对象,如果能被缓存成功则通过缓存提前暴露这个临时对象来完成循环依赖;
//
// 而这个属性默认为true,所以说spring默认支持循环依赖的,但是这个属性spring提供了api让程序员来修改,所以spring也提供了关闭循环引l用的功能;
//
// 再就是spring完成这个临时对象的生命周期的过程中当执行到注入属性或者自动装配的周期时候会通过getSingleton方法去得到需要注入的b对象;
//
// 而b对象这个时候肯定不存在故而会创建b对象创建b对象成功后继续b对象的生命周期,当执行到b对象的自动注,入周期时候会要求注入a对象;
//
// 调用getSingleton;从map缓存中得到a的临时对象(因为这个时候a在set集合中;这里可以开讲), 且获取的时候也会判断是允许循环弓用,但是判断的这个值是通过参数传进来的,也就是spring内部调用的,spring源码当中写死了为true,故而如果需要扩展spring、或者对spring=次开发的的时候程序员可以自定义这个值来实现自己的功能;不管放到缓存还是从缓存中取出这个临时都需要判断;而这两次判断spring源码当中都是默认为true; 这里也能再次说明spring默认是支持循环引用的;
//
// 然后面试中可以在说说两次调用getSingleton的意义,正在创建的那个set集合有什么用;最后在说说你在看spring循环引用的时候得出的aop实例化过程的新发现;就比较完美了
//
}