Dubbo源码解析之@DubboService、@DubboReference(Dubbo源码一)

news2024/9/20 1:05:34

更好的阅读体验:Dubbo源码解析之@DubboService、@DubboReference(Dubbo源码一)
视频讲解:https://www.bilibili.com/video/BV1nBsMejEbL



对于Dubbo用的最多的就是@DubboService、@DubboReference,与之对应的就是服务的提供方、调用方。

之所以加上注解就可以运行,定是生成了代理对象,这篇文章就来讲讲如何基于这两个注解生成代理对象。


不管是服务端还是客户端,在使用Dubbo的时候都会先使用@EnableDubbo,比如下面的demo

@SpringBootApplication
@EnableDubbo
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

@EnableDubbo 是一个组合注解,它头上还有@DubboComponentScan和@EnableDubboConfig,它们就是分别来解析@DubboService、@DubboReference


EnableDubbo

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {}

DubboComponentScan

@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {}

EnableDubboConfig

@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {}

一、@DubboReference 注解解析


DubboConfigConfigurationRegistrar 里面注入了一个bean处理器 ReferenceAnnotationBeanPostProcessor,这个处理器会把带有下面三个注解的bean代理成一个 ReferenceBean

  • DubboReference
  • Reference
  • com.alibaba.dubbo.config.annotation.Reference

1、入口


入口跳的很多,全部把代码复制过来阅读体验不好,也全无必要,这里给出每个方法的关键代码

EnableDubbo

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {}

EnableDubboConfig

@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {}

DubboConfigConfigurationRegistrar

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // initialize dubbo beans
        DubboSpringInitializer.initialize(registry);
    }
}

DubboSpringInitializer

public static void initialize(BeanDefinitionRegistry registry) {
    // ...
    // init dubbo context
    initContext(context, registry, beanFactory);
}

private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
                                ConfigurableListableBeanFactory beanFactory) {
    // ...
    // register common beans
    DubboBeanUtils.registerCommonBeans(registry);
}

DubboBeanUtils

static void registerCommonBeans(BeanDefinitionRegistry registry) {
    // ...
    // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
    registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
        ReferenceAnnotationBeanPostProcessor.class);
    // ...
}

2、bean处理器


所谓bean的后置处理器,其实就是在bean完成初始化之后,就会调用这个方法,给你所有的bean,然后你就可以对这个bean为所欲为了

bean处理器有很多,有前置处理器、后置处理器、bean工厂处理器


2-1、前、后置处理器

只需要实现BeanPostProcessor 接口,重写里面的前置、后置处理器就好了,系统会把每个bean作为参数去调用这个方法

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

2-2、bean工厂处理器

上面的前后置处理器,是系统拿到bean去调用这个方法,bean工厂处理器直接把bean工厂给你

public interface BeanFactoryPostProcessor {

	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

3、循环所有的bean,找到使用注解的bean生成代理对象


ReferenceAnnotationBeanPostProcessor实现了BeanFactoryPostProcessor,重写了postProcessBeanFactory,在这个方法里面对使用了注解的bean进行代理

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    // 遍历所有的bean
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        Class<?> beanType;
        // 判断是不是工厂bean,显然我们不是
        if (beanFactory.isFactoryBean(beanName)) {
            // ...
        } else {
            // 走这个、拿到class
            beanType = beanFactory.getType(beanName);
        }
        if (beanType != null) {
            // 找到需要被代理的 metadata
            AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
            try {
                // 进行代理生成
                prepareInjection(metadata);
            } catch (BeansException e) {
                throw e;
            } catch (Exception e) {
                throw new IllegalStateException("Prepare dubbo reference injection element failed", e);
            }
        }
    }
}

4、找到合适的bean


匹配逻辑,其实就是拿每一个Class 上面的注解,看看是否和DubboReference相关注解匹配,下面代码给出大概步骤,忽略一些判断逻辑

setp 1
protected AnnotatedInjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    // ...
    try {
        // 走这里构建
        metadata = buildAnnotatedMetadata(clazz);
        this.injectionMetadataCache.put(cacheKey, metadata);
    } catch (NoClassDefFoundError err) {
        throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
            "] for annotation metadata: could not find class that it depends on", err);
    } 
    // ...
    return metadata;
}
setp 2
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
    // 这个方法就会找到合适匹配的bean
    Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
    Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
    return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
setp 3
private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {

    final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<>();
    ReflectionUtils.doWithFields(beanClass, field -> {
        for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
            AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes(field, annotationType, getEnvironment(), true, true);
            if (attributes != null) {
                if (Modifier.isStatic(field.getModifiers())) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "@" + annotationType.getName() + " is not supported on static fields: " + field);
                    }
                    return;
                }
                elements.add(new AnnotatedFieldElement(field, attributes));
            }
        }
    });
    return elements;
}

getAnnotationTypes 就是在初始化的时候Set进去的

public ReferenceAnnotationBeanPostProcessor() {
    super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}

public AbstractAnnotationBeanPostProcessor(Class<? extends Annotation>... annotationTypes) {
    Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
    this.annotationTypes = annotationTypes;
}

5、注册 ReferenceBean类型的 beanDefinition


让我们再回到 ReferenceAnnotationBeanPostProcessor#postProcessBeanFactory 在这个方法里面循环每一个bean看它是否用到了 DubboReference 相关注解,如果有就进行代理对象

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        // ...
        if (beanType != null) {
            // 找到满足代理的bean,上一节已经讲了
            AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
            try {
                // 进行代理
                prepareInjection(metadata);
            }
        }
        // ... 
    }
}

protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException {
    try {
        //find and register bean definition for @DubboReference/@Reference
        for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
            if (fieldElement.injectedObject != null) {
                continue;
            }
            Class<?> injectedType = fieldElement.field.getType();
            AnnotationAttributes attributes = fieldElement.attributes;
            // 注册代理bean
            String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field);

            //associate fieldElement and reference bean
            fieldElement.injectedObject = referenceBeanName;
            injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName);

        }
        // ...
    } catch (ClassNotFoundException e) {
        throw new BeanCreationException("prepare reference annotation failed", e);
    }
}

registerReferenceBean 方法很长,前面有一系列的校验和缓存处理,这里为了简便,只给出后面注册 beanDefinition 部分

public String registerReferenceBean(String propertyName, Class<?> injectedType, Map<String, Object> attributes, Member member) throws BeansException {

    // ... 省略一大段校验缓存逻辑

    // Register the reference bean definition to the beanFactory
    RootBeanDefinition beanDefinition = new RootBeanDefinition();

    // 看看看看,找到了,注册的这个 beanDefinition ,是 ReferenceBean.class
    beanDefinition.setBeanClassName(ReferenceBean.class.getName());

    // ... 省略赋值的逻辑
    
    // 注册BeanDefinition, 被注册的 BeanDefinition,会生成一个对象
    beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);

    
    logger.info("Register dubbo reference bean: " + referenceBeanName + " = " + referenceKey + " at " + member);
    return referenceBeanName;
}

6、生成代理对象 ReferenceBean


ReferenceBean 是一个FactoryBean,所以在需要这个bean的时候会调用getObject 获取bean

ReferenceBean<T> implements FactoryBean<T>,ApplicationContextAware, BeanClassLoaderAware, BeanNameAware, InitializingBean, DisposableBean 
@Override
public T getObject() {
    if (lazyProxy == null) {
        createLazyProxy();
    }
    return (T) lazyProxy;
}

private void createLazyProxy() {

    // ....
    // 这里默认走的就是Javassist代理
    if (StringUtils.isEmpty(this.proxy) || CommonConstants.DEFAULT_PROXY.equalsIgnoreCase(this.proxy)) {
        generateFromJavassistFirst(interfaces);
    }

    if (this.lazyProxy == null) {
        generateFromJdk(interfaces);
    }
}

好啦,到这里代理对象就生成完毕了,这里看到它在代理对象里面注册了一个 LazyTargetInvocationHandler,它本质上是一个 InvocationHandler,所以当执行这个代理对象的时候就会执行org.apache.LazyTargetInvocationHandler#invoke 这就行下一期要讲的,运行原理

private void generateFromJavassistFirst(List<Class<?>> interfaces) {
    try {
        this.lazyProxy = Proxy.getProxy(interfaces.toArray(new Class[0])).newInstance(new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource()));
    } catch (Throwable fromJavassist) {
       // ...
    }
}

二、@DubboService 注解解析


相较于@DubboReference,@DubboService的解析要复杂不少,先是扫描到带有注解的实体,生成 ServiceBean,再基于ServiceBean生成CallbackRegistrationInvoker对象


1、入口


@EnableDubbo > @DubboComponentScan > DubboComponentScanRegistrar.class

DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar,重写了registerBeanDefinitions,这个方法里面就注入了Service处理器,通过扫描包下面使用相关注解的bean

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    // initialize dubbo beans
    DubboSpringInitializer.initialize(registry);

    Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

    // 注册Service处理器
    registerServiceAnnotationPostProcessor(packagesToScan, registry);
}

2、bean 处理器


通过扫描指定包下的bean来筛选是否满足生成 ServiceBean

org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationPostProcessor#postProcessBeanDefinitionRegistry

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    this.registry = registry;
    scanServiceBeans(resolvedPackagesToScan, registry);
}

scanServiceBeans

看看这里把 serviceAnnotationTypes 装进去了,下面判断的时候会用到

private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

    // 循环每个包
    for (String packageToScan : packagesToScan) {
        DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

        // 🌈🌈🌈🌈构建需要包含哪些bean, serviceAnnotationTypes 就是上面定义的注解
        for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        }

        // 扫描每个包
        scanner.scan(packageToScan);

        // 找到合适的 BeanDefinition
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        // 循环每一个合适的 BeanDefinition
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
            // 生成 ServiceBean
            processScannedBeanDefinition(beanDefinitionHolder);
            servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
        }

        servicePackagesHolder.addScannedPackage(packageToScan);
    }
}

serviceAnnotationTypes

private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
        // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
        DubboService.class,
        // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
        Service.class,
        // @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
        com.alibaba.dubbo.config.annotation.Service.class
);

3、找到合适的 BeanDefinition


这里寻找的过程有一点绕,还是和之前一起,给出每个关键节点的关键代码

本质上就是扫描包下面的BeanDefinition看看是否有包含 @DubboService相关注解


findServiceBeanDefinitionHolders

org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationPostProcessor#findServiceBeanDefinitionHolders

private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(
        ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,
        BeanNameGenerator beanNameGenerator) {

    Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);

    Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());

    for (BeanDefinition beanDefinition : beanDefinitions) {

        String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
        BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
        beanDefinitionHolders.add(beanDefinitionHolder);

    }

    return beanDefinitionHolders;

}

findCandidateComponents

org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner#findCandidateComponents

@Override
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> beanDefinitions = beanDefinitionMap.get(basePackage);
    // if beanDefinitions size is null => scan
    if (Objects.isNull(beanDefinitions)) {
        beanDefinitions = super.findCandidateComponents(basePackage);
        beanDefinitionMap.put(basePackage, beanDefinitions);
    }
    return beanDefinitions;
}

scanCandidateComponents

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet();
    // ...
    
    MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
    if (this.isCandidateComponent(metadataReader)) {
        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
        
        // 判断当前bean是不是满足
        if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
            if (debugEnabled) {
                this.logger.debug("Identified candidate component class: " + resource);
            }

            candidates.add(sbd);
        } else if (debugEnabled) {
            this.logger.debug("Ignored because not a concrete top-level class: " + resource);
        }
    } 
    // ...
    return candidates;
}

isCandidateComponent

includeFilters 数据就是在scanServiceBeans 里面设置进去的

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(org.springframework.core.type.classreading.MetadataReader)

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    Iterator var2 = this.excludeFilters.iterator();

    TypeFilter tf;
    do {
        if (!var2.hasNext()) {
            var2 = this.includeFilters.iterator();

            do {
                if (!var2.hasNext()) {
                    return false;
                }

                tf = (TypeFilter)var2.next();
            } while(!tf.match(metadataReader, this.getMetadataReaderFactory()));

            return this.isConditionMatch(metadataReader);
        }

        tf = (TypeFilter)var2.next();
    } while(!tf.match(metadataReader, this.getMetadataReaderFactory()));

    return false;
}

4、生成ServiceBean


org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationPostProcessor#processScannedBeanDefinition


private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) {

    Class<?> beanClass = resolveClass(beanDefinitionHolder);

    Annotation service = findServiceAnnotation(beanClass);

    // The attributes of @Service annotation
    Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);

    String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);

    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

    // ServiceBean Bean name
    String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);

    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);

    registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);

}

生成beanName

private String generateServiceBeanName(Map<String, Object> serviceAnnotationAttributes, String serviceInterface) {
    ServiceBeanNameBuilder builder = create(serviceInterface, environment)
            .group((String) serviceAnnotationAttributes.get("group"))
            .version((String) serviceAnnotationAttributes.get("version"));
    return builder.build();
}

构建bean参数

定义 BeanDefinition 的class,并且解析注解上的参数,放入BeanDefinition

private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
                                                            String serviceInterface,
                                                            String refServiceBeanName) {
    // 设置class                                                            
    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
    beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

    MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

    String[] ignoreAttributeNames = ObjectUtils.of("provider", "monitor", "application", "module", "registry", "protocol",
            "methods", "interfaceName", "parameters", "executor");

    propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotationAttributes, environment, ignoreAttributeNames));

    addPropertyReference(builder, "ref", refServiceBeanName);
    // Set interface
    builder.addPropertyValue("interface", serviceInterface);

    // ... 设置各种参数

    return builder.getBeanDefinition();
}

注册到 BeanDefinitionRegistry

private void registerServiceBeanDefinition(String serviceBeanName, AbstractBeanDefinition serviceBeanDefinition, String serviceInterface) {
    // check service bean
    // ...

    registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition);
    
    // ...
}

5、ServiceBean


一般生成代理对象,都会有一个 invoker方法,但ServiceBean没有,这是因为ServiceBean 只是存储DubboService的相关信息,后面还会生成一个真正invoker。


先来看一下ServiceBean的继承关系

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware {}

真正的invoker是在 ServiceConfig中生成的


ServiceBean 重写了 afterPropertiesSet ,把自己注册到了moduleConfigManager(这个后面会看到)
@Override
public void afterPropertiesSet() throws Exception {
    if (StringUtils.isEmpty(getPath())) {
        if (StringUtils.isNotEmpty(getInterface())) {
            setPath(getInterface());
        }
    }
    //register service bean
    ModuleModel moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
    moduleModel.getConfigManager().addService(this);
    moduleModel.getDeployer().setPending();
}

6、invoker生成


Dubbo注册了一个事件监听器,这个事件监听器会做很多初始化的工作,就会到生成Invoker的地方,这里的调用链路太长了,给一个Debug的图

在这里插入图片描述


最终会到当前类的org.apache.dubbo.config.ServiceConfig#doExportUrl方法

private void doExportUrl(URL url, boolean withMetaData, RegisterTypeEnum registerType) {
    if (!url.getParameter(REGISTER_KEY, true)) {
        registerType = RegisterTypeEnum.MANUAL_REGISTER;
    }
    if (registerType == RegisterTypeEnum.NEVER_REGISTER ||
        registerType == RegisterTypeEnum.MANUAL_REGISTER ||
        registerType == RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER) {
        url = url.addParameter(REGISTER_KEY, false);
    }

    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    if (withMetaData) {
        invoker = new DelegateProviderMetaDataInvoker(invoker, this);
    }
    Exporter<?> exporter = protocolSPI.export(invoker);
    exporters.computeIfAbsent(registerType, k -> new CopyOnWriteArrayList<>()).add(exporter);
}

interfaceClass 取的是刚刚解析ServiceBean来的,也就是使用DubboService注解的类,invoker 返回的是Java生成的代理对象,Dubbo会再把它包装一层,所谓的包装就是用 Filter去包它,一层层的。最终生成 CallbackRegistrationInvoker。


在这里插入图片描述


withMetaData: 每一个使用DubboService注解的对象都会生成两个代理对象,一个正常的代理对象,用来执行服务,一个 MetaData 类型的代理对象,用来维护基础数据


后续Netty收到请求,就会转而找到对应的 Invoker。

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

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

相关文章

diskgenuis打开vmdk文件提示读扇区错误

使用DiskGenius_V5_2_x86打开iKuai-0.vmdk 读扇区错误&#xff01; 磁盘: 起始于 0 扇区 共 1 个扇区。 (Err:0) 读扇区错误&#xff01; 磁盘: VD0:iKuai-0.vmdk(2GB) 起始于 0 扇区 共 2 个扇区。 (Err:0) 更换官网最新版本 https://www.diskgenius.cn/download.php 可…

一个PPT做3天?有这3个AI做PPT工具再也不发愁!

ppt是什么东西&#xff1f; ppt是英文Powerpoint的缩写&#xff0c;它是微软出品的演示文稿软件&#xff0c;因为其在演示领域的领导地位&#xff0c;也让它成为幻灯片或演示文稿的代名词。 一个完整的ppt&#xff0c;通常包含很多要素&#xff0c;如ppt封面页、ppt目录页、p…

解决Java中Long类型的序列化与JDK8时间的序列化

1.背景 SpringBoot与前端交互 这个场景下Long类型与前端JS交互时会存在精度丢失&#xff0c;根本原因是JS的Number支持不到19位&#xff0c;所以需要后端将Long类型的值转为StringJDK8的时间类 JDK8的时间类比如LocalDateTime、LocalDate 在交互时序列化默认格式是 “yyyy-MM-…

LLM 应用开发入门 - 实现 langchain.js ChatModel 接入火山引擎大模型和实现一个 CLI 聊天机器人(上)

前言 Langchain 是一个大语言模型(LLM)应用开发的框架,提供了 LLM 开发中各个阶段很多非常强大的辅助工具支持。对于进行 LLM 开发是必不可少的工具库。 本文将通过一个实际的开发例子来入门 LLM 开发基础工具链,并实现 langchain.js ChatModel 接入火山引擎大模型和基于…

【Python入门】第4节 函数

&#x1f4d6;第4节 函数 ✅函数介绍✅函数的定义✅函数的参数✅函数的返回值&#x1f9ca;None类型的应用场景 ✅函数说明文档✅函数的嵌套调用✅变量的作用域 ✅函数介绍 函数是&#xff1a; 组织好的、可重复使用的、用来实现特定功能的代码段使用函数的好处是&#xff1a;…

erlang学习:用OTP构建系统1

书上案例学习并测试 23.1 通用事件处理 -module(event_handler). %% API -export([make/1, add_handler/2, event/2]).%% 制作一个“什么都不干”的事件处理器Name&#xff08;一个原子&#xff09;。这样消息就有地方发送了。 make(Name) ->register(Name, spawn(fun() -…

SpringSpring搭建SpringJdbcTemplateSpring Bean管理Spring结合Mybatis

Spring基础 Spring是什么 Spring是一个轻量级的IOC和AOP的一站式Java开发框架,是为了简化企业级开发而生的 轻量级 框架体积小(核心模块) IOC Inversion Of Control (控制反转)缩写为IOC,是由Spring管理对象,而非传统实现中由程序代码直接操控,将创建对象的控制权反转给s…

8月28日

思维导图 作业&#xff1a; 使用C手动封装一个顺序表&#xff0c;包含成员数组一个&#xff0c;成员变量N个 代码&#xff1a; #include <iostream>using namespace std;using datatype int; #define MAX 30struct SeqList {private:datatype *data;int size 0;int l…

Kafka3.x 使用 KRaft 模式部署 不依赖 ZooKeeper

前言 Kafka 从 2.8.0 版本开始引入了 Kafka Raft Metadata Mode&#xff08;KRaft 模式&#xff09;&#xff0c;这个模式允许 Kafka 在不依赖 ZooKeeper 的情况下进行元数据管理。KRaft 模式在 Kafka 3.0.0 中进入了稳定版本,本文部署的 Kafka_2.12-3.6.0 单机模式 环境 Ce…

Android插件化技术之加载未安装APK

目录 1、概述2、HOOK技术2.1、根据Android9.0系统源码&#xff0c;查看Activity.startActivity(intent,...)的调用流程2.2、根据Android9.0系统源码&#xff0c;查看Context.startActivity(intent,...)的调用流程 3、最终解决方案3.1、实现思路3.2、演示效果 4、最终代码实现4.…

Codeforces Round 968 (Div. 2)

前言 掉大分的一场比赛aaa 原因是 D1 看错条件了一直在想 D2 最后还没想出来。 Standings&#xff1a;6922 题目链接&#xff1a;Dashboard - Codeforces Round 968 (Div. 2) - Codeforces A. Turtle and Good Strings 题意&#xff1a; 给一个字符串&#xff0c;判断是否能把…

AWS CodeCommit 停服,欢迎大家使用极狐GitLab!

2024 年 7 月 25 日&#xff0c;AWS 官方发布公告称&#xff0c;旗下的代码托管服务 AWS CodeCommit 不再接受新用户的注册。这也就意味着用户不得不选择其他同类型产品。 极狐GitLab 为 GitLab 的中国发行版&#xff0c;可以一键私有化部署&#xff0c;详情可查看官网指南。 A…

超实用的8个无版权、免费、高清图片素材网站整理

不管是设计、文章配图&#xff0c;还是视频制作&#xff0c;图片都至关重要。但是图片版权一直都是困扰很多设计、自媒体以及企业的大问题。现在&#xff0c;因为图片侵权被告的案例已经是司空见惯了&#xff0c;有的公众号甚至因为图片版权问题遭受致命打击。 1. Pexels Pexe…

2 Python开发工具:PyCharm的安装和使用

本文是 Python 系列教程第 2 篇&#xff0c;完整系列请查看 Python 专栏。 1 安装 官网下载地址https://www.jetbrains.com.cn/pycharm/&#xff0c;文件比较大&#xff08;约861MB&#xff09;请耐心等待 双击exe安装 安装成功后会有一个30天的试用期。。。本来想放鸡火教程&…

Elastic日志分析

目录 介绍步骤 介绍 Elasticsearch 是在 Apache Lucene 上构建的分布式搜索和分析引擎。Elasticsearch常用于日志分析、全文搜索、安全智能、业务分析和运维智能使用案例。 可以使用 JSON 文档形式或通过 API 等将数据发送到 Elasticsearch。 Elasticsearch 自动存储原始文档…

免杀笔记 ---> CS特性角度看Veh免杀

前一段时间在玩WBGlIl大佬以前发的一篇过卡巴的思路&#xff08;虽然现在不过了&#xff09;&#xff0c;但是在研究的时候发现如果我们我们在没有CS的特性基础下直接看这篇文章&#xff0c;或者说你去魔改他的脚本是不太可能的&#xff0c;刚好就来普及一下这个CS的一些简单特…

胃癌TMEscore的前瞻性临床研究(TME)

目录 ①关于胃癌TME分型介绍 ②TMEscore计算-TMEscore包 ③关于TMEscore的前瞻性研究 ①关于胃癌TME分型介绍 Tumor Microenvironment Characterization in Gastric Cancer Identifies Prognostic and Immunotherapeutically Relevant Gene Signatures - PubMed (nih.gov) …

【Linux —— POSIX信号量 - 基于环形队列的生产消费模型】

Linux —— POSIX信号量 - 基于环形队列的生产消费模型 POSIX信号量信号量的概念POSIX信号量的类型信号量的操作 POSIX信号量函数基于环形队列的生产消费模型设计思路同步和安全性代码 POSIX信号量 信号量的概念 POSIX信号量是一种用于进程和线程之间同步的机制&#xff0c;主…

【网络】网络层协议——IP协议

目录 1.TCP和IP的关系 2.IP协议报文 2.1. 4位首部长度&#xff0c;16位总长度&#xff0c;8位协议 2.2. 8位生存时间 &#xff0c;32位源IP地址和32位目的IP地址 3.IP地址的划分 3.1.IP地址的表现形式 3.2.旧版IP地址的划分 3.2.1.旧版IP地址的划分思路 3.2.2.分类划…

各种注意力评分函数的实现

预备知识 本文基于MXNet进行实现&#xff0c;需要对于注意力机制有一定初步了解。也需要对Python有足够了解。 另外这里稍加说明&#xff0c;在注意力机制中&#xff0c;本质上是“注意”的位置&#xff0c;即加权计算后进行Softmax回归的结果。在Nadaraya-Watson核回归中&am…