【Spring专题】Spring之Bean的生命周期源码解析——阶段二(IOC之实例化)
目录
- 前言
- 阅读准备
- 阅读指引
- 阅读建议
- 课程内容
- 一、SpringIOC之实例化
- 1.1 简单回顾
- 1.2 概念回顾
- 1.3 核心方法讲解
- 二、方法讲解
- 2.1 AbstractBeanFactory#getMergedLocalBeanDefinition:合并BeanDefinition
- 2.2 AbstractAutowireCapableBeanFactory#createBean:创建Bean
- 2.3 AbstractAutowireCapableBeanFactory#resolveBeanClass:加载类
- *2.4 AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:实例化前
- 2.5 AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation:【实例化前】真正干活的地方
- 2.6 AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization:第一次可能调用【初始化后】
- 2.7 AbstractAutowireCapableBeanFactory#doCreateBean:开始实例化(包括后续的实例化过程)
- *2.8 AbstractAutowireCapableBeanFactory#createBeanInstance:实例化
- 2.8.1 Supplier创建对象
- 2.8.2 工厂方法创建对象
- 2.9 AbstractAutowireCapableBeanFactory#autowireConstructor:推断构造方法
- 2.10 AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors:BeanDefinition后置处理
- 2.11 AbstractAutowireCapableBeanFactory#populateBean:属性注入(包含:实例化后)
- 方法总结后
- 三、实例化逻辑流程图
- 学习总结
前言
阅读准备
由于Spring源码分析是一个前后联系比较强的过程,而且这边分析,也是按照代码顺序讲解的,所以不了解前置知识的情况下,大概率没办法看懂当前的内容。所以,特别推荐看看我前面的文章(自上而下次序):
- Spring底层核心原理解析【学习难度:★★☆☆☆】
- 手写简易Spring容器过程分析【学习难度:★★☆☆☆】
- Spring之底层架构核心概念解析【学习难度:★★★☆☆,重要程度:★★★★★】
- Bean的生命周期流程图【学习难度:☆☆☆☆☆,重要程度:★★★★★】
- Spring之Bean的生命周期源码解析——阶段一(扫描生成BeanDefinition)
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
阅读指引
我们在上一节课已经说到过了,本次Spring源码剖析的总入口是new AnnotationConfigApplicationContext("org.tuling.spring");,这里就不再重复解释了。本节课要说的内容,是SpringIOC的实例化,我们这里直接给到入口吧,调用链如下:(调用链比较深,不要纠结细枝末节)
- AbstractApplicationContext#refresh:刷新方法,不用在意
- AbstractApplicationContext#finishBeanFactoryInitialization:在这里实例化所有剩余的(非lazy-init)单例
- DefaultListableBeanFactory#preInstantiateSingletons:在这里实例化所有剩余的(非lazy-init)单例(上面的方法,核心干活的方法就是这里)
- DefaultListableBeanFactory#getBean:获取Bean的方法
- AbstractBeanFactory#doGetBean:返回指定bean的一个实例,它可以是共享的,也可以是独立的
- 上面这个
AbstractBeanFactory#doGetBean里面的一段局部代码写的回调方法,如下:
// 如果是单例创建bean实例if (mbd.isSingleton()) {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;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
- AbstractAutowireCapableBeanFactory#createBean:这个类的中心方法:创建一个bean实例,填充bean实例,应用后处理器,等等。
如上面的调用链所示,最后一个方法,才是我们本次要研究的核心方法。而且通过注释,我想大家也看到了,这个方法不单单干了实例化的工作,还有属性填充、各种后置处理器等。(PS:哈哈,同学们,我知道这个【实例化】调用链挺深的,但是大家不要烦恼,只要切记【不要纠结那些细枝末节】那一切都OK,我们老老实实地跟着主线来研究就好,毕竟这些才是核心)
阅读建议
- 看源码,切记纠结细枝末节,不然很容易陷进去。正常来说,看主要流程就好了
- 遇到不懂的,多看看类注释或者方法注释。Spring这种优秀源码,注释真的非常到位
- 如果你是idea用户,多用F11的书签功能。
- Ctrl + F11 选中文件 / 文件夹,使用助记符设定 / 取消书签 (必备)
- Shift + F11 弹出书签显示层 (必备)
- Ctrl +1,2,3…9 定位到对应数值的书签位置 (必备)
课程内容
一、SpringIOC之实例化
这里说的【实例化】,是指单例的实例化,原型prototype不包含在内
1.1 简单回顾
大家知道,实例化的过程是怎样的吗?哈,我知道大部分的同学可能都不会知道。所以呢,我希望大家真的要有去看过《 Bean的声明周期流程图》,因为,通过【代码去理解业务】,远远比【通过业务理解代码】难得多!直接看个图吧,起码咱得知道【实例化到底干了什么,有哪些步骤】,我们才能更好的去研究。

如上图所示,实例化包含了:合并BeanDefinition、加载类、实例化之前、推断构造方法、实例化、BeanDefinition的后置处理、实例化后等,这些关键步骤。这就是我们本篇文章研究的核心!!
1.2 概念回顾
在这个【实例化】过程中,涉及到了一些Spring底层设计的概念,我在上一个笔记里面有大概介绍过Spring底层概念的一些讲解,不记得的同学记得回去翻一翻。
主要涉及的概念有:
- BeanDefinition(设计图纸):BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特征
- BeanPostProcessor(Spring重要的拓展点):Bean的后置处理器,对Bean做拓展操作。我们前面说过,
BeanPostProcessor提供了【初始化前/后】两个拓展方法。但是这里,Spring内部对这个接口做了很多拓展,新增了一些继承自BeanPostProcessor的子类或者子接口。在实例化阶段,主要用到的接口如下:InstantiationAwareBeanPostProcessor,直译过来就是:能感知实例化的Bean后置处理器。而这个继承自BeanPostProcessor,显然也具备这【初始化前/后】两个拓展方法。另外,通过InstantiationAwareBeanPostProcessor的名字大家也能猜到了,它肯定是具有对【实例化】阶段拓展的能力的。接口定义在后面。SmartInstantiationAwareBeanPostProcessor:直译过来就是:智能的,能感知实例化的Bean后置处理器。在这个接口中,拓展了InstantiationAwareBeanPostProcessor接口,新增了几个函数,其中就包括了推断构造的实现。另外,这是一个框架内部使用接口。接口定义在后面。MergedBeanDefinitionPostProcessor:直译过来就是:合并BeanDefinition的后置处理器。运行时用于合并bean定义的后处理器回调接口。接口定义在后面。
InstantiationAwareBeanPostProcessor接口定义如下:
/*** BeanPostProcessor的子接口,用于添加实例化前回调,以及实例化后但显式属性设置或自动生成之前的回调。* 通常用于抑制特定目标bean的默认实例化,例如创建具有特殊TargetSources的代理(池化目标、惰性初始化目标等),或者实现额外的注入策略,如字段注入。* 注:此接口为专用接口,主要供框架内部使用。建议尽可能实现普通的BeanPostProcessor接口。* 自:* 1.2* 参见:* org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.setCustomTargetSourceCreators, org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator* 作者:* 于尔根·霍勒,罗德·约翰逊*/
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {/*** 在目标bean实例化之前应用这个BeanPostProcessor。返回的bean对象可能是要使用的代理而不是目标bean,从而有效地抑制目标bean的默认实例化。* 如果此方法返回一个非空对象,则bean创建过程将会中断。应用的唯一进一步处理是来自配置的BeanPostProcessors的postProcessAfterInitialization回调。* 这个回调将应用于带有bean类的bean定义,以及工厂方法定义,在这种情况下,返回的bean类型将在这里传递。* 后置处理器可以实现扩展的SmartInstantiationAwareBeanPostProcessor接口,以便预测它们将在这里返回的bean对象的类型。* 默认实现返回null。* 参数:* beanClass——要实例化的bean的类* beanName—bean的名称* 返回:* 要公开的bean对象,而不是目标bean的默认实例,或者为空,继续进行默认实例化* 抛出:* BeansException -在错误的情况下* 参见:* postProcessAfterInstantiation, org.springframework.beans.factory.support.AbstractBeanDefinition.getBeanClass(), org.springframework.beans.factory.support.AbstractBeanDefinition.getFactoryMethodName()*/@Nullabledefault Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {return null;}/*** 在bean通过构造函数或工厂方法实例化之后,但在Spring属性填充(来自显式属性或自动装配)发生之前执行操作。* 这是在Spring自动装配开始之前对给定bean实例执行自定义字段注入的理想回调。* 默认实现返回true。* 参数:* Bean—已创建的Bean实例,其属性尚未设置* beanName—bean的名称* 返回:* 如果应该在bean上设置属性,则为True;如果应该跳过属性人口,则为False。正常的实现应该返回true。返回false还将阻止在此bean实例上调用任何后续的InstantiationAwareBeanPostProcessor实例。* 抛出:* BeansException -在错误的情况下* 参见:* postProcessBeforeInstantiation*/default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {return true;}/*** 在工厂将给定的属性值应用到给定的bean之前,对它们进行后处理,不需要任何属性描述符。* 如果实现提供自定义postProcessPropertyValues实现,则应该返回null(默认值),否则则返回pvs。在该接口的未来版本中(删除了postProcessPropertyValues),默认实现将直接返回给定的pv。* 参数:* PVS—工厂将要应用的属性值(永远不会为空)* bean——已创建的bean实例,但其属性尚未设置* beanName—bean的名称* 返回:* 应用于给定bean的实际属性值(可以是传入的PropertyValues实例),或者null,它继续处理现有属性,但特别地继续调用postProcessPropertyValues(需要为当前bean类初始化PropertyDescriptors)。* 抛出:* BeansException -在错误的情况下* 自:* 5.1* 参见:* postProcessPropertyValues*/@Nullabledefault PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)throws BeansException {return null;}/*** 在工厂将给定属性值应用到给定bean之前,对它们进行后处理。允许检查是否满足所有依赖项,例如基于bean属性设置器上的“Required”注释。* 还允许替换要应用的属性值,通常通过基于原始PropertyValues创建新的MutablePropertyValues实例,添加或删除特定值。* 默认实现按原样返回给定的pv。* 弃用* 从5.1开始,支持postProcessProperties(PropertyValues, Object, String)* 参数:* PVS—工厂将要应用的属性值(永远不会为空)* PDS——目标bean的相关属性描述符(忽略了依赖类型——工厂专门处理的依赖类型——已经过滤掉了)* bean——已创建的bean实例,但其属性尚未设置* beanName—bean的名称* 返回:* 应用于给定bean的实际属性值(可以是传入的PropertyValues实例),或者为null以跳过属性填充* 抛出:* BeansException -在错误的情况下* 参见:* postProcessProperties, org.springframework.beans.MutablePropertyValues*/@Deprecated@Nullabledefault PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {return pvs;}
}
SmartInstantiationAwareBeanPostProcessor接口定义如下:
/*** 扩展了InstantiationAwareBeanPostProcessor接口,添加了一个回调函数,用于预测被处理bean的最终类型。* 注:此接口为专用接口,主要供框架内部使用。一般来说,应用程序提供的后处理器应该简单地实现普通的BeanPostProcessor接口,或者从InstantiationAwareBeanPostProcessorAdapter类派生。即使在点发布版本中,也可能向该接口添加新方法。* 自:* 2.0.3* 参见:* InstantiationAwareBeanPostProcessorAdapter* 作者:* Juergen hoel*/
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {/*** 预测这个处理器的postProcessBeforeInstantiation回调最终返回的bean的类型。* 默认实现返回null。* 参数:* beanClass—bean的原始类beanName—bean的名称* 返回:* bean的类型,如果不可预测,则为空* 抛出:* BeansException -在错误的情况下*/@Nullabledefault Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {return null;}/*** 确定要为给定bean使用的候选构造函数。* 默认实现返回null。* 参数:* beanClass—bean的原始类(不为空)beanName—bean的名称* 返回:* 候选构造函数,如果未指定,则为空* 抛出:* BeansException -在错误的情况下*/@Nullabledefault Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)throws BeansException {return null;}/*** 获取对指定bean的早期访问的引用,通常是为了解析循环引用。* 这个回调让后处理器有机会尽早公开包装器——也就是说,在目标bean实例完全初始化之前。暴露的对象应该等同于postProcessBeforeInitialization / postProcessAfterInitialization所暴露的对象。请注意,此方法返回的对象将用作bean引用,除非后处理器返回与所述后处理回调不同的包装器。换句话说:那些处理后回调可能最终公开相同的引用,或者返回那些后续回调的原始bean实例(如果受影响的bean的包装器已经为对该方法的调用构建了,那么默认情况下它将作为最终bean引用公开)。* 默认实现按原样返回给定的bean。* 参数:* bean—原始bean实例beanName—bean的名称* 返回:* 要作为bean引用公开的对象(通常将传入的bean实例作为默认值)* 抛出:* BeansException -在错误的情况下*/default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {return bean;}
}
MergedBeanDefinitionPostProcessor接口定义如下:
/*** 运行时用于合并bean定义的后处理器回调接口。BeanPostProcessor实现可以实现这个子接口,以便对合并的bean定义(原始bean定义的处理副本)进行后处理,Spring BeanFactory使用合并的bean定义来创建bean实例。* 例如,postProcessMergedBeanDefinition方法可以对bean定义进行内省,以便在对bean的实际实例进行后处理之前准备一些缓存的元数据。也允许修改bean定义,但只允许修改用于并发修改的定义属性。本质上,这只适用于在RootBeanDefinition本身上定义的操作,而不适用于其基类的属性。* 自:* 2.5* 参见:* org.springframework.beans.factory.config.ConfigurableBeanFactory.getMergedBeanDefinition* 作者:* Juergen hoel*/
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {/*** 对指定bean的给定合并bean定义进行后处理。* 参数:* beanDefinition—为bean合并的bean定义beanType—托管bean实例的实际类型beanName—bean的名称* 参见:* AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors*/void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);/*** 通知指定名称的bean定义已被重置,并且此后处理器应清除受影响bean的所有元数据。* 默认实现为空。* 参数:* beanName—bean的名称* 自:* 5.1* 参见:* DefaultListableBeanFactory.resetBeanDefinition*/default void resetBeanDefinition(String beanName) {}
}
这里还是有一点想要提醒各位的,BeanPostProcessor是被Spring定义为【钩子】的,而钩子,很多时候都是为了改变系统原有行为的。这种改变,通常体现在:对Bean生命周期的影响,或者某个生命周期完成对象的改变等等。(这么说有点晦涩难懂,通俗点讲就是:Bean可能不会经过完整生命周期,比如【实例化前】之后,直接就跳到【初始化后】了,没有经过原本定义的【实例化】、【实例化后】、【其他后置处理】等;或者,原本初始化动作是Spring帮我门完成的,但是由于我们自己介入了,Spring就不帮我们初始化了,而是按照程序员意愿)
文章链接:
《【Spring专题】Spring之底层架构核心概念解析》
1.3 核心方法讲解
整个IOC实例化的主干过程,主要涉及了【2个类,11个核心方法】。下面,我们将会按照调用次序,依次讲解各个方法。
二、方法讲解
我们在上面说到过,这里的实例化过程包含了:合并BeanDefinition、加载类、实例化之前、推断构造方法、实例化、BeanDefinition的后置处理、实例化后等关键步骤。所以,在整个调用链上,基本上就是干了这些事情。
我们前面说过,本次的实例化代码入口如下:
// 如果是单例创建bean实例if (mbd.isSingleton()) {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;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
2.1 AbstractBeanFactory#getMergedLocalBeanDefinition:合并BeanDefinition
方法调用链:AbstractBeanFactory#doGetBean里面调用的
全路径:org.springframework.beans.factory.support.AbstractBeanFactory#getMergedLocalBeanDefinition
方法注释:返回合并的RootBeanDefinition,如果指定的bean对应于子bean定义,则遍历父bean定义。
我们在上节课最后的地方提到过这个,这边就不重复讲了,但是,他是算在实例化过程里面的,嘻嘻
2.2 AbstractAutowireCapableBeanFactory#createBean:创建Bean
方法调用链:AbstractBeanFactory#doGetBean里面调用的
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
方法注释:这个类的中心方法:创建一个bean实例,填充bean实例,应用后处理器,等等。
源码如下:
@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// 步骤一:加载类Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// 不用管这个try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// 步骤二:实例化前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 {// 步骤三:创建BeanObject beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("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);}}
方法解读:上面代码老长一段,trt-catch占了一半,啊哈哈。 这里面的步骤就三个,下面也会讲到。
- resolveBeanClass:加载类
- resolveBeforeInstantiation:实例化前
- doCreateBean:真正创建Bean的地方
2.3 AbstractAutowireCapableBeanFactory#resolveBeanClass:加载类
PS:不算很重要,知道有这个步骤就好
方法调用链:由2.2的createBean调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanClass
方法注释:这个类的中心方法:创建一个bean实例,填充bean实例,应用后处理器,等等。
源码如下:
protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch)throws CannotLoadBeanClassException {if (mbd.hasBeanClass()) {return mbd.getBeanClass();}if (System.getSecurityManager() != null) {return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>)() -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());}else {return doResolveBeanClass(mbd, typesToMatch);}
}
方法解读:这个代码外层挺简单的,也许大家对比System.getSecurityManager() != null这个比较难理解而已。这是Spring内部的一个安全管理器,很多时候都是不开启的。后面也会出现大量这种代码,反正遇到这个就直接看else的逻辑就好了。至于里层的doResolveBeanClass源码如下:
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)throws ClassNotFoundException {// 步骤一:获取默认的类加载器ClassLoader beanClassLoader = getBeanClassLoader();ClassLoader dynamicLoader = beanClassLoader;boolean freshResolve = false;// 步骤二:在我们这个调用链上不会走这里,因为typesToMatch为nullif (!ObjectUtils.isEmpty(typesToMatch)) {ClassLoader tempClassLoader = getTempClassLoader();if (tempClassLoader != null) {dynamicLoader = tempClassLoader;freshResolve = true;if (tempClassLoader instanceof DecoratingClassLoader) {DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;for (Class<?> typeToMatch : typesToMatch) {dcl.excludeClass(typeToMatch.getName());}}}}// 步骤三String className = mbd.getBeanClassName();if (className != null) {// 步骤3.1:这里也不用怎么看,这是Spring表达式解析的东西。Spring表达式这个东西我们用的很少(类似EL表达式,这样来理解)Object evaluated = evaluateBeanDefinitionString(className, mbd);if (!className.equals(evaluated)) {if (evaluated instanceof Class) {return (Class<?>) evaluated;}else if (evaluated instanceof String) {className = (String) evaluated;freshResolve = true;}else {throw new IllegalStateException("Invalid class name expression result: " + evaluated);}}if (freshResolve) {if (dynamicLoader != null) {try {// 步骤3.2 加载类return dynamicLoader.loadClass(className);}catch (ClassNotFoundException ex) {if (logger.isTraceEnabled()) {logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);}}}return ClassUtils.forName(className, dynamicLoader);}}// 步骤四:正常来说走的是这里。使用默认的类加载器加载类return mbd.resolveBeanClass(beanClassLoader);}
方法解读:在这里,大体分为4个步骤。步骤一就是获取类加载器,往里追踪会发现,实际上调用的是ClassUtils.getDefaultClassLoader()方法。里面的代码挺简单的,获取原则如下:(需要一点JVM底子才懂,不了解也无所谓)
- 优先返回当前线程中的ClassLoader
- 线程中类加载器为null的情况下,返回ClassUtils类的类加载器
- 如果ClassUtils类的类加载器为空,那么则表示是Bootstrap类加载器加载的ClassUtils类,那么则返回系统类加载器
步骤二,就是第一个if,在我们这里的调用链上是不会进来这里的,因为typeToMatch是null;
步骤三,可看可不看,一般也不会进来这里。这是Spring表达式解析的东西。Spring表达式这个东西我估计大部分人都没用过吧…(类似EL表达式,这样来理解)
步骤四,正常来说会执行到这里,然后在这里进行类加载,并且通过反射获得class对象,代码如下所示:
public Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {String className = getBeanClassName();if (className == null) {return null;}Class<?> resolvedClass = ClassUtils.forName(className, classLoader);this.beanClass = resolvedClass;return resolvedClass;}
*2.4 AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:实例化前
(PS:终于来到这里了,这才是核心考点)
方法调用链:由2.2的createBean调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
方法注释:应用实例化前的后处理器,解析指定bean是否存在实例化前的快捷方式。
源码如下:
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;}
方法解读:代码短短的几行。首先通过hasInstantiationAwareBeanPostProcessors看看是否有InstantiationAwareBeanPostProcessors。有,则调用对应的【实例化前】方法,即applyBeanPostProcessorsBeforeInstantiation(这里才是真正干活的地方,后面介绍)。
这里有一个细节,那就是applyBeanPostProcessorsBeforeInstantiation如果返回的对象不为空,则直接调用applyBeanPostProcessorsAfterInitialization走bean的【初始化后】方法了。这说明Bean的生命周期被改变!!而且,如果这里的Bean有值的话,外层直接返回创建成功了,不会继续往下走了,如下所示:(2.2的createBean调用处代码)
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {return bean;
}
2.5 AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation:【实例化前】真正干活的地方
方法调用链:由2.4的resolveBeforeInstantiation调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
方法注释:将InstantiationAwareBeanPostProcessors应用到指定的bean定义(通过类和名称),调用它们的postProcessBeforeInstantiation方法。如果返回不为空,则无需Spring帮我们实例化了
源码如下:
@Nullableprotected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}return null;}
方法解读:这里终于出现我们之前说的核心InstantiationAwareBeanPostProcessor了。老样子,遍历所有的InstantiationAwareBeanPostProcessor,然后调用postProcessBeforeInstantiation()。这里只要找到一个InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation()返回不为空,则直接停止遍历。
这个方法其实隐含的意思是:在Spring帮我们实例化之前,Spring会先去找找看,用户有没有对当前beanName对应的class有自己的实例化想法,如果有,则Spring就不帮我们创建了。
2.6 AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization:第一次可能调用【初始化后】
方法调用链:由2.4的resolveBeforeInstantiation调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
方法注释:应用初始化后方法。通常调用此方法的时候,会认为已经经过了属性填充、初始化等
源码如下:
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;}
方法解读:看,这里直接获取所有BeanPostProcessor 接口,然后应用postProcessAfterInitialization()方法。这里有个空判断,是接口要求这么做的。返回null表示不想继续执行剩余的BeanPostProcessor 接口
2.7 AbstractAutowireCapableBeanFactory#doCreateBean:开始实例化(包括后续的实例化过程)
方法调用链:由2.2的createBean调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
方法注释:AbstractAutowireCapableBeanFactory这个类的中心方法:创建一个bean实例,填充bean实例,应用后处理器,等等
源码很长,就不截取了。在这里个方法里面,主要干了4件事情:
- 实例化(推断构造方法、实例化)
- BeanDefinition的后置处理
- 属性注入(实例化后、属性注入)(对的,你没看错,【实例化后】是在属性注入这个方法里面被调用的)
- 初始化
方法解读:正如方法注释说的,这里【创建一个bean实例,填充bean实例,应用后处理器】等等。这意味着,在这里包含了【实例化】过程中剩余的==【推断构造方法、实例化、BeanDefinition的后置处理、实例化后等】==步骤。另外,下一个章节会说的【IOC属性填充】、【IOC-Aware回调】、【IOC初始化】等阶段入口其实也是在这个方法中。
*2.8 AbstractAutowireCapableBeanFactory#createBeanInstance:实例化
方法调用链:由2.7的doCreateBean调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
方法注释:AbstractAutowireCapableBeanFactory这个类的中心方法:创建一个bean实例,填充bean实例,应用后处理器,等等
源码如下:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 步骤一:再次确保类已经被加载Class<?> beanClass = resolveBeanClass(mbd, beanName);if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}// 步骤二:使用Supplier创建对象Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// 步骤三:工厂方法创建对象if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// Shortcut when re-creating the same bean...boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {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);}// Preferred constructors for default construction?ctors = mbd.getPreferredConstructors();if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, null);}// 步骤五:实例化return instantiateBean(beanName, mbd);}
方法解读:这里在实例化Bean的时候,采用了多种方式。如:
2.8.1 Supplier创建对象
Supplier,直译:供应商。
首先判断BeanDefinition中是否设置了Supplier,如果设置了则调用Supplier的get()得到对象。得直接使用BeanDefinition对象来设置Supplier,比如:
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setInstanceSupplier(new Supplier<Object>() {@Overridepublic Object get() {return new UserService();}
});
context.registerBeanDefinition("userService", beanDefinition);
2.8.2 工厂方法创建对象
如果没有设置Supplier,则检查BeanDefinition中是否设置了factoryMethod,也就是工厂方法,有两种方式可以设置factoryMethod,比如:
方式一:
<bean id="userService" class="com.zhouyu.service.UserService" factory-method="createUserService" />
对应的UserService类为:
public class UserService {public static UserService createUserService() {System.out.println("执行createUserService()");UserService userService = new UserService();return userService;}public void test() {System.out.println("test");}}
方式二:
<bean id="commonService" class="com.zhouyu.service.CommonService"/>
<bean id="userService1" factory-bean="commonService" factory-method="createUserService" />
对应的CommonService的类为:
public class CommonService {public UserService createUserService() {return new UserService();}
}
Spring发现当前BeanDefinition方法设置了工厂方法后,就会区分这两种方式,然后调用工厂方法得到对象。
值得注意的是,我们通过@Bean所定义的BeanDefinition,是存在factoryMethod和factoryBean的,也就是和上面的方式二非常类似,@Bean所注解的方法就是factoryMethod,AppConfig对象就是factoryBean。如果@Bean所所注解的方法是static的,那么对应的就是方式一。
2.9 AbstractAutowireCapableBeanFactory#autowireConstructor:推断构造方法
方法调用链:由2.7的createBeanInstance调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireConstructor
方法注释:推断构造方法,并且创建对象。
代码好长,我就不截取了。这里的代码看不看都还好,主要还是理解流程跟原则就好。
- 如果一个类只有一个构造方法,不管这个构造方法是有参还是无参,Spring都会使用这个构造方法创建对象
- 如果有多个构造方法,则看有没有无参构造方法。因为无参构造方法有默认的含义,若有,则选择无参的构造方法创建对象;
- 如果有多个构造方法,且没有无参,则看构造方法上是否有@Autowired注解,有就选择,没有就报错
额外的,在推断构造方法逻辑中除开会去选择构造方法以及查找入参对象意外,会还判断是否在对应的类中是否存在使用**@Lookup注解**了方法。如果存在则把该方法封装为LookupOverride对象并添加到BeanDefinition中。
在实例化时,如果判断出来当前BeanDefinition中没有LookupOverride,那就直接用构造方法反射得到一个实例对象。如果存在LookupOverride对象,也就是类中存在@Lookup注解了的方法,那就会生成一个代理对象。
@Lookup注解就是方法注入,使用demo如下:
@Component
public class UserService {private OrderService orderService;public void test() {OrderService orderService = createOrderService();System.out.println(orderService);}@Lookup("orderService")public OrderService createOrderService() {return null;}}
2.10 AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors:BeanDefinition后置处理
方法调用链:由2.7的doCreateBean调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
方法注释:将MergedBeanDefinitionPostProcessors应用于指定的bean定义,调用它们的postProcessMergedBeanDefinition方法。
源码如下:
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);}}
方法解读:Bean对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring又提供了一个扩展点,即当前的MergedBeanDefinitionPostProcessors。这里的主要难点还是,这种类型的BeanPostProcessor的应用场景,可能才是大家比较关心的。那你们觉得,这里的主要应用场景应该是什么呢?其实,按照Spring的生命周期来说,他现在提供给你的拓展点,只能是【实例化】阶段后的生命周期了,毕竟过去已经发生的已经没办法改变。所以,我们可以在这里,干预【初始化】这个阶段,如下示例:
public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);Object newUser = context.getBean("user");System.out.println(newUser);
}// 声明bean
@Component
public class User {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public void myInit() {System.out.println("这是我自己指定的初始化方法");}
}// 声明一个MergedBeanDefinitionPostProcessor,然后改变了User这个Bean的初始化方法
@Component
public class MyBeanPostProcessor implements MergedBeanDefinitionPostProcessor {@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {if (beanName.equals("user")) {beanDefinition.setInitMethodName("myInit");}}
}
PS:在Spring源码中,AutowiredAnnotationBeanPostProcessor就是一个MergedBeanDefinitionPostProcessor,它的postProcessMergedBeanDefinition()中会去查找注入点,并缓存在AutowiredAnnotationBeanPostProcessor对象的一个Map中(injectionMetadataCache),为依赖注入做准备。
2.11 AbstractAutowireCapableBeanFactory#populateBean:属性注入(包含:实例化后)
(PS:属性注入不是我们这里需要关注的地方,但因为【实例化后】在这个方法里面,所以就只能点进来了)
方法调用链:由2.7的doCreateBean调用过来
全路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
方法注释:使用来自bean定义的属性值在给定的BeanWrapper中填充bean实例(属性填充/依赖注入)。
populateBean中关键源码如下:
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}
方法解读:在处理完BeanDefinition后,Spring又设计了一个扩展点:InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation(),比如:
@Component
public class ZhouyuInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if ("userService".equals(beanName)) {UserService userService = (UserService) bean;userService.test();}return true;}
}
上述代码就是对userService所实例化出来的对象进行处理。这个扩展点,在Spring源码中基本没有怎么使用。估计是为了后面拓展做准备
方法总结后
以后可能不这么写了,挺麻烦的。我这样圈关键方法跟链路的原因,纯粹是想加深自己的印象,我也不知道放出来给大伙看,你们能否学习到什么。
三、实例化逻辑流程图
学习总结
相关文章:
【Spring专题】Spring之Bean的生命周期源码解析——阶段二(IOC之实例化)
目录 前言阅读准备阅读指引阅读建议 课程内容一、SpringIOC之实例化1.1 简单回顾1.2 概念回顾1.3 核心方法讲解 二、方法讲解2.1 AbstractBeanFactory#getMergedLocalBeanDefinition:合并BeanDefinition2.2 AbstractAutowireCapableBeanFactory#createBeanÿ…...
YOLOv8目标检测算法
YOLOv8目标检测算法相较于前几代YOLO系列算法具有如下的几点优势: 更友好的安装/运行方式速度更快、准确率更高新的backbone,将YOLOv5中的C3更换为C2FYOLO系列第一次尝试使用anchor-free新的损失函数 YOLOv8简介 YOLOv8 是 Ultralytics 公司继 YOLOv5…...
uniapp条件编译
uniapp条件编译 uni-app 将已经将可以跨平台兼容处理的组件及 API 等进行了封装,但是部分平台的特性无法跨平台。 由此,uni-app 提供了条件编译的方案,来处理不同平台的特定或差异。 写法 #ifdef: 仅在某平台存在#ifndef…...
2023年国赛数学建模思路 - 复盘:光照强度计算的优化模型
文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 建模资料 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米,宽为12米&…...
volte端到端问题分析(一)
1、MME专载保持功能验证 **描述:**当无线环境较差时,有可能由于“Radio_Connection_with_UE_Lost” 原因造成的VoLTE通话掉话,如果UE发生RRC重建成功,手机将不会掉话。 对MME1202进行功能验证:开启后,MME专…...
微信小程序(原生)搜索功能实现
一、效果图 二、代码 wxml <van-searchvalue"{{ keyword }}"shape"round"background"#000"placeholder"请输入关键词"use-action-slotbind:change"onChange"bind:search"onSearch"bind:clear"onClear&q…...
Android AOSP源码编译——AOSP整编(二)
切换到源码目录下执行下面命令 1、初始化环境 . build/envsetup.sh //清除缓存 make clobber2、选择编译目标 lunchAOSP 预制了很多 Product。这里为了简单我们先不用真机,而是选择模拟器的方式,对于 x86_64 模拟器,我们选择的是 aosp_x86…...
铁是地球科学争论的核心
一项新的研究调查了地球内部铁的形态。这些发现对理解内核的结构产生了影响。 一项新的研究探索了地球内核的铁结构,如图中的黄色和白色所示。 资料来源:地球物理研究快报 地球内核以铁为主,铁可以多种晶体形式作为固体材料存在。(…...
TX Text Control .NET Server for ASP.NET Crack
TX Text Control .NET Server for ASP.NET Crack TX Text Control.NET Server for ASP.NET是用于Web应用程序或服务的服务器端组件。它是一个完全可编程的ASP.NET文字处理引擎,提供了广泛的文字处理功能。使用TX Text Control.NET Server,程序员可以开发…...
工作纪实36-ES跨集群迁移
1.es数据备份、恢复 https://blog.csdn.net/andy_only/article/details/111319175 2.reindex命令 https://codeleading.com/article/40964498185/ 添加配置、重启ES cd bin sh elasticsearch -d3.开源工具 https://github.com/elasticsearch-dump/elasticsearch-dump 4.…...
【MFC】11.MFC文档和单文档架构-笔记
MFC文档 之前我们在写字符雨的时候,将数据都存储到了视图类中,这是不合理的,视图类只负责显示,不应该存储任何数据 文档:专门存储数据用的 CDocument 文档与视图的关系: 创建一个文档类 单文档四个类都…...
2023年“研究生科研素养提升”系列公益讲座在线测评题目与参考答案
一、单选题 1、关于参考文献的选择,说法错误的是 ( ) 参考文献的选择有原创性、必要性的原则 不能过度引用 不能故意隐藏引用来源 可以引用无关参考文献 您的答案:D 参考答案:D 答案解析:不可以引用无关参考…...
MySQL8.xx一主两从复制安装与配置
搭建环境: 查看系统版本cat /etc/redhat-release [rootwww tools]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) 查看内核版本cat /proc/version 目标: 一主两从 主机IP 主机名称 端口 搭建环境 安装目录192.168.1.100 docker…...
深度学习优化器
1、什么是优化器 优化器用来寻找模型的最优解。 2、常见优化器 2.1. 批量梯度下降法BGD(Batch Gradient Descent) 2.1.1、BGD表示 BGD 采用整个训练集的数据来计算 cost function 对参数的梯度: 假设要学习训练的模型参数为W,代价函数为J(W),…...
由浅入深C系列五:使用libcurl进行基于http get/post模式的C语言交互应用开发
使用libcurl进行基于http get/post模式的C语言交互应用开发 简介环境准备在线资源示例代码测试调用运行结果 简介 大多数在linux下的开发者,都会用到curl这个命令行工具。对于进行restful api的测试等,非常方便。其实,这个工具还提供了一个C…...
高效实用小工具之Everything
一,简介 有时候我们电脑文件较多时,想快速找到某个文件不是一件容易的事情,实用windows自带的搜素太耗时,效率不高。今天推荐一个用来搜索电脑文件的小工具——Everything,本文将介绍如何安装以及使用everything&…...
【Unity每日一记】关于物体(敌方)检测—(向量点乘相关)
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:uni…...
Elasticsearch-查询
一、查询和过滤 1.1 相关性分数 :_score 默认情况下,Elasticsearch 按相关性得分对匹配的搜索结果进行排序,相关性得分衡量每个文档与查询的匹配程度。 相关性分数是一个正浮点数,在搜索的数据字段中返回。_score越高࿰…...
首发 | FOSS分布式全闪对象存储系统白皮书
一、 产品概述 1. 当前存储的挑战 随着云计算、物联网、5G、大数据、人工智能等新技术的飞速发展,数据呈现爆发式增长,预计到2025年中国数据量将增长到48.6ZB,超过80%为非结构化数据。 同时,数字经济正在成为我国经济发展的新…...
Java反射获取所有Controller和RestController类的方法
Java反射获取所有Controller和RestController类的方法 引入三方反射工具Reflections <dependency><groupId>org.reflections</groupId><artifactId>reflections</artifactId><version>0.10.2</version> </dependency>利用反…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
