0501源码分析-启动过程-springboot2.7.x系列

news2024/10/7 14:26:47

文章目录

    • 1前言
    • 2 启动第一阶段
      • 2.1 deduceFromClasspath 推断应用类型
      • 2.2 getSpringFactoriesInstances(Class)
      • 2.3 ApplicationContextInitializer
      • 2.4 ApplicationListener
      • 2.5 自定义接口实现配置示例
    • 3 启动第二阶段
      • 3.1 SpringApplicationRunListener
      • 3.2 容器创建和准备
    • 4 总结
    • 结语

1前言

下面我们通过源码来初步了解下springboot启动流程,从下面链接2处借一张图整体展示下启动过程,如下图1-1所示:

在这里插入图片描述

追踪下启动类main方法的run方法,SpringApplicaiton#run源代码如下所示:

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

这里启动分为两个部分:

  • 启动第一阶段:new SpringApplication(),实例化SpringApplication对象;
  • 启动第二阶段:调用SpringApplication对象的run方法。

2 启动第一阶段

SpringApplication构造方法源代码如下所示:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  // 资源加载器
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
  // 启动配置类
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  // 推断应用类型:None,Reactive,Servlet
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
  // spring.factories获取BootstrapRegistryInitializer接口实现类
   this.bootstrapRegistryInitializers = new ArrayList<>(
         getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
  //  使用SpringFactoriesLoader加载 实例化ApplicationContextInitializer接口实现类初始器
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  //  使用SpringFactoriesLoader加载 实例化ApplicationListener接口实现类监听器
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

2.1 deduceFromClasspath 推断应用类型

WebApplicationType#deduceFromClasspath()源代码如下所示:

static WebApplicationType deduceFromClasspath() {
  // 判断应用是不是web Reactive
   if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
         && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
     // 判断如果存在Webflux标志类且不存在webmvc标志类DispatcherServlet和jersey标志类ServletContainer
      return WebApplicationType.REACTIVE;
   }
  // 非web应用
   for (String className : SERVLET_INDICATOR_CLASSES) {
     // Servlet与ConfigurableWebApplicationContext任一不存在,非web应用
      if (!ClassUtils.isPresent(className, null)) {
         return WebApplicationType.NONE;
      }
   }
  // web servlet应用
   return WebApplicationType.SERVLET;
}
  • 判断原理就是通过类加载器去加载对应应用的标志类,如果加载成功,为对应的应用类型;否则不是。

2.2 getSpringFactoriesInstances(Class)

获取spring.factories文件中对应类或者接口的实现类,我们通过源码来下如果实现的。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
  // 获取类加载器
   ClassLoader classLoader = getClassLoader();
   // SpringFactoriesLoader加载type类型的类名
   Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  // 实例化
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  // 根据注解@Order指定的顺序排序
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

SpringFactoriesLoader#loadFactoryNames()源代码如下所示:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

loadSpringFactories()源代码如下所示:

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  // 优先从缓存中获取,key为classLoader
   Map<String, List<String>> result = cache.get(classLoader);
   if (result != null) {
     // 如果已经加载过,直接返回
      return result;
   }

   result = new HashMap<>();
   try {
     // 资源(文件)位置FACTORIES_RESOURCE_LOCATION:"META-INF/spring.factories"
      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
        // 提取属性
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
           // 属性名称
            String factoryTypeName = ((String) entry.getKey()).trim();// 以逗号分割的值
            String[] factoryImplementationNames =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }

      // Replace all lists with unmodifiable lists containing unique elements
      result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
            .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
     // 缓存结果
      cache.put(classLoader, result);
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
   return result;
}
  • SpringFactoriesLoader为springboot SPI机制的具体实现的关键类。

2.3 ApplicationContextInitializer

ApplicationContextInitializer是Spring框架中的一个接口,用于在应用程序上下文完全创建和刷新之前对其进行初始化。它通常用于自定义应用程序上下文或在应用程序启动之前配置某些方面。

ApplicationContextInitializer接口定义了一个名为initialize的方法,该方法接受一个ApplicationContext作为参数。该方法在应用程序上下文初始化过程中由应用程序上下文调用。

您可以创建自己的ApplicationContextInitializer实现来执行自定义的初始化任务。例如,您可以使用它设置属性源、注册额外的Bean或根据外部条件配置特定的配置文件。实现类可以通过使用配置类或XML配置文件将其注册到Spring应用程序上下文中。

下面是一个使用ApplicationContextInitializer的示例:

javaCopy code
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 自定义的初始化逻辑在这里
        // 您可以访问应用程序上下文并执行任何所需的设置
        // 例如,注册额外的Bean定义或配置配置文件
    }
}

要将初始化程序注册到应用程序上下文中,您可以通过编程方式或通过配置来完成。以下是使用基于Java的配置类的示例:

javaCopy code
@Configuration
public class AppConfig {

    @Bean
    public static MyApplicationContextInitializer myApplicationContextInitializer() {
        return new MyApplicationContextInitializer();
    }

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(AppConfig.class);
        application.addInitializers(myApplicationContextInitializer());
        application.run(args);
    }
}

在上述示例中,MyApplicationContextInitializer被注册为一个Bean,并添加为SpringApplication的初始化程序。当应用程序上下文创建时,将调用MyApplicationContextInitializerinitialize方法,允许您执行自定义的初始化逻辑。

通过使用ApplicationContextInitializer,您可以在应用程序上下文初始化阶段灵活地自定义应用程序上下文,以根据特定需求进行配置和调整。

2.4 ApplicationListener

ApplicationListener是Spring框架中的一个接口,用于监听和响应应用程序事件。它是Spring事件处理机制的关键组件之一。通过实现ApplicationListener接口,您可以创建事件监听器,对在Spring应用程序上下文中发布的事件做出响应。

ApplicationListener接口定义了一个名为onApplicationEvent的方法,当发布一个应用程序事件时,该方法将被调用。该方法接受一个ApplicationEvent参数,表示发生的事件。

下面是一个使用ApplicationListener的示例:

javaCopy code
public class MyEventListener implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent event) {
        // 在这里处理事件的逻辑
        // 您可以访问事件并执行任何所需的操作
    }
}

在上述示例中,MyEventListener是一个自定义的事件监听器,专门监听MyEvent类型的事件。当在应用程序上下文中发布MyEvent的实例时,将调用MyEventListeneronApplicationEvent方法,允许您处理事件。

要在应用程序上下文中发布事件,您可以使用ApplicationEventPublisher接口。以下是发布事件的示例:

javaCopy code
@Component
public class MyEventPublisher {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void publishEvent() {
        MyEvent event = new MyEvent(this, "Event data");
        eventPublisher.publishEvent(event);
    }
}

在上述示例中,MyEventPublisher是一个组件,它使用@Autowired自动装配一个ApplicationEventPublisher来发布事件。当调用publishEvent方法时,它创建一个MyEvent的实例,并使用ApplicationEventPublisherpublishEvent方法发布事件。

要将ApplicationListener注册到应用程序上下文中,您可以通过编程方式或通过配置来完成。以下是使用基于Java的配置类的示例:

javaCopy code
@Configuration
public class AppConfig {

    @Bean
    public static MyEventListener myEventListener() {
        return new MyEventListener();
    }
}

在上述示例中,MyEventListener被注册为一个Bean。当发布MyEvent类型的事件时,将调用MyEventListeneronApplicationEvent方法。

通过使用ApplicationListener,您可以创建自定义的事件监听器,以响应Spring应用程序上下文中发生的特定事件。这使您可以将组件解耦,并根据应用程序中发生的事件实现响应性行为。

2.5 自定义接口实现配置示例

实现自定义的MyApplicationContextInitializer,代码2.5-1如下:

public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 容器初始化
        System.out.println("initializer");
    }
}

实现自定义的MyApplicationListener,代码2.5-1如下:

public class MyApplicationListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("==== " + event);
    }
}

在spring.factories中配置,文件内容如下:

# initializer
org.springframework.context.ApplicationContextInitializer=\
com.gaogzhen.initializer.MyApplicationContextInitializer

# listener
org.springframework.context.ApplicationListener=\
com.gaogzhen.listener.MyApplicationListener

3 启动第二阶段

下面我们看下SpringApplication#run()方法,源代码如下:

public ConfigurableApplicationContext run(String... args) {
  // 计时
  long startTime = System.nanoTime();
  // 创建默认的启动上下文
  DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  // 应用上下文
  ConfigurableApplicationContext context = null;
  configureHeadlessProperty();
  // srpingboot生命周期监听器
  SpringApplicationRunListeners listeners = getRunListeners(args);
  // 监听正在启动中,执行监听器对应的starting方法
  listeners.starting(bootstrapContext, this.mainApplicationClass);
  try {
    // 封装参数对象
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // 环境变量对象
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    configureIgnoreBeanInfo(environment);
    // 打印banner
    Banner printedBanner = printBanner(environment);
    // 根据不同的应用类型创建对应容器
    context = createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    // 刷新容器,执行AbstractApplicationContext#fresh()方法
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    if (this.logStartupInfo) {
      new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
    }
    // 监听启动完成,执行监听器对应的started方法
    listeners.started(context, timeTakenToStartup);
    callRunners(context, applicationArguments);
  }
  catch (Throwable ex) {
    handleRunFailure(context, ex, listeners);
    throw new IllegalStateException(ex);
  }
  try {
    Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
    // 监听准备就绪,执行监听器对应的ready方法
    listeners.ready(context, timeTakenToReady);
  }
  catch (Throwable ex) {
    handleRunFailure(context, ex, null);
    throw new IllegalStateException(ex);
  }
  return context;
}

3.1 SpringApplicationRunListener

SpringApplicationRunListener 接口是 Spring Framework 中的一个接口,用于自定义 SpringApplication 的运行过程中的事件监听器。

SpringApplicationRunListener接口源代码如下:

public interface SpringApplicationRunListener {

   default void starting(ConfigurableBootstrapContext bootstrapContext) {
   }

   default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
         ConfigurableEnvironment environment) {
   }

   default void contextPrepared(ConfigurableApplicationContext context) {
   }

   default void contextLoaded(ConfigurableApplicationContext context) {
   }

   default void started(ConfigurableApplicationContext context, Duration timeTaken) {
      started(context);
   }
  
   @Deprecated
   default void started(ConfigurableApplicationContext context) {
   }
  
   default void ready(ConfigurableApplicationContext context, Duration timeTaken) {
      running(context);
   }

   @Deprecated
   default void running(ConfigurableApplicationContext context) {
   }

   default void failed(ConfigurableApplicationContext context, Throwable exception) {
   }

}

该接口定义了一组方法,用于处理 SpringApplication 生命周期中的不同阶段的事件。以下是该接口定义的方法:

  • starting(): 当 SpringApplication 开始启动时调用。
  • environmentPrepared(ConfigurableEnvironment environment): 在应用程序环境准备完成后、应用程序上下文创建之前调用。
  • contextPrepared(ConfigurableApplicationContext context): 在应用程序上下文准备完成后、但尚未刷新之前调用。
  • contextLoaded(ConfigurableApplicationContext context): 在应用程序上下文加载完成后、但尚未刷新之前调用。
  • started(ConfigurableApplicationContext context): 在应用程序上下文刷新完成并且应用程序启动后调用。
  • running(ConfigurableApplicationContext context): 当应用程序正在运行时调用。
  • failed(ConfigurableApplicationContext context, Throwable exception): 如果应用程序启动失败时调用。

该接口的重要实现类EventPublishingRunListener,以starting()方法为例,看看它做了什么,源代码如下:

@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
   this.initialMulticaster
      .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
  • 通过广播发布了ApplicationStartingEvent类型的广播事件,

您可以通过实现 SpringApplicationRunListener 接口来创建自定义的 SpringApplicationRunListener,以便在应用程序的生命周期中处理特定的事件。请注意,您需要提供一个与默认构造函数匹配的合适构造函数。

以下是一个示例:

public class MyRunListener implements SpringApplicationRunListener {
    
    public MyRunListener(SpringApplication application, String[] args) {
        
    }
    
    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        System.out.println("===启动中===");
    }
}

要激活自定义的 SpringApplicationRunListener,您需要在项目的 META-INF 目录下的 spring.factories 文件中指定映射关系。spring.factories 文件应包含 SpringApplicationRunListener 接口和您自定义实现的映射关系:

org.springframework.boot.SpringApplicationRunListener=\
com.gaogzhen.listener.MyRunListener

运行结果:

==== org.springframework.boot.context.event.ApplicationStartingEvent[source=org.springframework.boot.SpringApplication@23e028a9]
===启动中===

通过这样的配置,您的自定义 SpringApplicationRunListener 将在 SpringApplication 的运行过程中被自动检测并调用。

请注意,SpringApplicationRunListener 的可用性和使用方式可能在更近期的 Spring Boot 版本中有所改变,因此建议参考官方的 Spring Boot 文档或相关资源以获取最新的信息和使用指南。

3.2 容器创建和准备

  • 容器创建

创建容器createApplicationContext()源代码如下所示:

protected ConfigurableApplicationContext createApplicationContext() {
   return this.applicationContextFactory.create(this.webApplicationType);
}
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;

DefaultApplicationContextFactory#create()方法源代码如下所示:

@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
   try {
      return getFromSpringFactories(webApplicationType, ApplicationContextFactory::create,
            AnnotationConfigApplicationContext::new);
   }
   catch (Exception ex) {
      throw new IllegalStateException("Unable create a default ApplicationContext instance, "
            + "you may need a custom ApplicationContextFactory", ex);
   }
}
	private <T> T getFromSpringFactories(WebApplicationType webApplicationType,
			BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
    // 遍历SpringFactoriesLoader加载spring.factories文件中ApplicationContextFactory接口的实现类
		for (ApplicationConSpringFactoriesLoader加载spring.factories文件中textFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class,
				getClass().getClassLoader())) {
      // 执行实现类的create方法
			T result = action.apply(candidate, webApplicationType);
			if (result != null) {
				return result;
			}
		}
		return (defaultResult != null) ? defaultResult.get() : null;
	}

spring.factories文件ApplicationContextFactory配置如下:

# Application Context Factories
org.springframework.boot.ApplicationContextFactory=\
org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext.Factory,\
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory

以AnnotationConfigReactiveWebServerApplicationContext.Factory为例看下create()方法源代码如下:

@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
   return (webApplicationType != WebApplicationType.REACTIVE) ? null
         : new AnnotationConfigReactiveWebServerApplicationContext();
}

注:根据不同的应用类型创建不同类型的容器

  • 容器准备

prepareContext()方法源代码如下:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
  // 设置环境变量
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
  // 应用初始化器
   applyInitializers(context);
  // 发布容器准备完成事件
   listeners.contextPrepared(context);
  // 发布applicationContext准备好,bootstrapConxtex关闭事件
   bootstrapContext.close(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // 注册指定的启动相关的单例bean
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
      ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
      if (beanFactory instanceof DefaultListableBeanFactory) {
         ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
      }
   }
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
   // 加载注册souces,其中包括我们的启动类
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
   listeners.contextLoaded(context);
}
  • applyInitializers()应用初始化器,我们可以看下之前示例打印会在banner打印之后执行,如下控制台输出

      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::               (v2.7.10)
    
    ===initializer===
    

4 总结

springboot启动过程简介:

  1. 实例化SpringApplication对象
    • 启动类赋值给primarySource
    • 推断webApplicationType,应用类型
    • 从spring.factories文件中读取ApplicationContextInitializer实现类,加载并实例化
    • 从spring.factories文件中读取ApplicationListener实现类,加载并实例化
    • 推断main方法所在类
  2. 调用SpringApplication#run()方法
    • 从spring.factories文件中读取SpringApplicationRunListener实现类,加载并实例化
    • 执行上述listeners的starting()方法
    • 创建对应应用类型的环境变量Environment,做相应的设置
    • 打印banner
    • 创建相应应用类型的容器,设置容器
      • 执行初始化器
      • contextPrepared()
      • 注册指定启动相关单例bean
      • contextLoaded()f
    • 刷新容器
    • started()
    • callRunners
    • fail()
    • ready()

结语

如果小伙伴什么问题或者指教,欢迎交流。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/springboot-custom

参考:

[1]Springboot视频教程[CP/OL].P10,11,20,21.

[2]【SpringBoot】SpringBoot启动流程图和扩展点说明[CP/OL].

[3]spring boot 启动流程分析[CP/OL].

[4]Java常用机制 - SPI机制详解[CP/OL].

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

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

相关文章

11. 数据结构之二叉树

前言 上一节&#xff0c;简单概述了树这种数据结构&#xff0c;以及树结构向下&#xff0c;具有某些一些特征的树&#xff0c;比如二叉树&#xff0c;B树&#xff0c;B树&#xff0c;堆等。其中&#xff0c;二叉树是一个很重要的模块。也是在一些技术面试中&#xff0c;可能会…

【Vue】学习笔记-Vuex

Vuex 理解VuexVuex是什么什么时候使用VuexVuex 工作原理图求和案例使用纯vue编写 搭建Vuex环境使用Vuex编写求和案例getters配置项四个map方法的使用多组件共享数据案例模块化命名空间 理解Vuex Vuex是什么 概念&#xff1a;专门在vue中实现集中式状态(数据) 管理的一个vue插…

基于P-Tuningv2轻量微调和推理chatglm

类ChatGPT的部署与微调(下)&#xff1a;从GLM、ChatGLM到MOSS、ChatDoctor、可商用_v_JULY_v的博客-CSDN博客随着『GPT4多模态/Microsoft 365 Copilot/Github Copilot X/ChatGPT插件』的推出&#xff0c;绝大部分公司的技术 产品 服务&#xff0c;以及绝大部分人的工作都将被革…

【CMake 入门与进阶(2)】CMake编译设置——多个源文件编译及生成库文件(附代码)

多个源文件 上篇我们学习了单个源文件的cmake 的编译&#xff0c;不过一个源文件的例子似乎没什么意思&#xff0c;我们再加入一个hello.h 头文件和 hello.c 源文件。在 hello.c 文件中 定义了一个函数 hello&#xff0c;然后在 main.c 源文件中将会调用该函数&#xff…

客服都要下岗了? 当ChatGPT遇见私有数据,秒变AI智能客服!

用ChatGPT搭建基于私有数据的WorkPlus AI客服机器人这个想法&#xff0c;源于WorkPlus售前工作需求。在ChatGPT之前&#xff0c;其实对话式AI一直在被广泛使用在客服场景&#xff0c;只不过不大智能而已。比如你应该看到不少电商客服产品&#xff0c;就有类似的功能&#xff0c…

车站信息管理系统(面向对象程序设计python版)

一、基本概述 1.项目背景 随着大数据时代的发展,大数据抓取了人们最想要的信息,数据查询能帮助用户获取更有用的信息,让每个人都能享受到大数据带给生活的高效和便捷。 2.设计目的 为了大大缩减人们出行选择站点所需时间,为了让人们在陌生地区,在对当地交通不熟的情况…

Redis数据类型之(哈希Hash和集合Set)

Redis数据类型之&#xff08;哈希Hash和集合Set&#xff09; 一定注意看红色注意项。 哈希&#xff08;Hash&#xff09;: Redis hash 是一个 string 类型的 field&#xff08;字段&#xff09; 和 value&#xff08;值&#xff09; 的映射表&#xff0c;hash 特别适合用于存…

promethues 之PromQL数据类型介绍(二)

promethues 之PromQL数据类型介绍(二) 1、PromQL 介绍 PromQL是promethues 监控系统内置的一种查询语言&#xff0c;类似于MySQL的SQL语句&#xff0c;该语言仅用于读取数据。PromQL是我们学习Promethues最困难也是最重要的部分。当Promethues从系统和服务收集到指标数据时&…

PIP-Net:用于可解释图像分类的基于patch的直观原型

文章目录 PIP-Net: Patch-Based Intuitive Prototypes for Interpretable Image Classification摘要本文方法模型结构Self-Supervised Pre-Training of PrototypesTraining PIP-NetScoring Sheet ReasoningCompact Explanations 实验结果 PIP-Net: Patch-Based Intuitive Proto…

bug 记录 - 接口被重复调用,响应时长不同,结果被覆盖的问题

发现问题与调试过程 需求&#xff1a;输入框中输入关键字&#xff0c;根据关键字去调用接口&#xff0c;返回模糊查询的结果集合。问题&#xff1a;输入的关键字越少&#xff0c;接口响应时间越长。例如&#xff1a;输入“阿”&#xff0c;接口响应时间大概是 5 秒&#xff0c…

【计算机网络中ip概念总结】【平时我们说的ip 到底是什么】【计算机网络中 ip地址是什么】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

【Linux】重定向dup

文章目录 前言重定向的原理dup函数添加重定向功能到myshell 前言 了解重定向之前需要明白文件描述符的工作规则&#xff0c;可以看这篇文章&#xff1a;文件系统 最关键的一点是&#xff1a;在进程中&#xff0c;在文件描述符表中&#xff0c;会将最小的、没有被使用的数组元…

vscode整合gitee

vscode需要下载的插件 第一个可以多仓库进行操作 第二个主要是用于仓库的管理和展示 vscode的gitee操作 1、按F1&#xff0c;搜索gitee 2、根据提示进行操作 标1的是第一个插件的操作 标2的是第二个插件的操作 绑定用户私钥 两个插件绑定私钥的方式不同&#xff0c; gitee的私…

这本数智平台白皮书讲透了大型企业数智化升级业务痛点

在以“升级企业数智化底座”为主题的2023用友BIP技术大会上&#xff0c;用友联合全球权威咨询机构IDC共同发布《建设数字中国 升级数智底座——企业数智化底座白皮书》&#xff0c;在这本数智平台白皮书里深入剖析了大型企业的数智化升级痛点。 大型企业普遍具有广域的业务覆盖…

六级备考15天|CET-6|翻译真题练习|北京大兴国际机场|9:15~10:20

目录 中文 英文 词汇 订正 解析 练习 中文 英文 词汇 put sth. into use 投入使用 距离south of地点 “...以南....公里处” construction 开工建设 the giant project 巨型工程 on the site …

LED显示屏驱动IC基本原理

LED显示屏驱动IC&#xff08;Integrated Circuit&#xff0c;集成电路&#xff09;是一种专门设计用于控制和驱动LED显示屏的电子元件。LED显示屏驱动IC的基本原理涉及到LED的电流控制、亮度调节、扫描控制和图像数据处理等方面。 以下是LED显示屏驱动IC的基本原理的详细说明&a…

只需简单几步,就能在报表工具FastReport .NET 中使用 RFID 标签

FastReport 是功能齐全的报表控件&#xff0c;可以帮助开发者可以快速并高效地为.NET&#xff0c;VCL&#xff0c;COM&#xff0c;ActiveX应用程序添加报表支持&#xff0c;由于其独特的编程原则&#xff0c;现在已经成为了Delphi平台最优秀的报表控件&#xff0c;支持将编程开…

喜讯丨计讯物联5G物联网数据网关TG463荣登2022年度中国物联网行业创新产品榜

近日&#xff0c;备受瞩目的2022‘物联之星’中国物联网产业年度榜单颁奖典礼在上海世博展览馆会场隆重举行。经由申报筛选、网络人气投票、专家评委投票等多重环节&#xff0c;计讯物联旗下5G物联网数据网关TG463荣登2022年度中国物联网行业创新产品榜。 作为中国物联网行业…

chatgpt赋能python:Python编写抽奖程序——让你的活动更加有趣

Python编写抽奖程序——让你的活动更加有趣 在现代社会中&#xff0c;抽奖活动已经成为了许多商家和组织吸引关注、增强互动的重要手段。而使用Python编写抽奖程序可以帮助我们更加方便地进行这一活动。本文将介绍Python编写抽奖程序的方法&#xff0c;以及如何在实际应用中优…

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等)、趣味项目实现、学术应用项目实现

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 对于深度强化学习这块规划为&#xff1a; 基础单智能算法教学&#xff08;gym环境为主&#xff09;主流多智能算法教学…