Spring系列-2 Bean的生命周期

news2024/9/21 18:50:53

背景:

作为Spring系列的第二篇,本文结合容器的启动流程介绍单例Bean的生命周期,包括Bean对象的创建、属性设置、初始化、使用、销毁等阶段;在此过程中会介绍Spring用于操作Bean或者BeanDefinition的相关扩展接口。
文章重心在于介绍整个Bean生命周期,不拘泥于每个阶段的细节;因此本文中会常见到“主线逻辑”这个关键词,请读者不要对此反感。

容器的启动流程请参考:Spring系列-1 启动流程

1.Bean的生命周期

本文介绍单例Bean的生命周期,本文后续提到中Bean对象默认指代单例Bean。

1.1 流程图

每个Bean的生命周期都包括:实例化、属性设置、初始化、入单例池、销毁等阶段,整个流程可表示为:
在这里插入图片描述
上图中Bean的生命周期主流程可以分为4个部分:
[1] 实例化阶段
实例化阶段的核心目的是生成Bean对象,过程包括构造函数的推断与选择、通过反射调用目标构造函数实例化Bean对象。
Spring在框架中引入了InstantiationAwareBeanPostProcessor接口,提供了操作对象实例化的切入点;用户可以继承InstantiationAwareBeanPostProcessor接口自定义实例化对象过程。

[2] 属性设置阶段
属性设置阶段的核心任务是对已实例化的Bean对象根据配置进行属性设置。
InstantiationAwareBeanPostProcessor接口的postProcessProperties方法也提供给了用户干扰属性设置的能力。
MergedBeanDefinitionPostProcessor接口提供了操作Beandefinition信息的能力,也可以基于Beandefinition提取相关信息;如AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor在实现依赖注入时使用MergedBeanDefinitionPostProcessor进行依赖注入前的数据准备工作。

[3] 初始化阶段
初始化阶段是Spring框架为Bean对象自定义的一个阶段,目的在于当Bea对象完成实例化和属性注入后可以执行一些扩展方法。
该阶段按执行顺序包括:Aware接口的执行与相关Aware属性的设置、执行BeanPostProcessor的前置方法、执行初始化方法、执行BeanPostProcessor的后置方法。
其中,BeanPostProcessor的后置方法是依赖注入、AOP等实现原理(后续Spring系列文章中会反复见到BeanPostProcessor的后置方法)
该阶段的核心逻辑是执行初始化方法,按照调用顺序包括:InitializingBean接口的afterPropertiesSet()方法 和
xml配置文件中通过Init-method属性指定的初始化方法。

[4] 销毁阶段
当容器被注销时,会销毁单例池中的Bean对象,此时进入Bean对象生命周期的销毁阶段,该阶段按照执行顺序包括:DisposableBean的destroy()方法和xml配置文件中通过destory-method属性指定的方法。

1.2 Bean生命周期方法

Bean生命周期方法是Bean对象级别的方法,即每种类型的Bean有自己的方法(仅作用于自己),区别于Spring系统级别的方法(如BeanpostProcessor)作用于所有Bean对象。

1.2.1 Aware接口

Aware接口逻辑较为简单,用于向Bean对象种设置某种类型的属性,如下是BeanFactoryAware接口定义:

public interface BeanFactoryAware extends Aware {
	void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

案例如下:

public class ComponentA implements BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    
    public Object getBean(String beanName) {
        return beanFactory.getBean(beanName);
    }
}

1.2.2 InitializingBean与DisposableBean

InitializingBean接口用于指定初始化逻辑,DisposableBean用于指定销毁逻辑:

public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}

public interface DisposableBean {
	void destroy() throws Exception;
}

1.2.3 SmartInitializingSingleton

public interface SmartInitializingSingleton {
	void afterSingletonsInstantiated();
}

SmartInitializingSingleton在Spring容器完成所有非延迟单例Bean的注入到IOC容器后执行。
用户可通过SmartInitializingSingleton定义Bean注入到IOC后的自定义过程。

1.3 钩子函数

Spring提供这些钩子函数是为了提高Spring框架的扩展能力,基于Spring构造Bean对象的主流程衍生出新功能,
如依赖注入或者AOP代理等。
钩子函数作为Spring系统级别的方法作用于所有Bean对象。

1.3.1 MergedBeanDefinitionPostProcessor

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
	void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
}

MergedBeanDefinitionPostProcessor接口除去因继承BeanPostProcessor引入的接口外,只有一个postProcessMergedBeanDefinition方法:通过beanDefinition,可修改beanDefinition以影响后续的属性设置;也可以从beanDefinition中提取信息做前置准备。
值得注意的是该接口不具备直接操作Bean对象的能力,原因如下:(1) 入参中没有携带Bean对象; (2) 此时Bean对象虽然已经被实例化,但是尚未加入三级缓存中, 即无法通过BeanFactory从IOC中获取该Bean对象。

1.3.2 BeanPostProcessor(BPP)

BeanPostProcessor中存在两个方法,postProcessBeforeInitialization和postProcessAfterInitialization:

public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

从名称可以看出postProcessBeforeInitialization在初始化方法之前执行,postProcessAfterInitialization在初始化方法之后执行。
postProcessBeforeInitialization和postProcessAfterInitialization均提高了直接修改Bean对象的能力:方法的入参上Bean对象和beanName, 返回值为修改后的Bean对象。需要注意:二者的执行时机都在Bean对象已完成实例化与属性赋值后。
其中,postProcessAfterInitialization方法的扩展能力被Spring使用得淋漓尽致;依赖注入、AOP等功能都是基于该方法的扩展能力而实现。

1.3.3 InstantiationAwareBeanPostProcessor(IABPP)

InstantiationAwareBeanPostProcessor接口的定义如下:

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}
	
	@Nullable
	default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
			throws BeansException {
		return null;
	}
}
// 除此之外,还有个postProcessPropertyValues方法,作用同postProcessProperties。
// 因被@Deprecated注解,本文选择进行忽略

InstantiationAwareBeanPostProcessor为扩展实例化过程提供了postProcessBeforeInstantiation和postProcessAfterInstantiation两个方法,为扩展属性设置过程提供了postProcessProperties方法。
基于InstantiationAwareBeanPostProcessor接口的设计目的,对该接口的说明需要结合Bean对象构建过程进行介绍,否则会失去其上下文意义(如同单词之于语句)。

postProcessBeforeInstantiation

postProcessBeforeInstantiation方法的入参为beanName和Bean对象的类型,用户可基于此构建Bean对象并返回,也可返回null(默认返回null);当有对象返回时,将走扩展流程而不是按照Bean生命周期的主体流程来创建Bean对象。
创建Bean对象的createBean方法中主线逻辑如下:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
	throws BeanCreationException {
	// ⚠️: 省略无关逻辑与try-catch等代码...
	// ⚠️:步骤1:调用resolveBeforeInstantiation方法构造Bean对象
	Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
	// 如果bean对象不为null,直接返回
	if (bean != null) {
		return bean;
	}
	// ⚠️:步骤2:调用doCreateBean方法构造Bean对象并返回
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	return beanInstance;
}

这里我们关注的重点在于resolveBeforeInstantiation(beanName, mbdToUse)方法,该方法的主线逻辑如下:

// ... 省略if分支,突出主线逻辑
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
	Object bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
	if (bean != null) {
		bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
	}
	return bean;
}

该方法逻辑较为简单,通过applyBeanPostProcessorsBeforeInstantiation调用IABPP的postProcessBeforeInstantiation方法构建Bean对象(遍历IOC中的IABPP对象,并调用其postProcessBeforeInstantiation方法;返回一个非空的对象,否则返回空),如果为空则直接放回;否则通过applyBeanPostProcessorsAfterInitialization调用BPP的postProcessAfterInitialization方法操作Bean对象。

此时,Bean的构建过程如下所示:
在这里插入图片描述
因为,AOP和依赖注入的实现逻辑都在BPP的postProcessAfterInitialization方法,上述设计使得自定义的Bean对象仍然可以拥有Spring框架的依赖注入和AOP等功能。

postProcessAfterInstantiation和postProcessProperties

对postProcessAfterInstantiation和postProcessProperties二者的介绍放在一起是因为二者都发生在populate()方法中,可以从代码层面认为二者隶属于属性设置过程,populateBean方法的主线逻辑如下:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	// ⚠️:1.调用postProcessAfterInstantiation
	for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
		if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
			return;
		}
	}
	
	PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

	// ... pvs对象修改
	
	// ⚠️:2.调用postProcessProperties
	for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
		pvs = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
	}
	
	// ... check
	
	// ⚠️:3.根据pvs的信息对Bean对象设置属性
	if (pvs != null) {
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
}

populateBean方法的核心功能是进行属性设置,步骤可分为如下三步:
[1] 调用postProcessAfterInstantiation;
[2] 根据配置信息构造pvs对象,调用IABPP对象的postProcessProperties方法修改pvs对象;
[3] 根据pvs的信息对Bean对象设置属性.

其中:步骤[1]中遍历IOC容器中的IABPP对象,并调用其postProcessAfterInstantiation方法,当所有的调用结果都返回true时,继续执行步骤[2]和步骤[3]进行属性的设置;否则跳过步骤[2]和[3],即不会执行属性设置流程.

2.案例介绍

3.原理

本章节基于Spring源码介绍Bean对象的构建过程。

3.1 触发时机

如Spring系列-1 启动流程文中介绍:在Spring容器启动之初先向IOC容器中注入Spring框架内置的组件对象、BeanFactory、BeanPostProcessor等Bean对象等, 之后初始化所有的非懒加载的单例Bean对象。过程中,当遇到依赖的Bean对象时,优先构造被依赖的Bean对象(无论是否时懒加载Bean).
AbstractBeanFactory.refresh()方法:

public void refresh() throws BeansException, IllegalStateException {
	//...
	// Instantiate all remaining (non-lazy-init) singletons.
	finishBeanFactoryInitialization(beanFactory);
	//...
}

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	//...
	// Instantiate all remaining (non-lazy-init) singletons.
	beanFactory.preInstantiateSingletons();
}

preInstantiateSingletons方法:
DefaultListableBeanFactory类提供的preInstantiateSingletons方法会加载所有的非lazy单例Bean,代码如下所示:

public void preInstantiateSingletons() throws BeansException {
	//⚠️:说明:为突出Bean的主线逻辑,省去了FactoryBean逻辑以及一些包装的内容。
	List<String> 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()) {
			getBean(beanName);
		}
	}

	// Trigger post-initialization callback for all applicable beans...
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		if (singletonInstance instanceof SmartInitializingSingleton) {
			SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;	
			smartSingleton.afterSingletonsInstantiated();
		}
	}
}

该方法对beanDefinitionNames进行了两次遍历:第一次遍历时对于非延迟的单例Bean调用getBean(beanName)方法构造Bean对象;第二次对于实现了SmartInitializingSingleton接口的Bean对象调用其afterSingletonsInstantiated方法,执行用户的自定义操作。

3.2 getBean和doGetBean

getBean方法:
该方法可以根据beanName获取Bean对象,如果对象不存在则创建该Bean对象。

public Object getBean(String name) throws BeansException {
	// 以do开头的方法,一般是实际干活的
	return doGetBean(name, null, null, false);
}

注意第二个参数是null,即调用doGetBean方法时,Class requiredType参数为null。

doGetBean方法:
doGetBean主线逻辑如下所示:

protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) {
    String beanName = transformedBeanName(name);
    Object beanInstance;
    
    // ⚠️1:parentBeanFactory.getBean(nameToLookup)

    // ⚠️2:checkMergedBeanDefinition(mbd, beanName, args);

    // ⚠️3:优先构造被依赖的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 + "'");
            }
            registerDependentBean(dep, beanName);
            getBean(dep);
        }
    }

    //  ⚠️4: 构造Bean对象
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);
        });
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    } else if (mbd.isPrototype()) {
        //...
    } else {
        //...
    }

    // ⚠️5:类型校验&&适配
    return adaptBeanInstance(name, beanInstance, requiredType);
}

doGetBean的主线逻辑可以分为如下五个步骤:
[1] 从父亲容器中获取Bean对象
如果当前容器中不存在beanName对应的Bean定义,Spring会尝试通过调用父容器的getBean方法获取Bean对象。
注意:父容器不能获取子容器中的Bean对象, 而子容器可以获取父容器中的Bean对象(因为子容器通过parentBeanFactory属性持有了父容器的引用)。

[2] 校验BeanDefinition
校验逻辑在checkMergedBeanDefinition方法中实现。

[3] 处理依赖关系
如果当前beanName对应的Bean对象存在依赖的Bean时,优先通过getBean方法处理被依赖的Bean对象。
如果存在相互依赖,则抛出BeanCreationException异常。

[4] 构造Bean对象
根据Bean的类型走不同的构造流程,当Bean为单例时,执行如下逻辑构造Bean对象:

sharedInstance = getSingleton(beanName, () -> createBean(beanName, mbd, args));
// getObjectForBeanInstance方法是为了适配FactoryBean类型的对象
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

上述lambda表达式用于传参给FactoryBean, 当执行该FactoryBean对象的getBean时会调用该表达式,因此

getSingleton方法的主线逻辑可以被整合为:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        beforeSingletonCreation(beanName);
        singletonObject = createBean(beanName, mbd, args);
        afterSingletonCreation(beanName);
        addSingleton(beanName, singletonObject);
    }
    return singletonObject;
}

首先尝试从单例池中根据beanName获取Bean对象,如果不为空——直接返回;否则调用createBean(beanName, mbd, args)构造Bean对象(前后环绕着beforeSingletonCreation和afterSingletonCreation方法)。addSingleton(beanName, singletonObject)用于将创建好的对象加入到单例池中并清理缓存。

[5] 类型校验和转换
adaptBeanInstance方法会根据requiredType确定是否执行类型校验和转换, 校验失败时抛出BeanNotOfRequiredTypeException异常:

<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) {
	if (requiredType != null && !requiredType.isInstance(bean)) {
		try {
			Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
			if (convertedBean == null) {
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
			return (T) convertedBean;
		} catch (TypeMismatchException ex) {
			throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
		}
	}
	return (T) bean;
}

当requiredType为null时,不经过任何处理直接返回。在上述getBean调用doGetBean方法时传递的requiredType为null, 因此在Spring容器初始化阶段构造的Bean对象不会经过类型校验和转换。

3.3 createBean和doCreateBean

createBean方法:
创建Bean对象的createBean方法中主线逻辑如下:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
	throws BeanCreationException {
	// ⚠️: 省略无关逻辑与try-catch等代码...
	// ⚠️:步骤1:调用resolveBeforeInstantiation方法构造Bean对象
	Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
	// 如果bean对象不为null,直接返回
	if (bean != null) {
		return bean;
	}
	// ⚠️:步骤2:调用doCreateBean方法构造Bean对象并返回
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	return beanInstance;
}

主线逻辑分为两个步骤:(1) 调用resolveBeforeInstantiation方法构造Bean对象和 (2) 调用doCreateBean方法构造Bean对象。其中resolveBeforeInstantiation方法在请参考1.3.3 InstantiationAwareBeanPostProcessor节中的内容。

doCreateBean方法:
该方法的主线逻辑如下所示:

// 省略无关逻辑
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // ...
    // ⚠️:1.实例化Bean对象
    // Instantiate the bean.
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();

    // ⚠️:2.将半成品对象加入三级缓存
    // 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) {
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // ⚠️:3.Bean对象的属性设置
    populateBean(beanName, mbd, instanceWrapper);
    // ⚠️:4.Bean对象的初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);

    // ⚠️:5.注册DisposableBean
	registerDisposableBeanIfNecessary(beanName, bean, mbd);
	
    // ⚠️:6.返回对象
    return exposedObject;
}

doCreateBean实际承担了构造Bean对象的任务,包括:
        [1] 调用createBeanInstance实例化Bean对象;
        [2] 将半成品的Bean对象加入三级缓存;
        [3] 对Bean对象进行属性设置;
        [4] 对Bean对象执行初始化;
        [5] 注册实现了DisposableBean接口的Bean对象;
        [6] 返回构建完成的Bean对象。

3.4 实例化

createBeanInstance方法:
通过createBeanInstance实例化Bean对象,代码如下所示:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // 1.校验Bean对象的字节码对象:必须为public且存在public的构造方法

    // 2.Supplier接口或者工厂方法不为空的,使用对应的构造流程构造Bean对象

    // 3.使用构造函数构造Bean对象 {
        //resolved表示构造函数是否已经解析完成;autowireNecessary表示是否需要自动装配
        boolean resolved = false;
        boolean autowireNecessary = false;

        if (args == null) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
        if (resolved) {
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            } else {
                return instantiateBean(beanName, mbd);
            }
        }

        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            return autowireConstructor(beanName, mbd, ctors, args);
        }
        return instantiateBean(beanName, mbd);
    }
}

该方法主线逻辑有以下三个步骤:
[1] 校验Bean对象的字节码对象:必须为public且存在public的构造方法;
[2] Supplier接口或者工厂方法不为空的,使用对应的构造流程构造Bean对象;
[3] 使用Bean的构造函数构造Bean对象。
其中第三步为本文感兴趣的部分,整体上看:根据Bean的具体情况选择调用autowireConstructor或者instantiateBean方法进行实例化;并引入了两个标记变量:resolved表示构造函数是否已经完成过解析;autowireNecessary表示是否调autowireConstructor方法进行实例化。
另外,BeanDefinition对象的constructorArgumentsResolved属性用于标记是否已进行过构造函数推断(true表示使用autowireConstructor方法实例化Bean对象,false表示使用instantiateBean方法),是一种优化:当同一个BeanDefinition被多次用于构造Bean对象时,不需要执行重复的推断步骤;该属性针对的是Prototype类型的Bean对象,对单例Bean没有优化的意义。

determineConstructorsFromBeanPostProcessors(beanClass, beanName)也是一个钩子函数,用于干预构造函数的选择:
如果用户自定义了SmartInstantiationAwareBeanPostProcessor实现类,且返回了通过determineConstructorsFromBeanPostProcessors(beanClass, beanName)为对应的bean确定了构造函数,则使用该构造函数实例化Bean对象。

因此,对于单例Bean对象上述逻辑可以简化为:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    boolean resolved = false;
    boolean autowireNecessary = false;

    if (args == null) {
        if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
        }
    }
    if (resolved) {
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        } else {
            return instantiateBean(beanName, mbd);
        }
    }

    if (mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR 
    	|| mbd.hasConstructorArgumentValues() 
    	|| !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, null, args);
    }
    return instantiateBean(beanName, mbd);
}

其实,不看具体实现而仅从上述代码逻辑也可以推断出来:instantiateBean(beanName, mbd)表示调用无参构造函数实例化对象,而autowireConstructor(beanName, mbd, ctors, args)使用有参构造函数。
autowireConstructor方法封装了一个重要的概念,构造函数推断:
(1)当存在使用@Autowired注解的构造函数时,使用被@Autowired注解的构造函数实例化对象;
(2)当只有一个构造函数时,使用该构造函数;
(3)当存在多个构造函数且存在默认构造函数时,使用默认构造函数;
(4)当存在多个构造函数且不存在默认构造函数时,抛出异常。

3.5 populateBean

因介绍InstantiationAwareBeanPostProcessor而前置了该部分内容,请参考1.3.3 InstantiationAwareBeanPostProcessor节中的内容。

3.6 初始化

initializeBean方法:
该方法的源码如下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    
    // ⚠️:1.调用Aware接口
    invokeAwareMethods(beanName, bean);

    // ⚠️:2.调用BPP的postProcessBeforeInitialization方法
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    
    // ⚠️:3.执行初始化方法
    invokeInitMethods(beanName, wrappedBean, mbd);

    // ⚠️:4.调用BPP的postProcessAfterInitialization方法
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    // ⚠️:5.调用BPP的postProcessBeforeInitialization方法
    return wrappedBean;
}

主线逻辑分为如下五个步骤:
        (1) 调用Aware接口,进行Aware相关属性的设置;
        (2) 调用BPP的postProcessBeforeInitialization方法;
        (3) 执行初始化方法;
        (4) 调用BPP的postProcessAfterInitialization方法;
        (5) 调用BPP的postProcessBeforeInitialization方法。
invokeInitMethods方法:

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	// ⚠️:1.调用InitializingBean的afterPropertiesSet方法
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        ((InitializingBean) bean).afterPropertiesSet();
    }

	// ⚠️:2.调用xml配置中通过init-method属性指定的初始化方法
    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

其他步骤在上文中均已进行过介绍,此处不再赘述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/357831.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot 日志文件,你都会了吗?

目录 1、日志文件的作用 2、日志的使用 2.1、从程序中得到日志对象 2.2、使用日志 2.3、日志格式 3、日志级别 3.1、这样的日志级别有什么用&#xff1f; 3.2、日志级别分类和使用 3.3、日志级别设置 4、日志持久化 5、更简单的日志输出——lombok 5.1、对比 5.2、…

阅读源码和查看官方文档,是解决问题最高效的办法。

作为一个工作8年的老程序员告诉你&#xff1a;阅读源码和查看官方文档&#xff0c;是解决问题最高效的办法。不信你来看&#xff0c;这个困扰了读者半天的问题我查了源码和文档后瞬间解决。 前言 上周五有位读者私信我一个问题&#xff0c;说困扰了他半天&#xff0c;研究了一…

利用Rust与Flutter开发一款小工具

1.起因 起因是年前看到了一篇Rust iOS & Android&#xff5c;未入门也能用来造轮子&#xff1f;的文章&#xff0c;作者使用Rust做了个实时查看埋点的工具。其中作者的一段话给了我启发&#xff1a; 无论是 LookinServer 、 Flipper 等 Debug 利器&#xff0c;还是 Flutt…

基于springboot+vue的疾病匿名检测查询系统

基于springbootvue的疾病匿名检测查询系统 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背…

【拥抱开源】发布自己的项目到maven中央仓库

文章目录&#x1f388;第一步&#xff0c;注册账号&#x1f4bf;第二步&#xff0c;登录&#x1f4c0;第三步&#xff0c;设置信息&#x1f4be;第四步&#xff0c;创建问题&#x1f4f9;第五步&#xff0c;验证信息&#x1f3a5;第六步&#xff0c;上传jar包到中央仓库&#x…

网页中字体混淆的处理(简易方法)——爬虫学习笔记

网页中字体混淆的处理——爬虫学习笔记之评论爬取一、网页分析1、打开某点评网址。2、在网页源代码中寻找评论信息&#xff08;1&#xff09;进入这个“火锅店”的详情页&#xff0c;然后右击“检查”&#xff08;2&#xff09;点上面“刷新”详情页&#xff0c;逐步寻找。&…

RK3288 GPIO记录

1、引脚对应的GPIO 编号第一种 使用/sys/kernel/debug/gpio查询所有gpio引脚的基数第二种 cat /sys/class/gpio/gpiochip248/label对应的label就是GPIO引脚&#xff0c;例如下图GPIO8对应的基数就是2482、计算编号编号 基数 PIN脚如GPIO8的基数是248&#xff0c; GPIO8_A6的编…

Java开发 - 数风流人物,还看“微服务”

目录 前言 服务器端的发展历程 早期的服务器 动态的页面 用户内容网站 微服务 企业级应用 互联网应用 微服务介绍 什么是微服务&#xff1f; 为什么使用微服务 怎么使用微服务 Spring Cloud 什么是Spring Cloud Nacos注册中心 什么是Nacos 创建微服务项目 创建…

【Servlet篇】一文带你吃透Request对象

文章目录1. 前言2. Request 对象2.1 Request 继承体系2.2 Request 获取请求参数1. 获取请求行数据2. 获取请求头数据3. 获取请求体数据4. 获取请求参数的通用方式3. IDEA中快速创建 Servlet你问我青春还剩几年&#xff1f;我的回答是&#xff0c;趁现在&#xff0c;正当时。身边…

CS144-Lab2

实验架构 除了写入传入流之外&#xff0c;TCPReceiver 还负责通知 sender 两件事&#xff1a; “First unassembled” 字节的索引&#xff0c;称为“acknowledgment”或 “ackno”。这是接收方需要来自发送方的第一个字节。“first unassembled ” 索引和“first unacceptable…

【项目精选】基于SSH的任务调度系统的设计与实现(视频+源码+论文)

点击下载源码 虽然科技进步在改革开发这几十年来速度飞快&#xff0c;计算机行业也发展迅速&#xff0c;但仍然有大量商家或企业&#xff0c;甚至项目组&#xff0c;采用落后的人工管理方式或者低效的任务调度策略&#xff0c;这无疑是对计算机的一种无视。 计算机处理信息的准…

Python每日一练(20230220)

目录 1. 存在重复元素 II 2. 按要求实现程序功能 3. 分割链表 附录 链表 1. 存在重复元素 II 给定一个整数数组和一个整数 k&#xff0c;判断数组中是否存在两个不同的索引 i 和 j&#xff0c;使得 nums [i] nums [j]&#xff0c;并且 i 和 j 的差的 绝对值 至多为 k。 …

高级数据类型

为了解决单一的业务而存在bitmapsBitmaps类型的基础操作 获取指定key对应偏移量上的bit值getbit key offset 设置指定key对应偏移量上的bit值&#xff0c;value只能是1或0setbit key offset valueBitmaps类型的扩展操作状态位的统计业务需求&#xff1a;1. 统计每天某一部电…

云计算ACP云服务器ECS实例题库(三)

&#x1f618;作者简介&#xff1a;一名99年软件运维应届毕业生&#xff0c;正在自学云计算课程。&#x1f44a;宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。&…

关于监控服务器指标、CPU、内存、警报的一些解决方案

文章目录关于监控服务器指标、CPU、内存、警报的一些解决方案Prometheus Grafana 配置 IRIS / Cach 监控服务器Prometheus简介特点架构图Grafana简介特点配置流程自定义Prometheus接口定义配置 Exporter 监控服务器系统资源简介配置流程使用 Alertmanager报警简介配置流程基于…

软考高级-信息系统管理师之知识管理(最新版)

知识管理 知识与知识管理知识管理常用的方法和工具显性知识的管理隐形知识的管理知识管理的工具学习型组织知识产权保护计算机软件保护条例商标法专利法补充建议学的考点:知识与知识管理 1、知识的分类 知识可分为两类,分别是显性知识与隐性知识。 凡是能以文字与数字来表达…

【C++】关联式容器——map和set的使用

文章目录一、关联式容器二、键值对三、树形结构的关联式容器1.set2.multiset3.map4.multimap四、题目练习一、关联式容器 序列式容器&#x1f4d5;:已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_list(C11)等&#xff0c;这些容器统称为…

新能源汽车,有毒

作者| Mr.K 编辑| Emma来源| 技术领导力(ID&#xff1a;jishulingdaoli)新能源汽车到底有多火&#xff0c;生生逼得奥迪某4S店挂出横幅&#xff1a;我们也有纯电新能源&#xff01;老牌名车的辛酸憋屈溢出屏幕。网友神评补刀“这标语给人‘诺基亚也有智能大屏机’的感觉。”一…

【Java基础】变量

Java基础 变量 variable 变量类型 实例变量(非静态字段) Instance Variables (Non-Static Fields) 类的非静态属性 类变量(静态字段) Class Variables (Static Fields) 类的静态属性 局部变量 Local Variables 参数 Parameters 变量命名 大小写敏感 开头&#xff1a;字…

爬虫基本知识的认知(爬虫流程 HTTP构建)| 爬虫理论课,附赠三体案例

爬虫是指通过程序自动化地从互联网上获取数据的过程。 基本的爬虫流程可以概括为以下几个步骤&#xff1a; 发送 HTTP 请求&#xff1a;通过 HTTP 协议向指定的 URL 发送请求&#xff0c;获取对应的 HTML 页面。解析 HTML 页面&#xff1a;使用 HTML 解析器对获取的 HTML 页面…