【Spring专题】Spring之Bean的生命周期源码解析——阶段一(扫描生成BeanDefinition)
目录
- 前言
- 阅读准备
- 阅读指引
- 阅读建议
- 课程内容
- 一、生成BeanDefinition
- 1.1 简单回顾
- *1.2 概念回顾
- 1.3 核心方法讲解
- 二、方法讲解
- 2.1 ClassPathBeanDefinitionScanner#scan
- 2.2 ClassPathBeanDefinitionScanner#doScan
- 2.3 ClassPathScanningCandidateComponentProvider#findCandidateComponents
- 2.4 ClassPathScanningCandidateComponentProvider#scanCandidateComponents
- 2.5 ClassPathScanningCandidateComponentProvider#isCandidateComponent
- 2.6 ClassPathScanningCandidateComponentProvider#isCandidateComponent
- 2.7 ClassPathBeanDefinitionScanner#postProcessBeanDefinition
- 2.8 AnnotationConfigUtils.processCommonDefinitionAnnotations
- 2.9 ClassPathBeanDefinitionScanner#checkCandidate
- 2.10 DefaultListableBeanFactory#registerBeanDefinition
- 三、扫描逻辑流程图
- 四、合并BeanDefinition
- 学习总结
前言
阅读准备
由于Spring源码分析是一个前后联系比较强的过程,而且这边分析,也是按照代码顺序讲解的,所以不了解前置知识的情况下,大概率没办法看懂当前的内容。所以,特别推荐看看我前面的文章(自上而下次序):
- Spring底层核心原理解析——引导篇【学习难度:★★☆☆☆】
- 手写简易Spring容器过程分析——引导篇【学习难度:★★☆☆☆】
- Spring之底层架构核心概念解析【学习难度:★★★☆☆,重要程度:★★★★★】
- Bean的生命周期流程图【学习难度:☆☆☆☆☆,重要程度:★★★★★】
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
阅读指引
Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象做准备,所以我们先明白Spring到底是怎么去创建Bean的,也就是先弄明白Bean的生命周期。
Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的。
本节课的内容,将会以下面这段代码为入口讲解:
public class MyApplicationTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("org.tuling.spring");Object newUser = context.getBean("newUser");System.out.println(newUser);}
}
主要用到的是如下Spring容器的构造方法:
/*** 创建一个新的AnnotationConfigApplicationContext,扫描给定包中的组件,为这些组件注册bean定义,并自动刷新上下文。* 参数:* baseppackages——要扫描组件类的包*/
public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh();
}
阅读建议
- 看源码,切记纠结细枝末节,不然很容易陷进去。正常来说,看主要流程就好了
- 遇到不懂的,多看看类注释或者方法注释。Spring这种优秀源码,注释真的非常到位
- 如果你是idea用户,多用F11的书签功能。
- Ctrl + F11 选中文件 / 文件夹,使用助记符设定 / 取消书签 (必备)
- Shift + F11 弹出书签显示层 (必备)
- Ctrl +1,2,3…9 定位到对应数值的书签位置 (必备)
课程内容
一、生成BeanDefinition
1.1 简单回顾
如果大家看过我前面的文章,应该会知道,BeanDefiniton
的生成,是在生产Bean之前的【扫描】阶段。如下图所示:
不过咱也说过,这只是简单实现而已,事实上,Spring实现这个扫描过程,涉及到了3个核心类,10个核心方法!
*1.2 概念回顾
在这个【扫描】过程中,涉及到了一些Spring底层设计的概念,我在上一个笔记里面有大概介绍过,不记得的同学记得回去翻一翻。
主要涉及的概念有:
- BeanDefinition(设计图纸):BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特征
- ClassPathBeanDefinitionScanner(图纸注册器):用于注册图纸
- BeanFacotory(Bean工厂):生产Bean。不过实际上,这里用到
DefaultListableBeanFactory
这个类的BeanDefinitionRegistry
接口的注册能力
文章链接:
《【Spring专题】Spring之底层架构核心概念解析》
1.3 核心方法讲解
我们在前面提到过,整个扫描流程会涉及到【3个核心类,10个核心方法】。下面,我们将会按照调用次序,依次讲解各个方法。
二、方法讲解
整个扫描流程的入口,是下面代码中的scan()
方法,而最终调用的是ClassPathBeanDefinitionScanner.scan()
/*** 创建一个新的AnnotationConfigApplicationContext,扫描给定包中的组件,为这些组件注册bean定义,并自动刷新上下文。* 参数:* baseppackages——要扫描组件类的包*/
public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh();
}public void scan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan").tag("packages", () -> Arrays.toString(basePackages));this.scanner.scan(basePackages);scanPackages.end();
}
2.1 ClassPathBeanDefinitionScanner#scan
全路径:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan
方法注释:在指定的基本包中执行扫描
源码如下:
public int scan(String... basePackages) {int beanCountAtScanStart = this.registry.getBeanDefinitionCount();doScan(basePackages);// Register annotation config processors, if necessary.if (this.includeAnnotationConfig) {AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);}
但其实真正干活的还不是这里,而是里面的doScan()
2.2 ClassPathBeanDefinitionScanner#doScan
方法调用链:由 2.1中的scan()调用进来
全路径:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
方法注释:在指定的基包中执行扫描,返回已注册的bean定义。此方法不注册注释配置处理程序,而是将此工作留给调用者。
源码如下:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {// 寻找候选的BeanDefinitionSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);// 判断是否要设置BeanDefinition属性if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}// 判断是否要设置通用的注解BeanDefiniton属性if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 判断是否注册BeanDefinitionif (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}
方法解读:这个代码看似很多,其实整体来说比较简单,分为4个步骤
- 先扫描classpath下所有的class,寻找候选的BeanDefinition。至于什么是候选的,后面会讲解
- 遍历找到的候选BeanDefinition,判断是否需要设置BeanDefinition的一些属性。比如默认属性等
- 判断是否要设置通用的注解BeanDefiniton属性
- 判断是否要注册BeanDefinition,毕竟可能出现重复的BeanDefinition
2.3 ClassPathScanningCandidateComponentProvider#findCandidateComponents
方法调用链:由 2.2中的doScan()调用进来
全路径:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
方法注释:扫描类路径上的候选Component。
源码如下:
public Set<BeanDefinition> findCandidateComponents(String basePackage) {if (this.componentsIndex != null && indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {return scanCandidateComponents(basePackage);}}
方法解读:这里有一个if-else
,但通常都是进入else
。if
里面的情况我们在业务开发中基本不会用到,这是一个为了加快扫描包的策略,通过使用索引思想。怎么实现呢?就是在resource
下新建一个spring.components
配置文件(K-V格式)。如下:
org.tuling.spring.bean.User = org.springframework.stereotype.Component
通过直接告诉Spring哪些是候选的Component,免除了扫描所有class文件筛选的过程,从而提升了效率。
2.4 ClassPathScanningCandidateComponentProvider#scanCandidateComponents
方法调用链:由 2.3中的findCandidateComponents()调用进来
全路径:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
方法注释:扫描类路径上的候选Component。
源码如下:
private Set<org.springframework.beans.factory.config.BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}try {// 读取类元信息MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);// 第一层判断是否为候选组件,判断核心为:是否需要过滤if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);// 第二层判断是否为候选组件,判断核心为:是否独立、抽象类等if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (FileNotFoundException ex) {if (traceEnabled) {logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());}}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;}
方法解读:代码看着很长,实际上很简单。主要分为4个步骤
- 读取类元信息(从磁盘中读取)
- 首先,通过ResourcePatternResolver获得指定包路径下的所有.class文件(Spring源码中将此文件包装成了Resource对象)
- 遍历每个Resource对象
- 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader)
- 判断是否为候选组件,判断核心为:是否需要过滤(第一个
isCandidateComponent
)- 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
- 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
- 判断是否为候选组件,判断核心为:是否独立、抽象类等。(第二个
isCandidateComponent
)(注意:他跟上面的isCandidateComponent
不是同一个东西,属于方法重载) - 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集
MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有
- 获取类的名字、
- 获取父类的名字
- 获取所实现的所有接口名
- 获取所有内部类的名字
- 判断是不是抽象类
- 判断是不是接口
- 判断是不是一个注解
- 获取拥有某个注解的方法集合
- 获取类上添加的所有注解信息
- 获取类上添加的所有注解类型集合
2.5 ClassPathScanningCandidateComponentProvider#isCandidateComponent
方法调用链:由 2.4中的scanCandidateComponents()调用进来
全路径:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
方法注释:确定给定的类是否不匹配任何排除筛选器,而匹配至少一个包含筛选器。
源码如下:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}
方法解读:这里只是校验,当前类,是否在过滤器排除选项以内。我估计很多朋友想到的都是@ComponentScan
注解里面的includeFilter
跟excludeFilter
属性。只能说不完全正确。
因为,在我们启动容器创建ClassPathBeanDefinitionScanner
也会添加默认的过滤策略。如下所示:
/*** 注册@Component的默认过滤器。* 这将隐式注册所有带有@Component元注释的注释,包括@Repository、@Service和@Controller构造型注释。* 还支持Java EE 6的javax.annotation.ManagedBean和JSR-330的javax.inject.Named注释(如果可用)。*/
protected void registerDefaultFilters() {this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.}try {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}
}
2.6 ClassPathScanningCandidateComponentProvider#isCandidateComponent
方法调用链:由 2.4中的scanCandidateComponents()调用进来
全路径:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
方法注释:确定给定的bean定义是否符合候选条件。默认实现检查类是否不是接口,是否依赖于封闭类。
源码如下:
/***确定给定的bean定义是否符合候选条件。* 默认实现检查类是否不是接口,是否依赖于封闭类。* 可以在子类中重写。*/
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();return (metadata.isIndependent() && (metadata.isConcrete() ||(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
方法解读:这里有2个比较陌生的,那就是,什么是独立类,以及Lookup注解。
- 独立类,及不是普通内部类(静态内部类是独立类)
- Lookup注解,自己去百度使用方法
总结方法,判断是候选Component的条件是:
- 首先是独立类
- 要么是具体的子类,要么就是:有Lookup注解的抽象类
2.7 ClassPathBeanDefinitionScanner#postProcessBeanDefinition
方法调用链:由 2.2中的doScan()调用进来
全路径:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#postProcessBeanDefinition
方法注释:扫描类路径上的候选Component。
源码如下:
/*** 除了通过扫描组件类检索到的内容之外,还可以对给定的bean定义应用进一步的设置。*/
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {beanDefinition.applyDefaults(this.beanDefinitionDefaults);if (this.autowireCandidatePatterns != null) {beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));}}
方法解读:这里主要还初始化了BeanDefinition一些默认属性beanDefinition.applyDefaults(this.beanDefinitionDefaults);
源码如下:
public void applyDefaults(BeanDefinitionDefaults defaults) {Boolean lazyInit = defaults.getLazyInit();if (lazyInit != null) {setLazyInit(lazyInit);}setAutowireMode(defaults.getAutowireMode());setDependencyCheck(defaults.getDependencyCheck());setInitMethodName(defaults.getInitMethodName());setEnforceInitMethod(false);setDestroyMethodName(defaults.getDestroyMethodName());setEnforceDestroyMethod(false);}
2.8 AnnotationConfigUtils.processCommonDefinitionAnnotations
方法调用链:由 2.2中的doScan()调用进来
全路径:org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations
方法注释:根据注解累类元信息,设置BeanDefinition注解相关属性。
源码如下:
public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {processCommonDefinitionAnnotations(abd, abd.getMetadata());
}static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);if (lazy != null) {abd.setLazyInit(lazy.getBoolean("value"));}else if (abd.getMetadata() != metadata) {lazy = attributesFor(abd.getMetadata(), Lazy.class);if (lazy != null) {abd.setLazyInit(lazy.getBoolean("value"));}}if (metadata.isAnnotated(Primary.class.getName())) {abd.setPrimary(true);}AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);if (dependsOn != null) {abd.setDependsOn(dependsOn.getStringArray("value"));}AnnotationAttributes role = attributesFor(metadata, Role.class);if (role != null) {abd.setRole(role.getNumber("value").intValue());}AnnotationAttributes description = attributesFor(metadata, Description.class);if (description != null) {abd.setDescription(description.getString("value"));}}
没啥好解读的,很简单的判断
2.9 ClassPathBeanDefinitionScanner#checkCandidate
方法调用链:由 2.2中的doScan()调用进来
全路径:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate
方法注释:检查给定的候选bean名称,确定是否需要注册相应的bean定义,或者是否与现有定义冲突。
源码如下:
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {if (!this.registry.containsBeanDefinition(beanName)) {return true;}BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();if (originatingDef != null) {existingDef = originatingDef;}if (isCompatible(beanDefinition, existingDef)) {return false;}throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");}
方法解读:这里正常来说,要么返回true
,要么就是重复定义相同名字的Bean
然后抛出异常。直接返回false
,通常是存在父子容器的情况下。
2.10 DefaultListableBeanFactory#registerBeanDefinition
方法调用链:由 2.2中的doScan()调用进来
全路径:org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
方法注释:用这个注册中心注册一个新的bean定义。必须支持RootBeanDefinition和ChildBeanDefinition。
源码如下:
@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}else if (existingDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +existingDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}else {if (logger.isTraceEnabled()) {logger.trace("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}this.beanDefinitionMap.put(beanName, beanDefinition);}else {if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}}else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}else if (isConfigurationFrozen()) {clearByTypeCache();}}
方法解读:这个就稍微复杂一点了,我也没细看。而且,这个隐藏的挺深的,虽然是从doScan()
里面进调用的,但是本质上是调用AnnotationConfigApplicationContext
继承自GenericApplicationContext
的registerBeanDefinition
方法
三、扫描逻辑流程图
四、合并BeanDefinition
到了后面的IOC过程,生成Bean的时候,我们会看到很多这样的调用:
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
这个就是所谓的,获取合并BeanDefinition。什么是合并BeanDefinition?其实就是父子BeanDefinition。跟Java里面的继承是一样一样的。但是,这个通常发生在xml配置bean里面,如下:
<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child"/>
但是这么定义的情况下,child就是原型Bean了。因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的BeanDefinition。
学习总结
- 学习了Spring源码流程中的【扫描】底层原理
相关文章:

【Spring专题】Spring之Bean的生命周期源码解析——阶段一(扫描生成BeanDefinition)
目录 前言阅读准备阅读指引阅读建议 课程内容一、生成BeanDefinition1.1 简单回顾*1.2 概念回顾1.3 核心方法讲解 二、方法讲解2.1 ClassPathBeanDefinitionScanner#scan2.2 ClassPathBeanDefinitionScanner#doScan2.3 ClassPathScanningCandidateComponentProvider#findCandid…...

【C#】判断打印机共享状态
打印机共享状态 /// <summary>/// 打印机共享状态/// </summary>public enum PrinterShareState{/// <summary>/// 无打印机/// </summary>None -1,/// <summary>/// 未共享/// </summary>NotShare 0,/// <summary>/// 已共享/// …...

运维监控学习笔记7
Zabbix的安装: 1、基础环境准备: 安装zabbix的yum源,阿里的yum源提供了zabbix3.0。 rpm -ivh http://mirrors.aliyun.com/zabbix/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm 这个文件就是生成了一个zabbix.repo 2、安…...

【业务功能篇64】maven加速 配置settings.xml文件 镜像
maven加速 添加阿里镜像仓 <?xml version"1.0" encoding"UTF-8"?><!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additi…...

Spring Boot(六十四):SpringBoot集成Gzip压缩数据
1 实现思路 2 实现 2.1 创建springboot项目 2.2 编写一个接口,功能很简单就是传入一个Json对象并返回 package com.example.demo.controller;import com.example.demo.entity.Advertising; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframewo…...

Mac安装opencv后无法导入cv2的解决方法
前提条件:以下两个插件安装成功 pip install opencv-python pip install --user opencv-contrib-python 注:直接用pip install opencv-contrib-python如果报错,就加上“–user" 第一步: 设置–添加python解释器 第二步&am…...

【题解】按之字形顺序打印二叉树
按之字形顺序打印二叉树 题目链接:按之字形顺序打印二叉树 解题思路:层次遍历,借助队列 首先解决如何模仿之字形的问题,我们为此设置一个flag,每到一层就修改flag,如果flag为true(初始为fals…...

后端人员如何快速上手vue
一、环境搭建 了解更多vue-cli 官网地址:https://cli.vuejs.org/zh/guide/browser-compatibility.html 前提 1.安装node(js代码的运行环境)、npm、cnpm/yarn; nodejs官网:https://nodejs.org/en cnpm安装:https://www.python100.com/htm…...

基于Prometheus监控Kubernetes集群
目录 一、环境准备 1.1、主机初始化配置 1.2、部署docker环境 二、部署kubernetes集群 2.1、组件介绍 2.2、配置阿里云yum源 2.3、安装kubelet kubeadm kubectl 2.4、配置init-config.yaml 2.5、安装master节点 2.6、安装node节点 2.7、安装flannel、cni 2.8、部署测…...

【数据分析】pandas (三)
基本功能 在这里,我们将讨论pandas数据结构中常见的许多基本功能 让我们创建一些示例对象: index pd.date_range(“1/1/2000”, periods8) s pd.Series(np.random.randn(5), index[“a”, “b”, “c”, “d”, “e”]). df pd.DataFrame(np.random.…...

nvm命令
1. 常见命令 1. nvm -v //查看nvm版本 nvm --version :显示 nvm 版本 2. nvm list //显示版本列表 nvm list :显示已安装的版本(同 nvm list installednvm list installed:显示已安装的版本nvm list available:显示所有…...

从此已是义无反顾
距离上次发这个专栏的文章已经过去了十多天,现在我已经开始准备面试内容,迟迟还没有投出第一份简历,只是因为我感觉对知识点的理解还不到位,于是开始一边看JavaGuide老师总结的面试题目,一边翻看以前学习的笔记&#x…...

Element组件浅尝辄止2:Card卡片组件
根据官方说法: 将信息聚合在卡片容器中展示。 1.啥时候使用?When? 既然是信息聚合的容器,那场景就好说了 新建页面时可以用来当做页面容器页面的某一部分,可以用来当做子容器 2.怎样使用?How? //Card …...

“深入剖析Java多态:点燃编程世界火花“
White graces:个人主页 🙉专栏推荐:Java入门知识🙉 🙉 内容推荐:“继承与组合:代码复用的两种策略“🙉 🐹今日诗词:马踏祁连山河动,兵起玄黄奈何天🐹 快去学习 🌸思维导…...

golang官方限流器rate包实践
日常开发中,对于某些接口有请求频率的限制。比如登录的接口、发送短信的接口、秒杀商品的接口等等。 官方的golang.org/x/time/rate包中实现了令牌桶的算法。 封装限流器可以将ip、手机号这种的作为限流器组的标识。 接下来就是实例化限流器和获取令牌函数的实现…...

[windows]MAT- 下载及安装
1. 下载安装包 1.1MAT下载链接: https://pan.baidu.com/s/1sUWPITSto8MjOrcF0BsJQg?pwd1111 提取码:1111 1.2MAT需要jdk17版本及以上支持,下载链接: https://pan.baidu.com/s/111jz90S4tie_48lQeExcZg?pwd1111 提取码:1…...

数组模拟环形队列详解
数组模拟环形队列 实现逻辑 创建一个固定大小的数组作为队列的存储空间,同时定义队列的头部和尾部指针(front和rear)。初始时,将头部和尾部指针都设置为0,表示队列为空。入队操作(enqueue)&am…...

《论文阅读12》RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds
一、论文 研究领域:全监督3D语义分割(室内,室外RGB,kitti)论文:RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds CVPR 2020 牛津大学、中山大学、国防科技大学 论文链接论文gi…...

elementPlus使用el-icon
安装 # NPM $ npm install element-plus/icons-vue # Yarn $ yarn add element-plus/icons-vue # pnpm $ pnpm install element-plus/icons-vue一、main.ts(全局注册) import * as ElementIcons from element-plus/icons-vuefor (const key in Element…...

预测知识 | 神经网络、机器学习、深度学习
预测知识 | 预测技术流程及模型评价 目录 预测知识 | 预测技术流程及模型评价神经网络机器学习深度学习参考资料 神经网络 神经网络(neural network)是机器学习的一个重要分支,也是深度学习的核心算法。神经网络的名字和结构,源自…...

【Linux】进程的基本属性|父子进程关系
个人主页:🍝在肯德基吃麻辣烫 我的gitee:Linux仓库 个人专栏:Linux专栏 分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处 文章目录 前言进程属性1.进程PID和PPID2.fork函数创建子进程1)为什…...

CCF考试:201809-1 卖菜(java代码)
目录 1、【问题描述】 2、【思路分析】 3、【代码区】 1、【问题描述】 在一条街上有n个卖菜的商店,按1至n的顺序排成一排,这些商店都卖一种蔬菜。 第一天,每个商店都自己定了一个价格。店主们希望自己的菜价和其他商店的一致…...

android wifi扫描 framework层修改扫描间隔
frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java 这个也就是说前台应用可以在120s(2分钟) 扫描 4 次 * a) Each foreground app can request a max of* {link #SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS} scan every* {l…...

webstorm debug调试vue项目
1.运行npm,然后控制台会打印下图中的地址,复制local的地址 2.run–>Edit Configuration,如下图 3.设置测试项 4.在你需要的js段打好断点 5.在上边框的工具栏里面有debug运行,点击debug运行的图标运行即可...

嵌入式linux的八股文之旅 DAY1
1 三次握手 四次挥手 服务端 先从close到listen 然后第一个syn报文 客户端 生成初始序列号 client_isn (就是iternal sequence number 初始序列号) 然后放到TCP首部的序列号端里 然后把SYN标志位置一 然后发送给服务器端 之后处于SYN-SENT状态 服务器…...

同创永益郑阳|与数智化共舞·业务稳定性保障新动力
2023年8月2日,由北大创新评论主办的2023 Inno China中国产业创新大会-保险产业创新论坛在京举办。本次论坛由同创永益、青牛软件、DaoCloud道客联合主办,INNO创新家、产业集群发展提供战略支持,未名数创承办,邀请到了学术专家、行…...

史上最全的Qt控件
本软件是收费工具,学生党勿扰,闹眼子党勿扰,白嫖党勿扰 收费金额:1000元 1 概述 经过这两年的编写,写不少控件,甚至把刘某某90%的控件都绘制了一遍。当然后还有一些其他刘某没有控件。 2 功能 借用刘某博…...

星星之火:国产讯飞星火大模型的实际使用体验(与GPT对比)
#AIGC技术内容创作征文|全网寻找AI创作者,快来释放你的创作潜能吧!# 文章目录 1 前言2 测试详情2.1 文案写作2.2 知识写作2.3 阅读理解2.4 语意测试(重点关注)2.5 常识性测试(重点关注)2.6 代码…...

传输控制协议TCP
目录 TCP报文格式 TCP的特点 TCP原理: 1.确认应答机制 2.超时重传机制 3.连接管理机制 建立连接 编辑关闭连接 4.滑动窗口机制 5.流量控制 6.拥塞控制 7.延迟应答 8.捎带应答 TCP报文格式 1.源端口号:发送端的哪一个端口发出的 2.目的端口号:接收端的哪一个端…...

jmeter中用户参数和用户定义的变量的区别
如果使用jmeter做过参数化的人都知道,参数化的方式有多种,其中一种就是使用用户定义的变量,还有一种是使用用户参数。那么,这两个有什么异同呢? 一、先说相同的点: 1、都可以参数化,以供sample…...