nacos源码 nacos注册中心1.4.x 源码 spring cloud alibaba 的discovery做了什么 nacos客户端是如何启动的(二)

news2024/11/18 8:48:08
spring-cloud-alibaba-nacos-discovery 老版本中如何调用nacos的

 1. 整体结构

         

2. 思考: 如果你来做,如何做client 向server注册服务:

           1.2.1 读yml,或本地文件找到服务器地址,以及其他配置

           1.2.2 向server注册服务

           1.2.3 定期发送心跳

3. 源码跟踪

     3.1查看spring 自动装配 spring.factories:

     3.2 截图:

     3.3 查看NacosDiscoveryAutoConfiguration类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.alibaba.cloud.nacos;

import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnClass(
    name = {"org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent"}
)
@ConditionalOnProperty(
    value = {"spring.cloud.service-registry.auto-registration.enabled"},
    matchIfMissing = true
)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class})
public class NacosDiscoveryAutoConfiguration {
    public NacosDiscoveryAutoConfiguration() {
    }

    @Bean
    public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        return new NacosServiceRegistry(nacosDiscoveryProperties);
    }

    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosRegistration nacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
        return new NacosRegistration(nacosDiscoveryProperties, context);
    }

    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
    }
}

    3.3.1 注: 可看出重点是三个@Bean 注解的方法,三个对象放入spring 进入管理,我们需要依次查看三个对象,这里就不一一查看了,重点是NacosAutoServiceRegistration.class 。或者说入口是在NacosAutoServiceRegistration, 然后再看另外两个对象

    3.4.  NacosAutoServiceRegistration:

       3.4.1 源码:

public class NacosAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {
    private static final Logger log = LoggerFactory.getLogger(NacosAutoServiceRegistration.class);
    private NacosRegistration registration;

    public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        super(serviceRegistry, autoServiceRegistrationProperties);
        this.registration = registration;
    }

    /** @deprecated */
    @Deprecated
    public void setPort(int port) {
        this.getPort().set(port);
    }

    protected NacosRegistration getRegistration() {
        if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
            this.registration.setPort(this.getPort().get());
        }

        Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
        return this.registration;
    }

    protected NacosRegistration getManagementRegistration() {
        return null;
    }

    protected void register() {
        if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
            log.debug("Registration disabled.");
        } else {
            if (this.registration.getPort() < 0) {
                this.registration.setPort(this.getPort().get());
            }

            super.register();
        }
    }

    protected void registerManagement() {
        if (this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
            super.registerManagement();
        }
    }

    protected int getConfiguredPort() {
        return this.getPort().get();
    }

    protected void setConfiguredPort(int port) {
        this.getPort().set(port);
    }

    protected Object getConfiguration() {
        return this.registration.getNacosDiscoveryProperties();
    }

    protected boolean isEnabled() {
        return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
    }

    protected String getAppName() {
        String appName = this.registration.getNacosDiscoveryProperties().getService();
        return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
    }
}

      3.4.2 没有发现如何启动,继续查看父类:

      3.4.2.1 源码:

package org.springframework.cloud.client.serviceregistry;

import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;

public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration {
    private final ServiceRegistry<R> serviceRegistry;
    private AutoServiceRegistrationProperties properties;

    /** @deprecated */
    @Deprecated
    protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
        this.serviceRegistry = serviceRegistry;
        this.properties = properties;
    }

    protected ServiceRegistry<R> getServiceRegistry() {
        return this.serviceRegistry;
    }

    protected abstract R getRegistration();

    protected abstract R getManagementRegistration();

    protected void register() {
        this.serviceRegistry.register(this.getRegistration());
    }

    protected void registerManagement() {
        R registration = this.getManagementRegistration();
        if (registration != null) {
            this.serviceRegistry.register(registration);
        }

    }

    protected void deregister() {
        this.serviceRegistry.deregister(this.getRegistration());
    }

    protected void deregisterManagement() {
        R registration = this.getManagementRegistration();
        if (registration != null) {
            this.serviceRegistry.deregister(registration);
        }

    }

    public void stop() {
        if (this.getRunning().compareAndSet(true, false) && this.isEnabled()) {
            this.deregister();
            if (this.shouldRegisterManagement()) {
                this.deregisterManagement();
            }

            this.serviceRegistry.close();
        }

    }

    protected boolean shouldRegisterManagement() {
        return this.properties != null && !this.properties.isRegisterManagement() ? false : super.shouldRegisterManagement();
    }
}

      3.4.2.2 注:

                 注意此时包名换了。现在是spring框架源码,并且继承了AbstractDiscoveryLifecycle

      3.5 查看 AbstractDiscoveryLifecycle

        3.5.1 AbstractDiscoveryLifecycle源码:

@Deprecated
public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> {
    private static final Log logger = LogFactory.getLog(AbstractDiscoveryLifecycle.class);
    private boolean autoStartup = true;
    private AtomicBoolean running = new AtomicBoolean(false);
    private int order = 0;
    private ApplicationContext context;
    private Environment environment;
    private AtomicInteger port = new AtomicInteger(0);

    public AbstractDiscoveryLifecycle() {
    }

    protected ApplicationContext getContext() {
        return this.context;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
        this.environment = this.context.getEnvironment();
    }

    /** @deprecated */
    @Deprecated
    protected Environment getEnvironment() {
        return this.environment;
    }

    /** @deprecated */
    @Deprecated
    protected AtomicInteger getPort() {
        return this.port;
    }

    public boolean isAutoStartup() {
        return this.autoStartup;
    }

    public void stop(Runnable callback) {
        try {
            this.stop();
        } catch (Exception var3) {
            logger.error("A problem occurred attempting to stop discovery lifecycle", var3);
        }

        callback.run();
    }

    public void start() {
        if (!this.isEnabled()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Discovery Lifecycle disabled. Not starting");
            }

        } else {
            if (this.port.get() != 0 && this.getConfiguredPort() == 0) {
                this.setConfiguredPort(this.port.get());
            }

            if (!this.running.get() && this.getConfiguredPort() > 0) {
                this.register();
                if (this.shouldRegisterManagement()) {
                    this.registerManagement();
                }

                this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
                this.running.compareAndSet(false, true);
            }

        }
    }

    /** @deprecated */
    @Deprecated
    protected abstract int getConfiguredPort();

    /** @deprecated */
    @Deprecated
    protected abstract void setConfiguredPort(int var1);

    protected boolean shouldRegisterManagement() {
        return this.getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);
    }

    /** @deprecated */
    @Deprecated
    protected abstract Object getConfiguration();

    protected abstract void register();

    protected void registerManagement() {
    }

    protected abstract void deregister();

    protected void deregisterManagement() {
    }

    protected abstract boolean isEnabled();

    /** @deprecated */
    @Deprecated
    protected String getManagementServiceId() {
        return this.context.getId() + ":management";
    }

    /** @deprecated */
    @Deprecated
    protected String getManagementServiceName() {
        return this.getAppName() + ":management";
    }

    /** @deprecated */
    @Deprecated
    protected Integer getManagementPort() {
        return ManagementServerPortUtils.getPort(this.context);
    }

    /** @deprecated */
    @Deprecated
    protected String getAppName() {
        return this.environment.getProperty("spring.application.name", "application");
    }

    public void stop() {
        if (this.running.compareAndSet(true, false) && this.isEnabled()) {
            this.deregister();
            if (this.shouldRegisterManagement()) {
                this.deregisterManagement();
            }
        }

    }

    @PreDestroy
    public void destroy() {
        this.stop();
    }

    public boolean isRunning() {
        return this.running.get();
    }

    protected AtomicBoolean getRunning() {
        return this.running;
    }

    public int getOrder() {
        return this.order;
    }

    public int getPhase() {
        return 0;
    }


    // ***************************************************************
    //                                重点
    // ***************************************************************     
    /** @deprecated */
    @Deprecated
    public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
        if (!"management".equals(event.getApplicationContext().getNamespace())) {
            this.port.compareAndSet(0, event.getEmbeddedServletContainer().getPort());
            this.start();
        }

    }
}

      3.5.2 注:

                  AbstractDiscoveryLifecycle抽象类继承了ApplicationListener该类需要实现方法onApplicationEvent,且此方法就在AbstractDiscoveryLifecycle。  该方法调用了this.start()

     3.6 查看this.start()

         3.6.1 源码:

public void start() {
        if (!this.isEnabled()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Discovery Lifecycle disabled. Not starting");
            }

        } else {
            if (this.port.get() != 0 && this.getConfiguredPort() == 0) {
                this.setConfiguredPort(this.port.get());
            }

            if (!this.running.get() && this.getConfiguredPort() > 0) {
                this.register();
                if (this.shouldRegisterManagement()) {
                    this.registerManagement();
                }

                 // 这个地方发布了一个服务注册事件,订阅不知道在哪 ==!
                this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
                this.running.compareAndSet(false, true);
            }

        }
    }

       3.6.2 注: start()方法内,调用了this.register()  并且此方法仍旧在AbstractDiscoveryLifecycle类中,且在spring 框架内,说明spring 留了一个口子(spi)来帮助实现服务注册功能,自己想做服务注册,只要继承AbstractDiscoveryLifecycle 重写register方法即可!

3.7 点击this.register() : 发现仍在AbstractDiscoveryLifecycle,且是个抽象类!并没有具体实现!

pr4otected abstract void register();

      3.8  idea 点击实现类: 找到NacosAutoServiceREgistration 实现类

      3.9 实现方法:

    protected void register() {
        if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
            log.debug("Registration disabled.");
        } else {
            if (this.registration.getPort() < 0) {
                this.registration.setPort(this.getPort().get());
            }

            super.register();
        }
    }

        4.0 继续跟踪super.register()

public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration {

    // *****省略
    protected void register() {
        this.serviceRegistry.register(this.getRegistration());
    }

}

         4.1   this.serviceRegistry.register(this.getRegistration());

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.client.serviceregistry;

public interface ServiceRegistry<R extends Registration> {
    void register(R var1);

    void deregister(R var1);

    void close();

    void setStatus(R var1, String var2);

    <T> T getStatus(R var1);
}

          4.2 点击register 实现,则 发现为NacosServiceRegistry 实现类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.alibaba.cloud.nacos.registry;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils;

public class NacosServiceRegistry implements ServiceRegistry<Registration> {
    private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);
    private final NacosDiscoveryProperties nacosDiscoveryProperties;
    private final NamingService namingService;

    public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        this.nacosDiscoveryProperties = nacosDiscoveryProperties;
        this.namingService = nacosDiscoveryProperties.namingServiceInstance();
    }

    // NacosServiceRegistry 实现spring 框架类ServiceRegistry的register方法 
    public void register(Registration registration) {
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
        } else {
            String serviceId = registration.getServiceId();
            Instance instance = this.getNacosInstanceFromRegistration(registration);

            try {
                this.namingService.registerInstance(serviceId, instance);
                log.info("nacos registry, {} {}:{} register finished", new Object[]{serviceId, instance.getIp(), instance.getPort()});
            } catch (Exception var5) {
                log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var5});
            }

        }
    }

    public void deregister(Registration registration) {
        log.info("De-registering from Nacos Server now...");
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No dom to de-register for nacos client...");
        } else {
            NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance();
            String serviceId = registration.getServiceId();

            try {
                namingService.deregisterInstance(serviceId, registration.getHost(), registration.getPort(), this.nacosDiscoveryProperties.getClusterName());
            } catch (Exception var5) {
                log.error("ERR_NACOS_DEREGISTER, de-register failed...{},", registration.toString(), var5);
            }

            log.info("De-registration finished.");
        }
    }

    public void close() {
    }

    public void setStatus(Registration registration, String status) {
        if (!status.equalsIgnoreCase("UP") && !status.equalsIgnoreCase("DOWN")) {
            log.warn("can't support status {},please choose UP or DOWN", status);
        } else {
            String serviceId = registration.getServiceId();
            Instance instance = this.getNacosInstanceFromRegistration(registration);
            if (status.equalsIgnoreCase("DOWN")) {
                instance.setEnabled(false);
            } else {
                instance.setEnabled(true);
            }

            try {
                this.nacosDiscoveryProperties.namingMaintainServiceInstance().updateInstance(serviceId, instance);
            } catch (Exception var6) {
                throw new RuntimeException("update nacos instance status fail", var6);
            }
        }
    }

    public Object getStatus(Registration registration) {
        String serviceName = registration.getServiceId();

        try {
            List<Instance> instances = this.nacosDiscoveryProperties.namingServiceInstance().getAllInstances(serviceName);
            Iterator var4 = instances.iterator();

            while(var4.hasNext()) {
                Instance instance = (Instance)var4.next();
                if (instance.getIp().equalsIgnoreCase(this.nacosDiscoveryProperties.getIp()) && instance.getPort() == this.nacosDiscoveryProperties.getPort()) {
                    return instance.isEnabled() ? "UP" : "DOWN";
                }
            }
        } catch (Exception var6) {
            log.error("get all instance of {} error,", serviceName, var6);
        }

        return null;
    }

    private Instance getNacosInstanceFromRegistration(Registration registration) {
        Instance instance = new Instance();
        instance.setIp(registration.getHost());
        instance.setPort(registration.getPort());
        instance.setWeight((double)this.nacosDiscoveryProperties.getWeight());
        instance.setClusterName(this.nacosDiscoveryProperties.getClusterName());
        instance.setMetadata(registration.getMetadata());
        return instance;
    }
}

         4.2.1 调用链路实现闭环

                   1. spring boot 自动装配,META-INF/spring.factories

          2. 第一个就是NacosDiscoveryAutoConfiguration 且该类创建三个Bean对象(NacosServiceRegistry, NacosRegistration,NacosAutoServiceRegistration)
          3. 核心NacosAutoServiceRegistration类继承AbstractAutoServiceRegistration

                   4. AbstractAutoServiceRegistration为spring 框架内容,且继承AbstractDiscoveryLifecycle

5. AbstractDiscoveryLifecycle实现了ApplicationListener的onApplicationEvent方法

6. onApplicationEvent内调用了this.start();

7. this.start 调用了this.register

8. NacosAutoServiceRegistration实现了this.register 并调用方法super.register()

9. 调用父类register(),此时为spring 框架方法, 且调用了this.serviceRegistry.register(this.getRegistration());

10.register为接口spring未提供实现类,由nacos的NacosServiceRegistry

类实现register方法

11. NacosServiceRegistry类register方法内调用了this.namingService.registerInstance(serviceId, instance);

                  12. 最终到达ncos源码中client 模块的注册服务方法

                  13. 为NacosNamingServiceregis.registerInstance(serviceId, instance);

5. 总结:

5.1 本文主要讲了spring cloud alibaba 的starter模块下的 discovery模块,讲了客户端依赖了discovery 之后,它是是如何步一步到达nacos源码过程

               5.2 读了源码后,发现spring 已为服务注册留下了spi !配合spring boot自动装配,自己也可以实现一个client, server。继承AbstractAutoServiceRegistration 以及实现register()等

5.3 读万卷书,不如行万里路,建议自己动手实际去看看,收获才是最多的!

注:本人能力有限,还请多多包涵,亦欢迎大佬指正错误!

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

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

相关文章

【中项第三版】系统集成项目管理工程师 | 第 4 章 信息系统架构① | 4.1-4.2

前言 第4章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于技术相关的内容&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 4.1 架构基础 4.1.1 指导思想 4.1.2 设计原则 4.1.3 建设目标 4.1.4 总体框架 4.2 系统架构 4.2.1 架构定义 4.…

encrypt decrypt CA

encrypt & decrypt & CA 加密解密证书

Cyuyan中的动态内存管理!!(对后面学习数据结构至关重要)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、为什么要进行动态内存分配二、实现动态内存分配的三种库函数&#xff08;一&#xff09;、malloc函数&#xff08;二&#xff09;、calloc函数&#xff08;…

Qt5.9.9 关于界面拖动导致QModbusRTU(QModbusTCP没有测试过)离线的问题

问题锁定 参考网友的思路&#xff1a; Qt5.9 Modbus request timeout 0x5异常解决 网友认为是Qt的bug&#xff0c; 我也认同&#xff1b;网友认为可以更新模块&#xff0c; 我也认同&#xff0c; 我也编译了Qt5.15.0的code并成功安装到Qt5.9.9中进行使用&#xff0c;界面拖…

可验证算法在招投标领域的专家“盲抽”中的标段识别码加密应用研究

摘要 在招投标过程中&#xff0c;标段&#xff08;包&#xff09;识别码的安全性至关重要。本文提出了一种基于可验证算法的标段识别码加密方法&#xff0c;以确保其在专家“盲抽”过程中的保密性和可信性。通过对不同表的标段识别码进行全量加密&#xff0c;并通过匹配验证其…

绿色金融相关数据合集(2007-2024年 具体看数据类型)

数据类型&#xff1a; 1.绿色债券数据&#xff1a;2014-2023 2.绿色信贷相关数据&#xff1a;2007-2022 3.全国各省及地级市绿色金融指数&#xff1a;1990-2022 4.碳排放权交易明细数据&#xff1a;2013-2024 5.绿色金融试点DID数据&#xff1a;2010-2023 数据来源&#…

RK3588 Android12实现UVC输出功能详解

首先需要在相关部分添加uvc的功能&#xff0c;这里参考一下&#xff1a;rockchip rk3588添加uvc及uvc,adb的复合设备_uvc.gs6-CSDN博客 setprop sys.usb.config none;setprop sys.usb.config uvc 或者setprop sys.usb.config none;setprop sys.usb.config uvc,adb 使rk3588 进…

为什么需要服务器?服务器可以做些什么

目录 一、服务器和电脑的区别二、什么是SSH三、什么是免密码登录四、服务器如何实现SSH免密码登录 一、服务器和电脑的区别 服务器和电脑是两种不同类型的计算机系统&#xff0c;它们在设计、功能和用途上存在明显的区别。首先&#xff0c;从硬件配置上看&#xff0c;服务器通…

基于java+springboot+vue实现的大学生就业需求分析系统(文末源码+Lw)233

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自…

基于java+springboot+vue实现的药店管理系统(文末源码+Lw)285

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;药品信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

【Linux】:程序地址空间

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux程序地址空间的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从…

016-GeoGebra基础篇-加载项错误_使用此功能所需的服务已关闭,请检查你的隐私设置,

最近有伙伴说遇到一个问题&#xff1a;“加载项错误_使用此功能所需的服务已关闭&#xff0c;请检查你的隐私设置”&#xff0c;该怎么解决&#xff1f; 若大家也遇到同样的问题&#xff0c;建议按照我下边的步骤逐个排查下&#xff0c;基本可以解决“GeoGebra无法完美插入PPT…

利用border绘制三角技巧

绘制三角形的效果如图 <html lang"zh-cn"> <head><meta charset"UTF-8"><title>demo</title><style>* {margin: 0;padding: 0;}.box {/* 盒子宽高改成零就变成三角形 &#xff0c;需要哪个方向的三角形就设置哪个方向…

PD协议诱骗芯片,XSP08Q,XSP16应用笔记

XSP08Q是3C数码或小家电产品的Type-C接口控制芯片&#xff0c;它负责和PD充电器通讯&#xff0c;获取充电器的快充电压档位&#xff0c;如5V4A&#xff0c;9V3A&#xff0c;12V2A&#xff0c;15V3A&#xff0c;20V5A等等。 XSP08Q支持PD协议&#xff0c;BC1.2协议&#xff0c;Q…

Spring MVC 获取请求数据的四种方式,以及获取请求头数据,获取Cookie 的数据,设置Spring MVC 的字符集编码过滤器

1. Spring MVC 获取请求数据的四种方式&#xff0c;以及获取请求头数据&#xff0c;获取Cookie 的数据&#xff0c;设置Spring MVC 的字符集编码过滤器 文章目录 1. Spring MVC 获取请求数据的四种方式&#xff0c;以及获取请求头数据&#xff0c;获取Cookie 的数据&#xff0c…

【IT领域新生必看】Java中的Static关键字详解:小白也能轻松掌握的神奇用法

文章目录 引言什么是Static关键字&#xff1f;Static变量&#xff08;类变量&#xff09;定义和使用示例&#xff1a; 应用场景 Static方法&#xff08;类方法&#xff09;定义和使用示例&#xff1a; 应用场景 Static代码块定义和使用示例&#xff1a; 应用场景 Static嵌套类定…

ESP32 通过蓝牙显示歌词代码示例

通过蓝牙协议播放音乐&#xff0c;有的时候需要显示歌词&#xff0c;这里就是a2dp库获取了歌词 值得注意的是要想正确获取到歌词&#xff0c;必须打开各种播放器的字幕&#xff08;歌词&#xff09;开关 本项目用了三个开源库 a2dp&#xff0c;tft_espi,xfont. a2dp &#x…

Qt 网络编程 udp通信

学习目标&#xff1a;使用udp通信 前置环境 运行环境:qt creator 4.12 学习内容 UDP 协议基础知识 1、UDP(用户数据报协议)是轻量的、不可靠的、面向数据报、无连接的协议&#xff0c;用于可靠性要求不高的场合。两个应用程序之间进行UDP 通信不需先建立持久的 socket 连接…

【IT领域新生必看】解密Java中的静态方法与实例方法:小白也能轻松掌握的全方位指南

文章目录 引言什么是静态方法&#xff1f;定义和使用静态方法示例&#xff1a; 静态方法的特点示例&#xff1a; 什么是实例方法&#xff1f;定义和使用实例方法示例&#xff1a; 实例方法的特点示例&#xff1a; 静态方法与实例方法的区别作用范围示例&#xff1a; 访问权限示…

宁德时代天行发布,商用车超充时代来临

近日&#xff0c;宁德时代正式推出商用动力电池品牌——“宁德时代天行”&#xff0c;同时发布“宁德时代天行轻型商用车&#xff08;L&#xff09;-超充版”和“宁德时代天行轻型商用车&#xff08;L&#xff09;-长续航版”两款产品&#xff0c;可实现4C超充能力和500km的实况…