Dubbo源码深度解析(二)

news2024/11/24 9:28:44

        接着《Dubbo源码深度解析(一)》继续讲,上篇博客主要讲Dubbo提供的三个注解的作用,即:@EnableDubbo、@DubboComponentScan、@EnableDubboConfig。其中后两个注解是在@EnableDubbo上的,因此在启动类上加上@EnableDubbo注解,等同于加上@DubboComponentScan注解和@EnableDubboConfig注解。并且还讲到了Dubbo的包扫描,以及Dubbo整合SpringBoot后,是如何将配置文件中的dubbo.xxx属性绑定到Dubbo的配置类上的。  

        本篇博文将主要讲@DubboService注解的原理以及Dubbo的SPI机制,其实@DubboService注解的原理,在上篇博文中已经讲过了。这里回顾一下:核心是依赖ServiceClassPostProcessor或者ServiceAnnotationBeanPostProcessor,其中,ServiceAnnotationBeanPostProcessor继承自ServiceClassPostProcessor(非抽象类),都是实现了BeanDefinitionRegistryPostProcessor接口。因此会实现BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry()方法,在该方法中,会扫描指定包下所有被@DubboService注解修饰的类,并往Spring容器中注册一个BeanDefinition对象,其中BeanDefinition的beanClass的值为ServiceBean.class,最终Spring实例化的就是beanClass返回的类型,即ServiceBean,上篇博文最后通过AbstractApplicationContext#getBeansOfType(ServiceBean.class)方法,能获取到一个Bean对象,这跟预期是一致的。

        其实在生成ServiceBean的时候,会用到Dubbo的配置类,在子模块service-provider中,我通过DubboConfiguration,定义了四个配置类,分别是:ApplicationConfig、ProtocolConfig、RegistryConfig和ConfigCenterConfig ,代码如下:

package com.szl.config;

import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Dubbo配置类,以下几个类实际上都是 AbstractConfig类的子类,在Spring容器初始化这几个Bean的时候,
 * 由于父类中的 AbstractConfig#addIntoConfigManager()方法是被@PostConstruct注解所修饰的,因此,
 *  该方法会调用被自动调用。
 * </p>
 */
@Configuration
public class DubboConfiguration {

    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("service-provider");
        return applicationConfig;
    }

    @Bean
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setPort(20880);
        protocolConfig.setName("dubbo");
        protocolConfig.setServer("netty4");
        return protocolConfig;
    }

    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("nacos://127.0.0.1:8848");
        return registryConfig;
    }

    @Bean
    public ConfigCenterConfig configCenterConfig() {
        ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
        configCenterConfig.setPort(8848);
        configCenterConfig.setProtocol("nacos");
        configCenterConfig.setAddress("127.0.0.1");
        // 使用默认值
        // configCenterConfig.setGroup("DEFAULT_GROUP");
        // configCenterConfig.setConfigFile("application-service-provider.yml");
        configCenterConfig.setNamespace("b1cb3cb8-9c6d-4cfc-a391-26aa0119a421");
        return configCenterConfig;
    }
}

        我在注释中写的很详细,这四个配置类都有一个相同的父类,即AbstractConfig,并且在其父类中,有一个方法是被@PostConstruct注解修饰的。最后在初始化这个Bean的时候会,Spring会自动调用被这个注解修饰的方法,看看AbstractConfig#addIntoConfigManager()方法,代码如下:

857bc0212ef344728250193657e641f0.png

        在该方法中,调用了ApplicationModel.getConfigManager()方法,得到ConfigManager对象,即配置管理器,看看是怎么获取的,代码如下:

70ce246bd2294d33a5a2584ffbcbe4f6.png

        又需要看看ApplicationModel的LOADER属性是怎么获取的,其实就是看ExtensionLoader#getExtensionLoader(FrameworkExt.class),代码如下:

d2f0817a735c4ba1ba12f9a807835018.png

        可知,ExtensionLoader有一个EXTENSION_LOADERS,用作缓存,如果通过传入的Class获取不到,则创建一个对象ExtensionLoader,调用其有参构造方法,传入Class,这里的Class就是FrameworkExt.class,并将创建好的ExtensionLoader放入EXTENSION_LOADERS,方便下次直接获取。具体看看ExtensionLoader的有参构造方法,代码如下: b18d97a90e31407d92e9cab44ab9ebb0.png

        因此又会调用到ExtensionLoader#getExtensionLoader()方法,也就是刚刚前面讲的。只不过此时的type属性为ExtensionFactory.class,然后还是先从EXTENSION_LOADERS中获取,假设是第一次,也获取不到,因此会调用ExtensionLoader的有参构造方法。再次回到上图的方法中,但是不同的是,这次由于type是等于ExtensionFactory.class,则此时objectFactory为null,并且还要调用ExtensionFactory#getAdaptiveExtension()方法,看看该方法,代码如下:

a5916d232fcc43efbd5bdca3dec35ace.png

b1484edda3894dd584c2862199f80b9f.png

97a9425a38294363877d2cdd1aaa58a6.png

c605db50c88b49258d4c017e4839f4fe.png

994842aaddac46dea3f3db80d1b77ff2.png

        看看ExtensionFactory,代码如下:

1dace79f000545eca2df299fbdbfd5e2.png

        ExtensionFactory确实被@SPI注解修饰了,只不过value值为"",因此ExtensionLoader#cacheDefaultExtensionName()方法中,最终设置的cachedDefaultName属性为null。回到ExtensionLoader#loadExtensionClasses(),紧接着就是调用ExtensionLoader#loadDirectory()方法,不过在此之前按,需要先看看ExtensionLoader的strategies属性,代码如下:

4983573706954382b8bf7040d672229d.png

8f92208c9477409aabd29518ea3df183.png

        调用ServiceLoader#load()方法,传入LoadingStrategy.class,代码如下:

2716ad08d90046918932a784912fea32.png

41d2825226db4fad9191b72712f46ec5.png

        通过Java的stream流,遍历调用,new LoadingStrategy接口的实现类,得到对象,放入ExtensionLoader的strategies属性中,那LoadingStrategy接口的实现类是如何获取的呢?其实ServiceLoader是Java的 rt.jar下提供的类,属于Java自己实现的SPI机制,它会从classpath目录下的META-INF/services下,找一个名为LoadingStrategy接口全限定名的文件,即org.apache.dubbo.common.extension.LoadingStrategy,刚好找到了,代码如下:

ec014ab9fa3b4dc1944671947927411b.png

        因此最终实例化的就是这三个实现类,断点验证一下:

5eb60f45e36d49a7add3506e6999d06b.png

        因此,我们如果想实现SPI机制,可以借助于Java自带的ServiceLoader实现,如果想实现更复杂的SPI机制,可以借鉴Dubbo,因为讲到这里,还不全是Dubbo的SPI机制。回到ExtensionLoader#loadExtensionClasses()方法,代码如下:

957400923afc45fe92698ad2de727dd4.png

        这里涉及到加载的路径,看看LoadingStrategy接口的三个实现类,代码如下:

be00706224884164bfc4cab3841d5ac0.png

e8156c26cccb4f0e80083f26d08baa54.png

fa9a60c7a48c44e49099502f15d5d7d2.png

        可以知道,扫描的路径分别是:META-INF/dubbo/internal/ 或 META-INF/dubbo/ 或 META-INF/services/。ok,再看看ExtensionLoader#loadDirectory()方法,该方法是关键,代码如下:

98687fe4938c406985339df06ad3a9ca.png

        可以知道,最终是根据前面的几个路径下,并且拼接接口的全限定名,得到文件路径,通过ClassLoader去找文件,得到urls并进行遍历,最终调用到ExtensionLoader#loadResource()方法,进行真正的加载,看看该方法,代码如下:

fd1d9afdf58a47269e162b2bcd9b51a0.png

ff770a58147b40e6b070534951e29da9.png

        当然,现在看的是dubbo-common下的文件,在其他模块下,可能也存在org.apache.dubbo.common.extension.ExtensionFactory,这里就不去找了。再看看ExtensionLoader#loadClass()方法,代码如下:

38e7794ab05f4811b517b0072e458a10.png

        顺便看看ExtensionFactory接口的实现类,如下:

722d82c7594e4a219fff2cad5c79314d.png

95debbb819674a4697e7aa6c42f689de.png

        由此可知,这两个实现类中,只有AdaptiveExtensionFactory是被@Adaptive注解修饰的。再回到ExtensionLoader#getAdaptiveExtensionClass()方法中,代码如下:

ebe0be33eb95485cb630545e80358786.png

        因此会返回AdaptiveExtensionFactory.class,再回到ExtensionLoader#createAdaptiveExtension()方法中,代码如下:

c586899fa0484a1caf1a86d1f0a0ce5e.png

        调用ExtensionLoader#injectExtension()方法,进行Dubbo的依赖注入,代码如下:
d7081a6eb2604d58b667e138bab34735.png

fd5e65b2e6b845668bacffed416a100d.png

        由于AdaptiveExtensionFactory中没有Setter方法,因此无需进行依赖注入,但是它的无参构造中,做了一些初始化处理,无参构造为:

873df9b2ce104ce299c58c77c18102c8.png

        需要看看ExtensionLoader#getSupportedExtensions()方法,代码如下:

951a665034ac43cdb7e2934065b2d58f.png

dfbac50821fe4cd5a03d6f2053971ed5.png

        这也是刚刚讲过的方法,最终只需要看ExtensionLoader#loadClass()方法,代码如下:

13fa0acbeaa743f58b883bc26d3d75dd.png

        因此AdaptiveExtensionFactory不会被放入AdaptiveExtensionFactory的factories属性中,而是ExtensionFactory接口的其他实现,分别在两个文件中,如下:

747fcc06d8f746789d44dca461d5dc98.png

4b9a73f5a50349fd8bfdc219a28741cf.png

        打断点验证一下,结果如下:

c7fa5b3c10f445e6bd1577907f3828eb.png

        这两个对象,即:SpringExtensionFactory和SpiExtensionFactory,将会是实现Dubbo的依赖注入的关键,看名字也能知道:前一个是通过Spring容器寻找依赖的对象;而后一个则是通过Dubbo的SPI机制寻找依赖的对象。当然,寻找依赖并不是直接通过这两个类来寻找的,而是通过AdaptiveExtensionFactory#getExtension()方法来寻找的,代码如下:

b5a1c3d57d8549e7b704d9a52d7c4b7c.png

f29424aeb9a6457dadb67c7032356183.png

        而SpringExtensionFactory的CONTEXTS属性,则是在ReferenceBean中或者ServiceBean总进行赋值的:

2224f12c13c144b4999bdd97d6071fa5.png

e651f132917242ea8d2f00df4b866ec8.png

     到这里为止,Dubbo的SPI机制大概讲清楚了,Dubbo的SPI剩余的内容,后面碰到再补充。想彻底搞懂这块,建议自己跟着源码,再配合博客一起看。OK,回到ApplicationModel#getConfigManager()方法传入的是 name,代码如下:

70b38bc08f9b41d698eb73d874de38dd.png

d0123574153841cdb853322fdc363d9c.png

2536f97635854fbd9091e5c808040837.png

        加载FrameworkExt接口实现类的逻辑跟ExtensionFactory类似,也是在classpath下,找文教名叫org.apache.dubbo.common.context.FrameworkExt的文件,如下:

c3a207ef253e457eb3450d46ecb5c8dd.png

        最终得到这三个实现类的Class的集合,打断点结果如下:

faff763e654d43018382f321b330c355.png

        回到ExtensionLoader#createExtension()方法中,代码如下:

abc0328e72e04d11880e430014cd039d.png
        通过name,即"config"可以获取到Class,即ConfigManager.class,然后通过EXTENSION_INSTANCES获取,由于是第一次,当然获取为空,因此进入if代码块,通过反射,实例化ConfigManager,并放入EXTENSION_INSTANCES中,其中key为"config",然后调用ExtensionLoader#injectExtension()方法,实际上就是调用AdaptiveExtensionFactory#getExtension()方法,寻找依赖,这块上文讲的很清楚了,这里不过多赘述。而ConfigManager有如下的Setter方法,代码如下:

77f19d7a75ef48bd8fb17e383489c85a.png

        这些Setter方法也不一定都会被调用,只有获取到了相应的对象,才会通过反射,进行赋值。需要说的是,给Setter方法赋值是不是一定通过Dubbo提供的依赖注入进行的赋值?这也不一定,可能在其他地方进行赋值,只不过Dubbo提供了这种方式。比如我打断点的时候,发现就不是通过Dubbo的方式进行的依赖注入,断点如下:

b79fc24f55c948ae99715b6a99f4fc5b.png

        OK,至于是哪种方式调用Setter方法进行赋值的,这里不过多纠结,这并非核心流程。回到最开始的地方,即AbstractConfig#addIntoConfigManager()方法,由于该方法被@PostConstruct注解修饰,因此会自动被调用,断点如下:

288cb1491b064e12ace13778887f8d0f.png

bb77f11bb67349249d8a5bffa7b20738.png

        在上篇博客中,讲到了往Spring容器中,添加了两个监听器,分别是:DubboBootstrapApplicationListener、DubboLifecycleComponentApplicationListener,这两个监听器会监听两种事件,分别是ContextRefreshedEvent、ContextClosedEvent。先看看DubboBootstrapApplicationListener,代码如下:

1ef79ab93c4a4d90bc886dda601cc019.png

2221e074073a41b5be4a958a09e2c1fb.png

        先看看DubboBootstrap#initialize()方法,代码如下:

f63335f6cfc943df8ba8fab43a929713.png

        在该方法中,又调用了七个方法,分别看看,代码如下:

① DubboBootstrap#initFrameworkExts()方法:

a93c371872c048deab0f70b9203b7bbb.png

f80d1d93225545e9b430f880d8328dd1.png

        处理默认的配置中心,这里没有配置,因此均为空:

72c1c6dd41544939809dde13d5a99ad5.png

② DubboBootstrap#startConfigCenter()方法:

9e0800bc6275476d8e9671ab20cceab7.png

        先看看我在DubboConfiguration配置的配置中心配置类ConfigCenterConfig,代码如下:

73997557584b404489b69bad107ffa43.png

        其中,group、configFile、namespace我都没有指定,将使用默认值,当然也可以种指定,默认值分别为:

4fa577865c664ab39482cf242626fc84.png

2f89230f846e4fed9daebf7b3b627001.png

080c2546478449f186817c9d3287e7e4.png

        然后我在Nacos上的配置为如下:

de1bfda192554f13b995f107f666667a.png

       dubbo.properties 内容为:

e94ac5ac9e874a9fa09093f3e86f5821.png

        目前我看的Dubbo版本应该是不支持yml/yaml的,继续往下看,重点看看DubboBootstrap#prepareEnvironment()方法,代码如下:

d3f13a94c879411c98301cacd62c2ff7.png

ff8c29114a054003bf9d69e2d806cdbb.png

3aaae5287acb46d286f7335fd6e0a9dd.png

        顺便提了下,为了实现配置中心的功能,我在pom文件中加了一个依赖:

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-configcenter-nacos</artifactId>
            <version>2.7.10</version>
        </dependency>

        因此,我应该在该模块中找一个文件名为org.apache.dubbo.configcenter.support.nacos.NacosDynamicConfigurationFactory,也确实让我找到了,如下:

58dd943252564f73917954c71c8029a3.png

        而它的名字也确实是nacos,符合预期,OK,回到代码继续往下看:

f8624ad168604e79b9a232560a5f8699.png

cc72391f738f439689df773fa41a9781.png

        地址是怎么拼接的,也简单,就不带大家看了,感兴趣的自己看看。再回到DubboBootstrap#prepareEnvironment()方法,看看DynamicConfiguration#getProperties(),代码如下:

e7a9f59472ab4564b3d14dc77d96f079.png

1fba77e8cef34aa9a3944e223d9719e7.png

0b231416f72d42b1adf2ce210c36848d.png

9d81184ad79a44d289718c3ab3ca0cc2.png

c14741acba204b82a1748387d9a634d6.png

2ce59ca96c144b959f914ebf9ad29dcb.png

b289f90e650d44b2a7c804e29721cada.png

        讲到这里就很清晰了,不必深究,因为后面我会专门写两篇博客,讲Nacos的注册中心源码和配置中心源码,敬请期待。再回到DubboBootstrap#prepareEnvironment()方法,打断点看看是否获取到了配置,如下:

791b1a3f9d814b5e8766e5fff4dbe005.png

        结果符合预期,剩下的就是解析获取到的内容,也就是调用ConfigurationUtils#parseProperties()方法,代码如下:

6a07433142be46478da53d7e6f519a37.png
        最后调用Environment#updateExternalConfigurationMap()方法,将解析后的配置,即Map,设置到Environment的externalConfigurationMap属性中。191ac4f3432e41f29f669c4dd07c1d02.png

并且我没有看到把这些属性设置到Spring的环境变量对象中去的代码,因此,很可能这些配置只能从Dubbo的环境变量对象获取到,但是无法从Spring的环境变量对象获取到对应的属性值,这个也可以打断点验证一下:

271510ad14704bc98cdf15d20e92f9f3.png

096cace4bbe7447d959c7c11d799bb9a.png

        确实如此,这是一个问题,还有一个问题是,如果我修改了dubbo.properties的内容,服务感知不到,需要自己实现(下文我做个示例,如何实现),这个就有点坑爹。当然我看的这个版本确实有些地方还不完善,后续版本肯定会做处理的,后面有空我再研究下Dubbo 3.X的版本,如果支持了,我在本篇博客中补充一下。至于感知Nacos配置中心是如何做到了,我也提一嘴,代码如下:

488fc739855e409db9291dc8a3f8690c.png

73db36b9f4dd4a9aafbf85c5f7ff305a.png

0f2a2b6889bc4e4ea96e83281e9bd21b.png

8035ac87e0c64ffdaa37be09291b16ab.png

        我在这里处理做个示例,代码如下:

8889c98c0583444a90c55712ac7baa92.png

        修改Nacos配置,看看结果:

9a5c41b69b6249d5840facec2061734e.png

2ee1a7578ab5491cb6e8c1cfc8080ac7.png

        从修改到感知需要花4~5s的样子,反正获取的是全量的配置。然后可以直接调用之前讲的ConfigurationUtils#parseProperties()方法解析配置,再调用Environment#updateExternalConfigurationMap()方法,更新配置即可。

        限于篇幅,剩下的内容将在下一篇博客中继续讲,敬请期待,如有错误,还望在留言中指正,感谢!

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

相关文章

Java并发—volatile关键字的作用及使用场景

在这篇文章Java并发—Java内存模型以及线程安全-CSDN博客多次提及volatile关键字&#xff0c;这是一个非常重要的概念&#xff0c;主要用于多线程编程中&#xff0c;它确保了变量的可见性和禁止指令重排序&#xff0c;但不保证原子性&#xff0c;下面详细解释volatile关键字的作…

使用SpringAOP实现公共字段填充

文章目录 概要整体架构流程技术细节小结 概要 在新增员工或者新增菜品分类时需要设置创建时间、创建人、修改时间、修改人等字段&#xff0c;在编辑员工或者编辑菜品分类时需要设置修改时间、修改人等字段。这些字段属于公共字段&#xff0c;也就是也就是在我们的系统中很多表…

Flux:Midjourney的新图像模型挑战者

--->更多内容&#xff0c;请移步“鲁班秘笈”&#xff01;&#xff01;<--- Black Forest Labs是一家由前Stability.ai开发人员创立的AI初创公司&#xff0c;旨在为图像和视频创建尖端的生成式 AI 模型。这家初创公司声称&#xff0c;其第一个模型系列Flux.1为文本到图像…

【FAQ】为啥MultipartFile 的InputStream available会为0

背景 在Spring boot 文件上传案例中可能会存在获取MultipartFile InputStream.available()方法为0的情况&#xff0c;导致在文件上传到Minio后对象大小为0的情况 问题原因 在介绍问题原因前我们先探究下MultipartFile 是怎么实现的 这里只是剖析InputStream&#xff0c;所以…

Linux驱动入门实验班day03-另一种注册cdev的方式

问题&#xff1a;原来的函数/*major register_chrdev(0, "100ask_hello", &hello_drv);*/会将主设备号major对应的所有次设备号&#xff0c;对应的设备节点&#xff0c;总是访问到驱动程序hello_drv。 这个问题&#xff0c;会导致主设备号不够用。 解决方式&am…

c++ - unordered_set与unordered_map模拟实现

文章目录 前言一、unordered_set模拟实现二、unordered_map模拟实现 前言 1、unordered_set与unordered_map的介绍与接口使用可参考&#xff1a;unordered_set 、 unordered_map。 2、unordered_set和 unordered_map 的底层实现都是基于哈希表的。哈希表是一种通过哈希函数组织…

HarmonyOS(48) 挂载卸载事件 UI组件的添加和删除监听

UI组件的添加和删除监听 一级目录示例代码参考资料 一级目录 我们通过if条件添加组件的时候&#xff0c;是可以通过onAttach、onDetach、onAppear、onDisAppear来监听组件的添加和删除。 示例代码 // xxx.ets// xxx.ets import { promptAction } from kit.ArkUIEntry Compo…

2024华数杯数学建模A题完整论文讲解(含每一问python代码+结果+可视化图)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024 年华数杯全国大学生数学建模竞赛A题机器臂关节角路径的优化设计完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成…

VBA信息获取与处理:VBA代码分类及如何利用代码自动关闭空闲文件

《VBA信息获取与处理》教程(版权10178984)是我推出第六套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。这部教程给大家讲解的内容有&#xff1a;跨应用程序信息获得、随机信息的利用、电子邮件的发送、VBA互…

LC65---2164.对奇偶下标分别排序(排序)--Java版

1.题目 2.思路 &#xff08;1&#xff09;分别提取奇数下标和偶数下标的元素。 &#xff08;2&#xff09;对奇数下标的元素按非递增顺序排序&#xff0c;对偶数下标的元素按非递减顺序排序。 (3)最后将排列好的数字进行合并。 补充&#xff1a; 3.代码实现 class Solution…

PyCharm 2024.1 总结和最新变化

​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 PyCharm 2024.1 是 JetBrains 最新发布的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在提供更强大的功能和更好的用户体验。以下是对这个版本的总结和最新变化的介绍 智能代码建议和自动完成&#xff1a…

C语言 ——— 学习并使用 strerror 函数

目录 学习strerror函数 使用strerror函数 学习strerror函数 库函数在执行的时候&#xff0c;发生了错误&#xff0c;会将这个错误码存放在errno这个变量中&#xff0c;而errno是C语言提供的一个全局变量 而strerror函数是一个错误报告函数&#xff0c;可以将对应的错误码转…

roomformer-端到端矢量检测模型

论文&#xff1a;Connecting the Dots: Floorplan Reconstruction Using Two-Level Queries 论文地址&#xff1a;https://arxiv.org/pdf/2211.15658 code&#xff1a;https://github.com/ywyue/RoomFormer or https://github.com/woodfrog/poly-diffuse 参考&#xff1a;ht…

指针的指针作为形参实测

1. VS2019里面创建C控制台工程 2. 代码 #include <iostream>uint8_t buf[3][10] { {1,2,3},{4,5,6,7,8},{9,0} }; uint8_t len1 3,len2 5,len3 2;void f1(uint8_t **dstBuf, uint8_t *dstLen) {*dstBuf buf[0];*dstLen len1; }void f2(uint8_t** dstBuf, uint8_t*…

密码学基础-数据加密

密码学基础-对称加密与非对称加密 概述 安全通常从四个方面来定义&#xff1a; 机密性完整性合法性&#xff08;可用性&#xff0c;合法的数据才可用&#xff09;不可否认性&#xff08;发送方不可否认发送过的消息&#xff0c;接收方不可否认接收过的消息&#xff09; 对当…

低代码: 开发难点分析,核心技术架构设计

开发难点分析 1 &#xff09;怎样实现组件 核心问题&#xff1a;编辑器 和 页面其实整个就是一系列元素构成的这些元素的自然应该抽象成组件&#xff0c;这些组件的属性应该怎样设计在不同的项目中怎样做到统一的使用 2 &#xff09;跨项目使用 在不同的项目中怎样做到统一的…

最强开源文生图模型一夜易主!SD一作、Stabililty AI核心成员Robin Rombach下场创业了,一出手就是王炸。

时隔4个月&#xff0c;开源文生图模型霸主Stable Diffusion原班人马再创业&#xff01;2024年8月1日官宣&#xff1a;Black Forest Labs成立&#xff0c;公司的第一个产品FLUX.1系列模型包含专业版、开发者版、快速版三种模型&#xff0c;效果直接秒杀Midjourney、DALL-E和Stab…

解决报错:AssertionError: Torch not compiled with CUDA enabled

首先查看自己的cuda是否可用 torch.cuda.is_available()这里我的cuda是不适配torch的&#xff0c;所以需要重新安装适配的torch 查看自己的cuda版本 方法1 方法2 在cmd处输入nvidia-smi 这样可以找到的自己的CUDA版本安装符合自己版本的pytorch 进入pytorch官网https://pyt…

双指针实现删除字符串中的所有相邻重复项

class Solution:def removeDuplicates(self, s: str) -> str:res list(s)slow fast 0length len(res)while fast < length:# 如果一样直接换&#xff0c;不一样会把后面的填在slow的位置res[slow] res[fast]# 如果发现和前一个一样&#xff0c;就退一格指针if slow …

app逆向实战:某监管app2.0.5版本ROOT检测绕过

本篇博客旨在记录学习过程&#xff0c;不可用于商用等其它途径 场景 如下图&#xff0c;在我们打开APP时页面提示如此样式说明被检测到ROOT了&#xff0c;这种情况下无法进入页面请求抓包。 查壳 如果这个APP没有加固&#xff0c;那我们可以通过反编译修改检测的代码或者F…