标签:类型 时间 turn 条件判断 工作 增强 OLE date 逻辑
目录
之前开了一个解读IOC容器启动源码的坑Spring IOC容器启动流程源码解析(一)——容器概念详解及源码初探,不过由于最近比较忙,这个坑挖了却一直没时间填。最近在看分布式事务相关的开源项目,碰到了一些Spring AOP相关的问题,比如Spring AOP中的增强逻辑是何时以何种方式织入目标类中的;多个切面的执行顺序如何确定;如何以反射的方式调用事务方法等等,才发现我对Spring AOP的底层了解的还是太少了。Spring AOP默认使用动态代理的方式在运行时织入切面,这个动态代理对象需要由Spring容器创建并进行管理。因而,深入了解Spring AOP的前提就是熟悉IOC容器创建一个实例的过程,这个过程清晰了,自然也就找到了继续深入研究Spring AOP的入口。趁着这个机会就先来好好梳理下IOC容器创建实例的流程,顺便也把解读IOC容器启动源码这个大坑的第四部分(初始化单实例bean)先填了,其实这也是整个IOC容器启动流程中最重要的阶段,这部分内容非常复杂,细节相当多,对这部分的讲解主要还是以梳理流程为主,知道容器初始化单实例bean的过程分为哪几个阶段,每个阶段主要做了哪些工作,解决了哪些重要的问题,一些和容器核心功能无关的细节可以适当忽略,这样分清主次更有助于理解。
整个IOC容器的启动过程都包含在容器抽象类AbstractApplicationContext
的模板方法refresh()
中
在这之前已经创建了核心容器BeanFactory,完成了bean定义信息的加载解析和注册,对于用户定义的每一个bean,创建一个对应的BeanDefinition,以beanName为key,Definition为value保存在核心容器beanFactory的map中。
这个时候还没有真正创建Bean,而是创建了一个Bean的设计图——BeanDefinition,之后可以根据这个BeanDefinition创建真正的Bean实例。完成核心容器的创建后,还会注册一些容器的基础组件,之后才会来到启动容器最重要的阶段——初始化bean的阶段,这部分的入口在finishBeanFactoryInitialization(beanFactory)
方法中,如下箭头所示
进入finishBeanFactoryInitialization(beanFactory)方法,在真正进行初始化动作前还会有一些准备工作,这部分内容因为不是特别重要,就在这顺便提及下
上面这部分逻辑中,容器提前初始化了两类特殊的bean,一类是ConversionService,可以进行属性值的转化,比如将前端传过来的特定格式的时间字符串转化为Date对象,功能和PropertyEditor类似;另一类则是实现了LoadTimeWeaverAware接口的Bean,这部分和Spring中的LTW(LoadTimeWeaving)相关,尽管也是AOP,但并不是Spring AOP中的默认实现。初始化时通过调用BeanFactory的getBean(..)方法实现的,这个方法其实才是初始化bean的真正入口,不过后面还会碰到,这里就跳过。进入箭头所指的方法,从方法名可以得知,下面这部分还是属于准备阶段的次要内容
//获取所有BeanDefinition的beanName
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
//遍历所有beanName
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//如果bean不是抽象的且单例且非懒加载则通过if条件
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
//如果是实现FactoryBean接口的bean
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<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
//初始化bean的真正入口
getBean(beanName);
}
}
}
else {
//不是FactoryBean则执行这里,这是初始化bean的真正入口
getBean(beanName);
}
}
}
这里会拿到之前注册的所有BeanDefinition,进行初始化的条件判断,如果Bean被设置为单例(scope=singleton)且非懒加载(lazy-init=false)则会开始真正的初始化流程,如果这其中任一条件不满足,则在容器启动的过程中是不会初始化这个bean的。之后的处理逻辑根据bean是否为FactoryBean类型而有所不同,但最后多会调用getBean()方法,这个方法其实才是初始化bean的真正的入口方法。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
这其实是一个相当通用的方法,它的真正含义其实是供客户端从容器中获取bean,若客户端想要的bean不存在,容器当然会创建并初始化它,但bean可能已经创建好并缓存在容器中,那么直接把缓存的对象返回给客户端就好,所以这个方法的前缀是get而不是create。不过我们是在IOC容器的启动流程中去分析这个方法,这个上下文环境下,所有bean都还未创建,所以这就相当于一个初始化方法。进入内部的doGetBean()方法,这个方法比较长,但是流程还是比较清晰的。
这是transformedBeanName(name)做的工作
//对原始的beanName进行转化,获取真实的beanName,如果是FactoryBean则去除前缀'&',如果是别名则通过
//别名获取真实的名称
final String beanName = transformedBeanName(name);
Object bean;
// 从缓存中获取bean若缓存中不存在则从其ObjectFactory中获取,若ObjectFactory不存在则返回null
Object sharedInstance = getSingleton(beanName);
这个getSingleton(beanName)比较值得讲,一是因为后面还会见到,二是它和spring解决bean循环依赖的方式有关,因而有必要理解其实现原理。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先尝试从singletonObjects这个缓存map中获取,这是个全局的缓存,里面存放着真正创建完成的bean
//单例的bean只会被创建一次,之后便会缓存在这个map中供客户端重复获取
Object singletonObject = this.singletonObjects.get(beanName);
//如果缓存中不存在该name的bean且该bean正在创建过程中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//同步
synchronized (this.singletonObjects) {
//尝试从earlySingletonObjects这个缓存map中获取bean,这个map中的bean并未真正创建完成
//但是提前暴露出来用来解决依赖问题
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//若依旧未获取到则从singletonFactories这个map中获取其工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//成功获取该bean的工厂实例
//调用工厂方法获取bean
singletonObject = singletonFactory.getObject();
//将该bean加入earlySingletonObjects这个map中
this.earlySingletonObjects.put(beanName, singletonObject);
//将创建该bean的工厂从singletonFactories中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
从缓存中获取bean的流程并不复杂,但是因为涉及到3个Map,所以逻辑有点绕。
/**
* Cache of singleton objects: bean name to bean instance.
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
这个map用来缓存真正创建完成的bean,真正创建完成是指对象存在且所有属性/依赖已经注入且所有初始化操作已经完成。
/**
* Cache of early singleton objects: bean name to bean instance.
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
这个map中的bean并为真正创建完成,但是提前放在这个map中暴露出来,主要是为了解决循环依赖问题。
/**
* Cache of singleton factories: bean name to ObjectFactory.
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
这个map中缓存的是用来获取bean的工厂ObjectFactopry,这个工厂中有一个刚创建完成但是未注入属性也未进行初始化的bean,当从工厂中取出这个bean后,该bean会缓存到earlySingletonObjects这个map中,并且对应的工厂会从singletonFactories移除。
为什么要搞的这么复杂?这和Spring解决bean之间的循环依赖的思路有关:Spring在创建Bean A时如果发现A依赖于B会先去创建B,这个发现的时机其实是在为A注入属性时,此时bean A其实已经被创建,但是还未进行任何属性赋值和初始化操作,此时会将这个原始的bean A封装在一个ObjectFactory工厂中,保存在singletonFactories缓存,之后在创建B的过程中如果又需要创建A则会从缓存中获取A的工厂,调用其getObject()方法获取其实力,并将实例对象A放入earlySingletonObjects这个缓存中,之后将对应的ObjectFactory从singletonFactories中移除。
因而getSingleton()
的逻辑就是根据beanName先从全局缓存中查找bean,没找到再从工厂缓存查找其工厂,找到就从工厂中取出,没找到上的话则返回null。
// 从缓存中获取bean若缓存中不存在则从其ObjectFactory中获取,若ObjectFactory不存在则返回null
Object sharedInstance = getSingleton(beanName);
这里我们假设之前没有从当前容器的缓存中找到bean,这样比较符合初始化语境。这时候sharedInstance为null,接着会尝试从当前容器的父容器中去获取
// Check if bean definition exists in this factory.
//获取父容器,尝试从父容器中获取
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
//递归从父容器中获取想要的bean
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);
}
逻辑可以简化为:获取当前容器的父容器并递归调用getBean()方法进行查找
这里有解析bean依赖的操作,原来我一直以为这里就是递归创建依赖bean的入口,但其实这里正如注释所言,只是为了保证当前bean的所有依赖bean已经初始化完毕,
真正开始解析bean之间的依赖关系其实是在后面为bean注入属性时,当发现bean A依赖于bean B时,会暂停A的属性注入和初始化操作转而去创建B。所以这部分不是很重要,了解下即可。
// Guarantee initialization of beans that the current bean depends on.
//保证该bean所有依赖的bean已经初始化完毕
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 + "'");
}
//记录与当前bean有关的依赖关系
registerDependentBean(dep, beanName);
try {
//先尝试获取该bean所依赖的bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
这里再一次调用了getSingleton()方法,不过这次传递了一个实现ObjectFactory接口的匿名内部类(lambda语法简化),
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
// Create bean instance.
if (mbd.isSingleton()) {
//走到这里说明bean的定义是单实例的
//尝试从全局缓存中获取bean,若没获取到则通过BeanDefinition信息创建bean,并清理相关缓存
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
//从FactoryBean中获取真正的bean实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
首先还是会尝试从全局缓存中获取bean,如果不存在才会调用工厂的getObject()方法去创建该bean,这个匿名内部类的getObject()方法又调用了createBean()方法,这个方法定义在AbstractBeanFactory
这个抽象工厂类中,不过具体实现在其子类AbstractAutowireCapableBeanFactory
,这个类中的createBean()才是真正创建Bean的方法
兜兜转转终于来到了最重要的真正创建bean的方法,来到这个方法,说明从缓存中获取bean的尝试失败,转为真正创建并初始化它。
//根据设置的class属性或者根据className来解析Class
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
//2.对override属性进行标记及验证
mbdToUse.prepareMethodOverrides();
}
这里和Spring的方法注入功能相关,Spring除了有构造器注入、属性注入和工厂方法注入外还有方法注入
spring的bean配置中存在lookup-method和replace-method两个配置,这两个放在BeanDefinition的methodOverrides属性中
如果bean实例化的时候检测到methodOverrides属性,会动态的为当前bean生成动态代理并使用相关拦截器对bean做增强,
其底层通过CGLib在运行期动态操作Class字节码实现
比如bean定义中的
方便为单实例bean的多实例属性注入依赖且与Spring容器没有耦合;还有一种
的方法替换另一个bean的方法。
这里应该是初始化bean的流程中第一个允许用户进行回调的扩展点。
在讲解这部分源码前先了解下一个特殊的后置处理器——InstantiationAwareBeanPostProcessor
它扩展自BeanPostProcessor
它内部定义了三个回调方法,其中比较重要的是下面这个
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
在对象实例化前回调,可以通过回调该方法返回一个代理对象来替代默认的对象实例化过程
好了可以开始讲这部分的源码了
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
//实例化bean之前的处理,会获取所有的InstantiationAwareBeanPostProcessor,执行其回调方法
//这部分和Spring AOP相关
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
//短路控制,实例化bean之前的前置处理过程返回的bean如果不为空,则直接返回该bean
if (bean != null) {
return bean;
}
}
/**
* Apply before-instantiation post-processors, resolving whether there is a
* before-instantiation shortcut for the specified bean.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @return the shortcut-determined bean instance, or {@code null} if none
*/
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
//尚未被解析
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
// 执行实例化前置方法
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
//执行实例化后置方法
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
获取后处理器并回调的逻辑封装在resolveBeforeInstantiation()方法中,注意这里的返回值,如果返回值不为null,直接return,不走后面的实例化流程了。
值得一提的是,通过Spring在这里提供的扩展点,确实有可能返回一个代理对象,那么Spring AOP生成的动态代理对象是这里生成的吗?很遗憾,通常情况并不是,除非你做了相关的自定义设置。
当resolveBeforeInstantiation)()方法返回的结果为null,会执行后续的常规实例化操作。
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
doCreateBean是实例化bean的核心方法
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//实例化bean,这里根据BeanDefinition创建BeanWrapper
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
createBeanInstance()里面是创建bean实例(准确来说是个bean的wrapper对象)的过程,这个过程设计两类策略的选择
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
final Constructor<?> ctor, Object... args) {
//判断是否有需要动态改变(lookup-method动态重写,replace-method动态替换)的方法
if (!bd.hasMethodOverrides()) {
if (System.getSecurityManager() != null) {
// use own privileged to change accessibility (when security is on)
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(ctor);
return null;
});
}
//不存在需要动态改变的方法,直接使用反射创建对象
return BeanUtils.instantiateClass(ctor, args);
}
else {
//存在需要动态改变的方法,使用cglib生成子类的方式动态替换原有方法
return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
}
}
之前只是创建了一个空的bean,为bean的属性进行赋值通过下面的方法完成
populateBean(beanName, mbd, instanceWrapper);
整个过程可以分为4步:
//调用InstantiationAwareBeanPostProcessor的实例化后置处理方法
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
//其中一个处理器的回调方法返回false,则跳出循环
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
//不执行后续的属性填充操作,直接返回
return;
}
//根据名称或者类型进行依赖注入
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
//按名称进行依赖注入
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
//按类型进行依赖注入
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
这里尝试为bean注入依赖,可以按名称或者按类型进行注入。这里才是真正开始进行依赖解析并递归创建bean的地方,以autowireByName()为入口一探究竟
1处开始递归创建依赖的bean
2处则是讲与当前bean有关的依赖关系进行注册,主要是填充两个map
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
//dependentBeanMap<String(beanName),Set<String>(依赖于前者的所有beanName集合)>
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
//已经存在
return;
}
}
//dependenciesForBeanMap<String(beanName), Set<String>(被前者依赖的所有beanName集合)>
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
dependentBeanMap:value为依赖于当前bean的所有bean的beanName集合
dependenciesForBeanMap:value为当前bean所依赖的所有bean的beanName集合
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
这步执行完毕,bean的所有依赖就都已经注入,所有属性都已经填充完毕了
exposedObject = initializeBean(beanName, exposedObject, mbd);
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
//回调其初始化前置方法
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
这里的初始化方法有两种,一种是用户在定义bean时配置的init-method,一种是InitialLizingBean接口的的afterProperties()方法
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
//回调初始化后置方法
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
到这里基本上IOC容器创建bean的流程就结束了,只有还有一些无关紧要的内容,就不贴源码了。
本来还希望容器初始化单实例bean这部分内容能够讲的简单清晰,主次分明些,想不到还是写成了流水账,唯一值得欣慰的地方大概是终于把容器启动流程中最复杂也是最重要的阶段——初始化单实例bean的过程好好梳理了一遍,想继续探讨AOP源码的话也能找到切入点了。最后以下面这张图作为总结吧,基本理清了容器实例化bean的过程以及解决循环依赖的思路。
假设要创建的bean A和B之间存在循环依赖,整个过程如下图所示
《spring源码深度解析》
Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段
标签:类型 时间 turn 条件判断 工作 增强 OLE date 逻辑
原文地址:https://www.cnblogs.com/takumicx/p/10162811.html