当前位置: 首页 > news >正文

Spring实例化源码解析之ConfigurationClassParser(三)

前言

上一章我们分析了ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法的源码逻辑,其中核心逻辑do while中调用parser.parse(candidates)方法,解析candidates中的候选配置类。然后本章我们主要分析ConfigurationClassParser的parse方法到底做了什么。

parse

configCandidates在项目启动的时候只有我们的AopConfig类,也就是说这个parse方法其实就是解析启动类。

在这里插入图片描述

public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());}else {parse(bd.getBeanClassName(), holder.getBeanName());}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);}}this.deferredImportSelectorHandler.process();}

当前我的启动类是AopConfig,属于AnnotatedBeanDefinition,会进入到第一个if逻辑里。parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());第一个参数传入的元数据,第二个参数传入的是beanName。

private static final Predicate<String> DEFAULT_EXCLUSION_FILTER = className ->(className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."));protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);}

processConfigurationClass方法传递了一个ConfigurationClass和一个Predicate,Predicate就是为了判断ClassName的,ConfigurationClass包含了元数据、beanName等信息。

processConfigurationClass

进入方法的第一个if用于判断是否包含@Condition注解,反正含义就是看这个候选配置类需不需要跳过,里面的其他逻辑就不关注了。下面其实就是核心逻辑了,我们继续拆解分析。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}// 查看这个配置类是不是存在了,如果存在就走下面的if逻辑ConfigurationClass existingClass = this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}// Otherwise ignore new imported config class; existing non-imported class overrides it.return;}else {// Explicit bean definition found, probably replacing an import.// Let's remove the old one and go with the new one.this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}}// Recursively process the configuration class and its superclass hierarchy.SourceClass sourceClass = asSourceClass(configClass, filter);// 此处使用了个do whiledo {// 查看源码就是习惯性的查看do something开头的,至少spring是这样的一个习惯。sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);}while (sourceClass != null);// 解析完了放入configurationClassesthis.configurationClasses.put(configClass, configClass);}

existingClass是看配置类是不是已经解析过了,为什么启动的时候要判断是否存在,原因就是因为底下有个do while循环。这个方法后续会调用很多次。如果不存在会走底下的do while逻辑,就是本章的核心内容。这里会很绕,我尽量讲清楚。

SourceClass sourceClass = asSourceClass(configClass, filter);还是可以单独拿出来描述一下:

方法的参数包括一个可为null的className和一个Predicate<String>类型的filter,还可能抛出IOException异常。

方法的执行逻辑如下:

1、首先,检查className是否为null或者经过filter筛选后返回true。如果是这种情况,直接返回预先设定的objectSourceClass对象。

2、如果className以"java"开头,表示该类是核心Java类型,将不使用ASM(Java字节码操作库)进行处理。此时,尝试加载该类,并使用ClassUtils.forName方法获取类的Class对象。如果加载失败,将抛出ClassNotFoundException异常并包装为NestedIOException。

3、如果以上条件都不满足,说明className表示的类不是核心Java类型,那么使用metadataReaderFactory(可能是一个元数据读取工厂)获取className对应的元数据读取器(MetadataReader),并将其传递给SourceClass的构造函数,创建一个新的SourceClass对象。

doProcessConfigurationClass

首先我的AopConfig有两个注解,@EnableAspectJAutoProxy和@ComponentScan(value = {“com.qhyu.cloud.**”}),当然我们的springboot项目一般都是约定大于配置的方式,不写这个ComponentScan注解。

在这里插入图片描述

下面是整个方法的源码和我的个人注释信息,我们这边还是先将源码整体放出来,然后再拆解分析。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)throws IOException {// 如果这个候选配置类中有Component注解,走下面的逻辑if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass, filter);}// Process any @PropertySource annotationsfor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotations// 我们的AopConfig候选配置类就会走到下面这个逻辑Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);// componentScans不为空,第一个条件满足// sourceClass.getMetadata()不为null;通过Conditional注解来控制bean是否需要注册,控制被@Configuration标注的配置类是否需要被解析 第二个条件false,取反。if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediately// 使用this.componentScanParser ComponentScanAnnotationParser来解析// 这里面将会注册我们自己写的一些将被spring接管的类的BeanDefinition信息Set<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotationsprocessImports(configClass, sourceClass, getImports(sourceClass), filter, true);// Process any @ImportResource annotationsAnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methodsSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;}
片段一
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass, filter);}

当前AopConfig启动类是没有Component注解的,不会进入这个逻辑。代码中的英文注释说的是首先递归处理任何成员(嵌套)类,也就是说如果@Component注解注释的类中没有内部类(嵌套类)也不会做任何处理。

片段二
// Process any @PropertySource annotationsfor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}

直接看注释,处理任何被@PropertySource注解修饰的class

片段三
// Process any @ComponentScan annotations// 我们的AopConfig候选配置类就会走到下面这个逻辑Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);// componentScans不为空,第一个条件满足// sourceClass.getMetadata()不为null;通过Conditional注解来控制bean是否需要注册,控制被@Configuration标注的配置类是否需要被解析 第二个条件false,取反。if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediately// 使用this.componentScanParser ComponentScanAnnotationParser来解析// 这里面将会注册我们自己写的一些将被spring接管的类的BeanDefinition信息Set<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}

处理任何被@ComponentScan注解修时的类,当然这里还包括@ComponentScans注解,componentScans属性会获取到注解的信息,如下图所示:

在这里插入图片描述

if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN))

首先componentScans属性肯定不为空,所以第一个条件是ture,然后看是否应该跳过,当前AopConfig不存在Condition注解,返回false,此处取反,所以if条件返回true,进入if逻辑。

for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediately// 使用this.componentScanParser ComponentScanAnnotationParser来解析// 这里面将会注册我们自己写的一些将被spring接管的类的BeanDefinition信息Set<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}

这个循环,获取的AnnotaionAttributes就是我们的@ComponentScan(value = {“com.qhyu.cloud.**”})注解的信息。然后会调用使用this.componentScanParser ComponentScanAnnotationParser来解析,主要就是扫描我们定义的路径下的所有需要被Spring管理的bean,组装程BeanDefinitionHolder返回到scannedBeanDefinitions集合,同时这些bean的定义信息会被注入到bean工厂中,本章在此处不会深入的去分析里面的源码,将在下一章节进行分析。

在这里插入图片描述

if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}

这段源码在之前已经分析过了,第二章节,如果发现我们scannedBeanDefinitions中还存在配置类,就继续解析,也就是把这些beanDefinition都注册到bean工厂中。
如果不是配置类或者处理完了配置类之后会继续往下走他的逻辑。

片段四

处理被@Import注解修时的类

processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

比如我们的AopConfig不是有个@EnableAspectJAutoProxy注解嘛,这个注解里面会有个@Import,所以AopConfig解析之后会执行configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());代码,在ConfigurationClassPostProcessor的processConfigBeanDefinitions方法中this.reader.loadBeanDefinitions(configClasses);这行代码会读取出来进行注册。

在这里插入图片描述

片段五

处理任何@ImportResource annotations

// Process any @ImportResource annotations
AnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}
}
片段六

处理各个@Bean的方法

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}
片段七

处理接口上的默认方法

processInterfaces(configClass, sourceClass);
片段八

处理superclass,超类,如果有的话。可以发现这里面有个return,其他的片段都是返回的void,此处如果返回的话,外部的do while就会继续执行。

if (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}
}

在这里插入图片描述

整理流程梳理

方法的执行逻辑如下:

  • 首先,检查配置类 configClass 是否被 @Component 注解标记。如果是,则递归处理任何成员类(嵌套类)。

  • 处理任何 @PropertySource 注解。遍历配置类中的 @PropertySource 注解,如果当前环境实现了 ConfigurableEnvironment 接口,则进行属性源的处理。否则,记录日志,表明忽略该注解。

  • 处理任何 @ComponentScan 注解。获取配置类中的 @ComponentScan 注解,如果存在且满足相关条件(componentScans 不为空,且配置类满足条件判断),则使用 componentScanParser 来解析注解,执行组件扫描操作,并注册相应的 BeanDefinition 信息。如果扫描到的定义中包含其他配置类,则递归解析这些配置类。

  • 处理任何 @Import 注解。根据配置类中的 @Import 注解,获取需要导入的类,并进行处理。这些导入的类可以是其他配置类,也可以是其他普通的类。处理过程中,可能会继续递归解析导入的类。

  • 处理任何 @ImportResource 注解。根据配置类中的 @ImportResource 注解,获取资源的位置和读取器类型,并进行处理。通常,这些资源是 XML 配置文件。解析过程中,可能会使用 BeanDefinitionReader 来读取并注册相关的 BeanDefinition。

  • 处理配置类中的每个 @Bean 方法。获取配置类中所有的 @Bean 方法的元数据,并将其添加到 configClass 对象中。

  • 处理接口的默认方法。如果配置类实现了接口,并且接口中定义了默认方法,则对这些默认方法进行处理。

  • 处理父类(如果存在)。如果配置类有父类,并且父类不是 Java 核心类,也不在已知的父类列表中,则将父类添加到已知的父类列表中,并返回父类的注解元数据,以便进行递归处理。

  • 如果没有父类,则处理完成,返回 null。

总结

本章主要分析了ConfigurationClassParser的parse方法的源码,其中设计到的ComponentScanAnnotationParser的parse方法我们没有深入分析,这里面涉及到的是将我们启动类目录或者@ComponentScan注解的basePackages目录下的我们需要交给容器管理的bean的定义信息注册将在下一章进行分析。

相关文章:

Spring实例化源码解析之ConfigurationClassParser(三)

前言 上一章我们分析了ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法的源码逻辑&#xff0c;其中核心逻辑do while中调用parser.parse(candidates)方法&#xff0c;解析candidates中的候选配置类。然后本章我们主要分析ConfigurationClassParser的…...

在 Substance Painter中实现Unity Standard Shader

由于有需要在Substance Painter中显示什么样的效果&#xff0c;在Unity就要显示什么样的效果的需求&#xff0c;最近研究了几天&#xff0c;总算在Substance Painter中实现Unity standard的材质的渲染效果。具体效果如下&#xff1a; 在Unity中&#xff1a; Substance Painte…...

第二证券:个人开证券账户要开户费吗?

随着互联网和移动端东西的遍及&#xff0c;越来越多的人开端涉足股票投资&#xff0c;开立证券账户也成为一个热门话题。但是&#xff0c;许多初学者或许会有疑问&#xff0c;个人开证券账户是否需求支付开户费呢&#xff1f;这个问题的答案并不是那么简略&#xff0c;需求考虑…...

大厂面试-16道面试题

1 java集合类有哪些&#xff1f; List是有序的Collection&#xff0c;使用此接口能够精确的控制每个元素的插入位置&#xff0c;用户能根据索引访问List中元素。常用的实现List的类有LinkedList&#xff0c;ArrayList&#xff0c;Vector&#xff0c;Stack。 ArrayList是容量…...

搭建GraphQL服务

js版 GraphQL在 NodeJS 服务端中使用最多 安装graphql-yoga: npm install graphql-yoga 新建index.js: const {GraphQLServer} require("graphql-yoga")const server new GraphQLServer({ typeDefs: type Query { hello(name:String):String! …...

数据仓库介绍及应用场景

数据仓库&#xff08;Data Warehouse&#xff09;是一个用于存储、管理、检索和分析大量结构化数据的集中式数据库系统。与传统的事务处理数据库不同&#xff0c;数据仓库是为了支持决策支持系统&#xff08;Decision Support Systems, DSS&#xff09;和业务智能&#xff08;B…...

代码随想录算法训练营Day56 | 动态规划(16/17) LeetCode 583. 两个字符串的删除操作 72. 编辑距离

动态规划马上来到尾声了&#xff0c;当时还觉得动态规划内容很多&#xff0c;但是也这么过来了。 第一题 583. Delete Operation for Two Strings Given two strings word1 and word2, return the minimum number of steps required to make word1 and word2 the same. In on…...

HTML+CSS+JavaScript 大学生网页设计制作作业实例代码 200套静态响应式前端网页模板(全网最全,建议收藏)

目录 1.介绍2.这样的响应式页面这里有200套不同风格的 1.介绍 资源链接 &#x1f4da;web前端期末大作业 (200套) 集合 Web前端期末大作业通常是一个综合性的项目&#xff0c;旨在检验学生在HTML、CSS和JavaScript等前端技术方面的能力和理解。以下是一些可能的Web前端期末大…...

CFimagehost私人图床本地部署结合cpolar内网穿透实现公网访问

文章目录 1.前言2. CFImagehost网站搭建2.1 CFImagehost下载和安装2.2 CFImagehost网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…...

uniapp瀑布流布局写法

首先我们要清楚瀑布流是什么&#xff1f; 瀑布流布局&#xff08;Waterfall Flow Layout&#xff09;&#xff0c;也称为瀑布流式布局&#xff0c;是一种常见的网页或移动应用布局方式&#xff0c;特点是元素以不规则的方式排列&#xff0c;就像瀑布中的流水一样&#xff0c;每…...

蓝桥杯 题库 简单 每日十题 day8

01 扫雷 题目描述 在一个n行列的方格图上有一些位置有地雷&#xff0c;另外一些位置为空。 请为每个空位置标一个整数&#xff0c;表示周围八个相邻的方格中有多少个地雷。 输入描述 输入的第一行包含两个整数n&#xff0c;m。 第2行到第n1行每行包含m个整数&#xff0c;相邻整…...

Keepalived 高可用(附带配置实例,联动Nginx和LVS)

Keepalived 一、Keepalived相关知识点概述1.1 单服务的风险&#xff08;单点故障问题&#xff09;1.2 一个合格的集群应该具备的特性1.3 VRRP虚拟路由冗余协议1.4 健康检查1.5 ”脑裂“现象 二、Keepalived2.1 Keepalived是什么&#xff1f;2.2 Keepalived体系主要模块及其作用…...

第二证券:今年来港股回购金额超700亿港元 9月近200家公司获增持

本年以来&#xff0c;港股上市公司回购力度不断增强。据恒生指数公司计算&#xff0c;到9月15日&#xff0c;本年以来港股回购金额到达735亿港元&#xff0c;占去年全年总额的70%。该公司预测&#xff0c;2023年港股回购金额可能到达929亿港元&#xff0c;是前5年年度平均水平的…...

Autosar基础——RTE简介

AutoSAR文章目录 AUTomotive Open System Architecture Autosar-简介和历史发展 Autosar-软件架构 Autosar软件组件-Application Layer介绍和SWC(Software Component)类型 Autosar-Runnables(可运行实体) Autosar-OS配置 Autosar IOC机制(核间通信) Autosar实践-CANTp Auto…...

几个国内可用的强大的GPT工具

前言&#xff1a; 人工智能发布至今&#xff0c;过去了九个多月&#xff0c;已经成为了我们不管是工作还是生活中一个重要的辅助工具&#xff0c;大大提升了效率&#xff0c;作为一个人工智能的自然语言处理工具&#xff0c;它给各大行业的提供了一个巨大的生产工具&#xff0c…...

《Python等级考试(1~6级)历届真题解析》专栏总目录

❤️ 专栏名称&#xff1a;《Python等级考试&#xff08;1~6级&#xff09;历届真题解析》 &#x1f338; 专栏介绍&#xff1a;中国电子学会《全国青少年软件编程等级考试》Python编程&#xff08;1~6级&#xff09;历届真题解析。 &#x1f680; 订阅专栏&#xff1a;订阅后可…...

在IntelliJ IDEA 中安装阿里P3C以及使用指南

在IntelliJ IDEA 中安装阿里P3C以及使用指南 1.关于阿里p3c1.1说明1.2什么是P3C插件1.3p3c的作用是什么 2 如何在IDEA中安装p3c2.1 插件安装2.2 插件使用 3.参考连接 1.关于阿里p3c 1.1说明 代码规范检查插件P3C&#xff0c;是根据《阿里巴巴java开发手册(黄山版)》转化而成的…...

Java集成支付宝沙箱支付,详细教程(SpringBoot完整版)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、开发前准备&#xff1f;二、使用步骤1、引入库2、配置在 application.yml 里面进行配置&#xff1a;3、alipay的java配置&#xff1a;AplipayConfig.java4、支付…...

详解Nacos和Eureka的区别

文章目录 Eureka是什么Nacos是什么Nacos的实现原理 Nacos和Eureka的区别CAP理论连接方式服务异常剔除操作实例方式自我保护机制 Eureka是什么 Eureka 是Spring Cloud 微服务框架默认的也是推荐的服务注册中心, 由Netflix公司与2012将其开源出来,Eureka基于REST服务开发,主要用…...

在Vue中实现组件间的通信(父子通信,非父子通信,通用通信)

在vue中实现组件间的通信 文章目录 在vue中实现组件间的通信1、组件通信1.1、不同的组件关系和组件通信方案分类1.2、组件通信的解决方案1.3、非父子通信- event bus事件总线 2、prop2.1、prop详解2.2、prop校验2.3、prop & data、单向数据流 3、v-mdoel原理 1、组件通信 …...

LLaMA参数微调方法

1.Adapter Tuning&#xff1a;嵌入在transformer中 新增了一个名为adapter的结构&#xff0c;其核心思想是保持模型其他原始参数不变&#xff0c;只改变adapter的参数&#xff0c;其结构如下图所示&#xff1a; 1.在每一个transformer模块最后都加入一层adapter。 2.adapter首…...

NSSCTF之Misc篇刷题记录(17)

NSSCTF之Misc篇刷题记录&#xff08;17&#xff09; [闽盾杯 2021]DNS协议分析[GFCTF 2021]pikapikapika NSSCTF平台&#xff1a;https://www.nssctf.cn/ PS&#xff1a;所有FLAG改为NSSCTF [闽盾杯 2021]DNS协议分析 数据包提示给得是DNS数据包 直接过滤一下 发现 数据里面存…...

红与黑(bfs + dfs 解法)(算法图论基础入门)

红与黑问题 文章目录 红与黑问题前言问题描述bfs 解法dfs 解法 前言 献给阿尔吉侬的花束( 入门级bfs查找 模版解读 错误示范 在之前的博客当中&#xff0c;详细地介绍了这类题目的解法&#xff0c;今天为大家带来一道类似的题目练练手&#xff0c;后续还会更新更有挑战的题目…...

为何学linux及用处

目前企业使用的操作系统无非就是国产类的&#xff0c;windows和linux类。我们要提升自己的技能&#xff0c;需要学习这两款。我记得在大学时期&#xff0c;学习过windows以及linux&#xff0c;但当时觉得又不常用&#xff0c;就学的模棱两可。毕业之后&#xff0c;你会发现&…...

ChatGPT高级数据分析功能

目录 只需上传数据集,系统即可自动进行分析。我们首先进行了一次测试。准备了一份关于二手车的数据,其格式如下: 接下来调用,GPT中的高级数据分析功能,上传数据,并要求进行分析 第一步:自动对数据字段进行详细的解释: 第二步,对数据进行预处理,比如缺失值,基本的…...

共享WiFi贴项目怎么实施与运营,微火为你提供高效解答!

共享WiFi贴是一项有前景的商业项目&#xff0c;不仅可以满足用户对网络的需求&#xff0c;还可以为创业者带来盈利的机会。那么&#xff0c;我们来看看如何有效地开展共享WiFi贴项目。 最重要的是选择合适的位置。共享WiFi贴项目的成功与否很大程度上取决于位置选择。优先选择人…...

计算机组成原理——基础入门总结(二)

上一期的路径&#xff1a;基础入门总结&#xff08;一&#xff09; 目录 一.输入输出系统和IO控制方式 二.存储系统的基本概念 三.cache的基本概念和原理 四.CPU的功能和基本结构 五.总线概述 一.输入输出系统和IO控制方式 IO设备又可以被统一称为外部设备~ IO接口&…...

腾讯mini项目-【指标监控服务重构】2023-08-06

今日已办 feature/client_traces_profile 修改 consumer 4个阶段的 spankind将 profile 的 span 作为 root span&#xff0c;保持与 venus 的 follows from 的 link feature/profile-otelclient-metric 将 metric 部分使用新分支 push go.opentelemetry.io/otel/propagatio…...

ruoyi菜单折叠,菜单收缩

问题描述 VUE菜单有一个BUG&#xff0c;当我们点击其它按钮或者首页的时候&#xff0c;已经展示的一级菜单是不会自动收缩的。这个问题也导致很多开发者把一级菜单都换成了二级菜单。 错误展示 错误的效果请看下图。 解决方法 1、寻找菜单文件 因为我使用的是ruoyi的前端框…...

Linux 用户和用户组

Linux中关于权限的管控级别有2个级别&#xff0c;分别是: 针对用户的权限控制 针对用户组的权限控制 比如&#xff0c;针对某文件&#xff0c;可以控制用户的权限,也可以控制用户组的权限。 1、用户组管理 1.1、创建用户组 groupadd 用户组名 1.2、删除用户组 groupdel 用户…...

武宣县住房和城乡建设局网站/百度热线电话

这些软件测试常识你必须牢记&#xff1a; 01、软件测试&#xff08;软件测试存在的意义&#xff09; 1、发现程序中的错误而执行程序的过程 2、检验产品是否符合用户需求 3、提高用户体验 02、软件测试原则&#xff08;常识&#xff09; 1、尽早介入&#xff08;需求分析…...

本地做网站教程/中国旺旺(00151) 股吧

关于VB中的冒号&#xff0c;给许多人的印象都是&#xff1a;“一行可书写几句语句”。这么说是对的&#xff0c;但是有一种情况是不对的&#xff0c;那就是在条件语句中。这也是做一个VB项目升级的时候遇到&#xff0c;因为这个问题我查了好长时间程序&#xff0c;一直在找VB的…...

制作企业网站作业网页模板/优化大师电脑版

MQTT中的QoS等级 MQTT设计了一套保证消息稳定传输的机制&#xff0c;包括消息应答、存储和重传。在这套机制下&#xff0c;提供了三种不同层次QoS&#xff08;Quality of Service&#xff09;&#xff1a; QoS0&#xff0c;At most once&#xff0c;至多一次&#xff1b; QoS…...

wordpress 推广 插件/杯子软文营销300字

java中日期时间类 1、Date类 ​ java.util 包提供了 Date 类来封装当前的日期和时间。 ​ Date 类提供两个构造函数来实例化 Date 对象。 ​ 第一个构造函数使用当前日期和时间来初始化对象。 Date()​ 第二个构造函数接受一个参数&#xff0c;该参数是从1970年1月1日起的…...

个性创意网站/电子商务网站建设与管理

BLE各版本新功能总结 2017-04-20 by isyq 协议发布时间协议版本2016/12Bluetooth 52014/12Bluetooth 4.22013/12Bluetooth 4.12010/6Bluetooth 4.0 Bluetooth 5 新功能 新增功能协议层描述Slot Availability Mask (SAM)物理层标记设备的收发时间块。其他蓝牙设备获取该信息即…...

帮客户做传销网站/百度seo外包

Go channel 关闭和广播 Dont Communicate by sharing memory, sharing memory by communicate. 不要通过共享内存的方式通讯,要通过通讯的方式共享内存Channel 基本概念 一个通道相当于 FIFO 的队列, 通道中各个元素之间都是严格按照发送的顺序队列,先发送的队列一定会被先…...