Sping源码(七)—ConfigurationClassPostProcessor创建流程

序言

前两篇文章介绍了invokeBeanFactoryPostProcessors的执行流程,和自定义实现BeanDefinitionRegistryPostProcessor类的方式方法。

这篇文章会主要介绍Spring启动加载xml时,ConfigurationClassPostProcessor类是如何加载到Spring中,并且看看这个类中都做了些什么。

ConfigurationClassPostProcessor这个类在Spring中非常重要,并且之后在SpringBoot中也有涉及。

component-scan

介绍ConfigurationClassPostProcessor之前就不得不说一下context component-scan标签是如何进行加载解析的。

 <context:component-scan base-package="com.xxx" ></context:component-scan>

我们在Spring中通常都用 < context:component-scan >标签来实现包的扫描,而xml在加载解析时。
如果碰到默认的 bean、beans、import、alias标签,会走一套解析流程。
除此之外,其余每个标签中的每个属性都会有单独的Parser类来进行单独的解析。

component-scan解析流程图

在这里插入图片描述

源码

通过源码来仔细看上面的每一步都做了什么。

custom标签解析流程之前帖子中都有提过,不熟悉的这篇帖子中都有介绍。

spring.handlers
加载handlers文件并转换成Map,通过key找到context对应的handler
在这里插入图片描述

ContextNamespaceHandler
执行Handler下init()方法进行初始化,创建context标签下每一个属性的Parser,解析时通过key value形式获取到具体的Parser

public class ContextNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());}
}

parseCustomElement
通过key获取到具体的Handler

@Nullablepublic BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {// 获取对应的命名空间String namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;}// 根据命名空间找到对应的NamespaceHandlerspringNamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}// 调用自定义的NamespaceHandler进行解析return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}

parse
handler调用具体的parse()方法进行元素解析。

	@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {// 获取元素的解析器BeanDefinitionParser parser = findParserForElement(element, parserContext);return (parser != null ? parser.parse(element, parserContext) : null);}

ComponentScanBeanDefinitionParser
解析context标签的 component-scan元素,所以找到具体ComponentScanBeanDefinitionParser类parse方法。

public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {// 获取<context:component-scan>节点的base-package属性值String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);// 解析占位符basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);// 解析base-package(允许通过,;\t\n中的任一符号填写多个)String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);// Actually scan for bean definitions and register them.// 构建和配置ClassPathBeanDefinitionScannerClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);// 使用scanner在执行的basePackages包中执行扫描,返回已注册的bean定义Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);// 组件注册(包括注册一些内部的注解后置处理器,触发注册事件)registerComponents(parserContext.getReaderContext(), beanDefinitions, element);return null;}
}

doScan
获取base-package属性所对应的包名, 并加载所有能够被识别的Bean,封装到BeanDefinition中并注册。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();// 遍历basePackagesfor (String basePackage : basePackages) {// 扫描basePackage,将符合要求的bean定义全部找出来Set<BeanDefinition> candidates = findCandidateComponents(basePackage);// 遍历所有候选的bean定义for (BeanDefinition candidate : candidates) {// 解析@Scope注解,包括scopeName和proxyModeScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());// 使用beanName生成器来生成beanNameString beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {// 处理beanDefinition对象,例如,此bean是否可以自动装配到其他bean中postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {// 处理定义在目标类上的通用注解,包括@Lazy,@Primary,@DependsOn,@Role,@DescriptionAnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 检查beanName是否已经注册过,如果注册过,检查是否兼容if (checkCandidate(beanName, candidate)) {// 将当前遍历bean的bean定义和beanName封装成BeanDefinitionHolderBeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);// 根据proxyMode的值,选择是否创建作用域代理definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 注册beanDefinitionregisterBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}

registerComponents
这个方法一共做两件事:

  1. 将上一步获取到的所有BeanDefinition添加到compositeDef的nestedComponents属性中。
  2. 判断属性annotation-config值(默认为true),如果为true,则调用AnnotationConfigUtils.registerAnnotationConfigProcessors方法。
protected void registerComponents(XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {Object source = readerContext.extractSource(element);// 使用注解的tagName和source构建CompositeComponentDefinitionCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);// 将扫描到的所有beanDefinition添加到compositeDef的nestedComponents属性中for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));}// Register annotation config processors, if necessary.boolean annotationConfig = true;if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {// 获取component-scan标签的annotation-config属性值,默认为trueannotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));}if (annotationConfig) {// 如果annotation-config属性值为true,在给定的注册表中注册所有用于注解的bean后置处理器Set<BeanDefinitionHolder> processorDefinitions =AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);for (BeanDefinitionHolder processorDefinition : processorDefinitions) {// 将注册的注解后置处理器的BeanDefinition添加到compositeDef的nestedComponents属性中compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));}}// 触发组件注册事件,默认实现为EmptyReaderEventListenerreaderContext.fireComponentRegistered(compositeDef);}

核心方法 AnnotationConfigUtils.registerAnnotationConfigProcessors

经过了前面一系列的加载、铺垫,此时终于来到了核心方法,来看看ConfigurationClassPostProcessor是如何进行创建的。

AnnotationConfigUtils类中有许多的常量值,而代码中会进行判断,如果没有该BeanDefinition,则会进行创建,而创建的正是ConfigurationClassPostProcessor.class

源码片段

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {//省略部分源码....
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME ="org.springframework.context.annotation.internalConfigurationAnnotationProcessor";// 创建BeanDefinitionHolder集合Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// 注册内部管理的用于处理@configuration注解的后置处理器的beanif (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);// 注册BeanDefinition到注册表中beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}return beanDefs;}

完整方法
会在类型进行多个常量判断,如果BeanDefinition中不包含该类,则创建并封装到BeanDefinition中。

public abstract class AnnotationConfigUtils {/*** The bean name of the internally managed Configuration annotation processor.*/public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME ="org.springframework.context.annotation.internalConfigurationAnnotationProcessor";/*** The bean name of the internally managed BeanNameGenerator for use when processing* {@link Configuration} classes. Set by {@link AnnotationConfigApplicationContext}* and {@code AnnotationConfigWebApplicationContext} during bootstrap in order to make* any custom name generation strategy available to the underlying* {@link ConfigurationClassPostProcessor}.* @since 3.1.1*/public static final String CONFIGURATION_BEAN_NAME_GENERATOR ="org.springframework.context.annotation.internalConfigurationBeanNameGenerator";/*** The bean name of the internally managed Autowired annotation processor.*/public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME ="org.springframework.context.annotation.internalAutowiredAnnotationProcessor";/*** The bean name of the internally managed Required annotation processor.* @deprecated as of 5.1, since no Required processor is registered by default anymore*/@Deprecatedpublic static final String REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME ="org.springframework.context.annotation.internalRequiredAnnotationProcessor";/*** The bean name of the internally managed JSR-250 annotation processor.*/public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME ="org.springframework.context.annotation.internalCommonAnnotationProcessor";/*** The bean name of the internally managed JPA annotation processor.*/public static final String PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME ="org.springframework.context.annotation.internalPersistenceAnnotationProcessor";private static final String PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME ="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor";/*** The bean name of the internally managed @EventListener annotation processor.*/public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME ="org.springframework.context.event.internalEventListenerProcessor";/*** The bean name of the internally managed EventListenerFactory.*/public static final String EVENT_LISTENER_FACTORY_BEAN_NAME ="org.springframework.context.event.internalEventListenerFactory";/*** Register all relevant annotation post processors in the given registry.* @param registry the registry to operate on* @param source the configuration source element (already extracted)* that this registration was triggered from. May be {@code null}.* @return a Set of BeanDefinitionHolders, containing all bean definitions* that have actually been registered by this call*/public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {// 获取beanFactoryDefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {// //设置依赖比较器beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {// //设置自动装配解析器beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}// 创建BeanDefinitionHolder集合Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// 注册内部管理的用于处理@configuration注解的后置处理器的beanif (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);// 注册BeanDefinition到注册表中beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册内部管理的用于处理@Autowired,@Value,@Inject以及@Lookup注解的后置处理器beanif (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.// 注册内部管理的用于处理JSR-250注解,例如@Resource,@PostConstruct,@PreDestroy的后置处理器beanif (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));}// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.// 注册内部管理的用于处理JPA注解的后置处理器beanif (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition();try {def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,AnnotationConfigUtils.class.getClassLoader()));}catch (ClassNotFoundException ex) {throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);}def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册内部管理的用于处理@EventListener注解的后置处理器的beanif (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}// 注册内部管理用于生产ApplicationListener对象的EventListenerFactory对象if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));}return beanDefs;}
}

截止到这,从xml文件的加载到context 标签 component-scan属性的解析的整体流程已经介绍完成,
接下来看下费尽周折创建的ConfigurationClassPostProcessor中到底有什么?

ConfigurationClassPostProcessor

可以看到ConfigurationClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor和PriorityOrdered。

正好呼应上了上一篇所讲的内容,所以当调用beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class)时。
一定可以获取到Spring已经创建了的ConfigurationClassPostProcessor,并且也一定会调用对应的postProcessBeanDefinitionRegistry();方法。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {//省略部分源码。。。。/*** 定位、加载、解析、注册相关注解** Derive further bean definitions from the configuration classes in the registry.*/@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {// 根据对应的registry对象生成hashcode值,此对象只会操作一次,如果之前处理过则抛出异常int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);}// 将马上要进行处理的registry对象的id值放到已经处理的集合对象中this.registriesPostProcessed.add(registryId);// 处理配置类的bean定义信息processConfigBeanDefinitions(registry);}
}

下一篇帖子介绍ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry()方法都干了些什么。

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

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

相关文章

VScode 里面使用 python 去直接调用 CUDA

上一个 帖子主要分享了如何 去将 C 程序 打包成一个package。 我们最后的 目的实际上是想把 CUDA 的程序 打包成 一个 Package &#xff0c; C 程序只是起到了桥梁的作用&#xff1a; 首先&#xff1a;CUDA 程序 和 C 的程序一样&#xff0c; 都有一个 .cu 的源文件和 一个 .…

【数学建模】最优旅游城市的选择问题:层次分析模型(含MATLAB代码)

层次分析法&#xff08;The analytic hierarachy process&#xff0c;简称AHP&#xff09;是一种常用的决策分析方法&#xff0c;其基本思路是将复杂问题分解为多个组成部分&#xff0c;然后对这些部分进行逐一评估和比较&#xff0c;最后得出最优解决方案。&#xff08;例如&a…

JVM知识点总结二

参考文章&#xff1a;【Java面试题汇总】JVM篇&#xff08;2023版&#xff09;_jvm面试题2023-CSDN博客 1、说说你了解的JVM内存模型&#xff1a; JVM由三部分组成&#xff1a;类加载子系统、运行时数据区、执行引擎 JVM内存模型&#xff1a; 内存模型里的运行时数据区&#…

mongodb 实现两个集合的关联并分页查询

问题描述 实现两个集合的关联并分页查询。 假设&#xff1a; collection1中有deviceId等字段&#xff0c;collection2 中有deviceId、unitName等字段&#xff0c; 关联这两个colltion&#xff0c;并分页查询 代码实现 public ResponseEntity<String> getPageList(Reque…

ruby 配置代理 ip(核心逻辑)

在 Ruby 中配置代理 IP&#xff0c;可以通过设置 Net::HTTP 类的 Proxy 属性来实现。以下是一个示例&#xff1a; require net/http// 获取代理Ip&#xff1a;https://www.kuaidaili.com/?refrg3jlsko0ymg proxy_address 代理IP:端口 uri URI(http://www.example.com)Net:…

机器学习 | 使用Scikit-Learn实现分层抽样

在本文中&#xff0c;我们将学习如何使用Scikit-Learn实现分层抽样。 什么是分层抽样&#xff1f; 分层抽样是一种抽样方法&#xff0c;首先将总体的单位按某种特征分为若干次级总体&#xff08;层&#xff09;&#xff0c;然后再从每一层内进行单纯随机抽样&#xff0c;组成…

Django项目使用uwsgi+nginx部署上线

Django项目使用uwsginginx部署上线 前言settings 配置安装uwsgi 和配置uwsgi推荐配置文件启用wsgi不使用nginx的配置&#xff08;不推荐&#xff09;使用nginx的配置 安装 nginx和配置niginx 配置 运行参考资料 前言 代码已经开发完成&#xff0c;正式部署上线 settings 配置…

Redmi Turbo 3新品发布,天星金融(原小米金融)优惠加持护航新机体验

Redmi新十年使命不变&#xff0c;挑战不断升级。Redmi Turbo 3&#xff0c;作为Turbo系列的开篇之作&#xff0c;将自身定位为新生代性能旗舰&#xff0c;决心重塑中端性能新格局。据悉&#xff0c;Redmi Turbo 3于4月10日已正式发布。预售期间更是连续数日&#xff0c;蝉联小米…

SSL证书在HTTP与HTTPS中的角色差异是什么?

在互联网的广泛应用背景下&#xff0c;随着网络攻击和数据泄露事件频发&#xff0c;保障用户的数据安全已成为至关重要的议题。传统的HTTP协议在传输数据时不进行加密处理&#xff0c;导致数据在传输过程中暴露于潜在的窃听和篡改风险中&#xff0c;安全性薄弱。而通过引入SSL/…

解决Keil V5.38 和 ST-Link V3 Debug不能运行问题

目录 概述 1 问题描述 1.1 情况一 1.2 情况二 1.3 情况三 2 解决方法 2.1 认识Keil Mico Lib 2.2 使能Keil Mico Lib 3 验证 3.1 进入C程序Main验证 3.2 断点验证 3.3 上电重启验证 4 结论 笔者使用的验证代码下载地址&#xff1a; stm32-freeRTOS-queue资源-CSD…

微信小程序日期增加时间完成订单失效倒计时(有效果图)

效果图 .wxml <view class"TimeSeond">{{second}}</view>.js Page({data: {tiem_one:,second:,//倒计时deadline:,},onLoad(){this.countdown();},countdown(){let timestamp Date.parse(new Date()) / 1000;//当前时间戳let time this.addtime(2024…

k8s 部署 kube-prometheus监控

一、Prometheus监控部署 1、下载部署文件 # 使用此链接下载后解压即可 wget https://github.com/prometheus-operator/kube-prometheus/archive/refs/heads/release-0.13.zip2、根据k8s集群版本获取不同的kube-prometheus版本部署 https://github.com/prometheus-operator/k…

VSCode断点调试(ROS)

0、安装ros插件 在扩展商店中安装ROS插件&#xff08;Microsoft&#xff09; 1、修改CMakeList.txt # set(CMAKE_BUILD_TYPE "Release") // 注释Release模式 set(CMAKE_BUILD_TYPE "Debug") // 设置为Debug模式 # set(CMAKE_CXX_FLAGS_RELEASE &…

递归、搜索与回溯算法:回溯,决策树

回溯算法是⼀种经典的递归算法&#xff0c;通常⽤于解决组合问题、排列问题和搜索问题等。 回溯算法的基本思想&#xff1a;从⼀个初始状态开始&#xff0c;按照⼀定的规则向前搜索&#xff0c;当搜索到某个状态⽆法前进时&#xff0c;回退到前⼀个状态&#xff0c;再按照其他…

Hadoop3:大数据的基本介绍

一、什么是大数据 1、大数据的4v特点 Volume&#xff08;大量&#xff09; Velocity&#xff08;高速&#xff09; Variety&#xff08;多样&#xff09; Value&#xff08;低价值密度&#xff09; 2、大数据部门间的工作岗位 第三部分&#xff0c;其实就是JavaWeb 二、…

yarn 会从npm config registry 下载依赖吗

Yarn 通常会使用自己配置的 registry 来下载依赖包&#xff0c;但如果在 Yarn 中没有专门配置 registry&#xff0c;它有时会默认继承 npm 的全局配置。当你执行 npm config set registry <registry-url> 设置了 npm 的 registry 后&#xff0c;若 Yarn 没有单独设置 reg…

react经验12:等待状态更新

应用场景: 等待react组件内的state发生变更后进行后续操作。 已知问题 通常state的变化会引起dom的刷新&#xff0c;更新state一般使用setState&#xff0c;但这是个异步操作。 如果此时需要立即操作dom&#xff0c;得到的目标dom是刷新之前的样子。 应对方法 方法1:使用u…

用 element ui 实现季度选择器

由于在数据项目中经常以各种时间条件查询数据&#xff0c;所以时间选择器&#xff08;DatePicker&#xff09;组件是很常用的组件。但是在我使用的 Element UI 中&#xff0c;缺少了季度选择器的功能。 简易实现 一开始我根据时间范围使用 select 去遍历,如 2024-Q1、2023-Q4…

注册中心~

注册中心&#xff1a;是服务实例信息的存储仓库&#xff0c;也是服务提供者和服务消费者进行交互的桥梁。它主要提供了服务注册和服务发现这两大核心功能。 注册中心可以说是微服务架构中的“通讯录”&#xff0c;它记录了服务和服务地址的映射关系。 常用的注册中心有Zookeep…

Spring boot注解开发mybatis

数据库连接配置&#xff0c;在resources目录下的application.propertis文件配置&#xff1a; #数据库连接配置 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.datasource.urljdbc:mysql://localhost:3306/db1 spring.datasource.usernameroot spring.d…