Spring Boot 系统初始化器详解

Spring Boot 3.x系列文章

  1. Spring Boot 2.7.8 中文参考指南(一)
  2. Spring Boot 2.7.8 中文参考指南(二)-Web
  3. Spring Boot 源码阅读初始化环境搭建
  4. Spring Boot 框架整体启动流程详解
  5. Spring Boot 系统初始化器详解

自定义系统初始化器

Spring Boot 有多种加载自定义初始化器的方法:
1、创建一个实现ApplicationContextInitializer接口的类,在spring.factories中添加,如MyInitializer
2、创建一个实现ApplicationContextInitializer接口的类,在SpringApplication 中使用addInitializers添加,如MyInitializer2
3、创建一个实现ApplicationContextInitializer接口的类,在application.ymlapplication.properties中使用context.initializer.classes添加,如MyInitializer3
4、创建一个实现EnvironmentPostProcessor接口的类,在spring.factories中添加,如MyEnvironmentPostProcessor

代码如下所示:

MyInitializer.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;import java.util.HashMap;
import java.util.Map;@Slf4j
@Order(2)
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();Map<String, Object> map = new HashMap<>();map.put("key", "value");MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);configurableEnvironment.getPropertySources().addLast(mapPropertySource);log.info("My Initializer run");}
}

MyInitializer2.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;import java.util.HashMap;
import java.util.Map;@Slf4j
@Order(1)
public class MyInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();Map<String, Object> map = new HashMap<>();map.put("key2", "value2");MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);configurableEnvironment.getPropertySources().addLast(mapPropertySource);log.info("My Initializer2 run");}
}

MyInitializer3.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;import java.util.HashMap;
import java.util.Map;@Slf4j
@Order(10)
public class MyInitializer3 implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();Map<String, Object> map = new HashMap<>();map.put("key3", "value3");MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);configurableEnvironment.getPropertySources().addLast(mapPropertySource);log.info("My Initializer3 run");}
}

MyEnvironmentPostProcessor.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;import java.util.HashMap;
import java.util.Map;@Slf4j
@Order(5)
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {Map<String, Object> map = new HashMap<>();map.put("key", "value");MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);environment.getPropertySources().addLast(mapPropertySource);//为什么不打印日志
//        log.info("My EnvironmentPostProcessor run");System.out.println("My EnvironmentPostProcessor run");}
}

启动后截图:
在这里插入图片描述

疑问❓

  • 在MyEnvironmentPostProcessor的示例中,用log.info("My EnvironmentPostProcessor run"); 不会打印日志。
  • MyInitializer3的输出怎么会在MyInitializer2之前。

加载原理

实例1加载原理

在之前的文章中《Spring Boot 框架整体启动流程详解》有介绍到Spring Boot 应用程序初始化的时候会从META-INF/spring.factories加载ApplicationContextInitializer类实例

在这里插入图片描述
SpringFactoriesLoader 是Spring 框架中的类,用于从多个Jar文件的META-INF/spring.factories中加载并实例化给定的类型,spring.factories文件必须采用Properties格式,其中key是接口或抽象类的完全限定名称,value是以逗号分隔的实现类名列表。例如:
example.MyService=example.MyServicesImpl1,example.MyService Impl2
其中example.MyService是接口的名称,MyServiceImpl1和MyServiceImpl2是两个实现。

获取实例分成了两部分,首先从多个Jar文件的META-INF/spring.factories中加载key和value,返回一个SpringFactoriesLoader实例,然后调用SpringFactoriesLoader的load方法初始化指定key(key为接口或者抽象类的全限定名)对应的所有value(接口实现类),返回实例列表。

spring.factories的加载

在这里插入图片描述
FACTORIES_RESOURCE_LOCATION指定了加载的路径

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
// 判断资源路径是否为空,若为空则抛出异常Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
// 获取资源对应的类加载器,若传入的类加载器为空,则使用SpringFactoriesLoader类的类加载器ClassLoader resourceClassLoader = (classLoader != null ? classLoader :SpringFactoriesLoader.class.getClassLoader());
// 从缓存中获取SpringFactoriesLoader,若不存在,则创建一个并缓存 Map<String, SpringFactoriesLoader>,key为ClassLoader,资源对应的类加载器Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());
// 返回resourceLocation对应的SpringFactoriesLoader对象,若不存在,则创建一个并缓存,key为resourceLocation,资源路径return loaders.computeIfAbsent(resourceLocation, key ->new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}

computeIfAbsent 返回的是key关联的value值

最后一步value创建了一个SpringFactoriesLoader实例,loadFactoriesResource 使用给定的资源类加载器从"META-INF/spring.factories"中加载

protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
//实现列表,key=接口或抽象类全限定名 value=实现类全限定名Map<String, List<String>> result = new LinkedHashMap<>();try {//获取指定路径下所有的资源URLEnumeration<URL> urls = classLoader.getResources(resourceLocation);while (urls.hasMoreElements()) {UrlResource resource = new UrlResource(urls.nextElement());//从URL资源中读取配置Properties properties = PropertiesLoaderUtils.loadProperties(resource);properties.forEach((name, value) -> {//实现类逗号分割,转换为数组String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);//接口的实现类列表List<String> implementations = result.computeIfAbsent(((String) name).trim(),key -> new ArrayList<>(factoryImplementationNames.length));//去掉实现类两边空格,并插入实现类列表Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);});}//去重
result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);}//返回不可修改的mapreturn Collections.unmodifiableMap(result);
}

加载部分有很多的key,value 要分清楚。

spring.factories接口实现类的实例化

实例化通过调用SpringFactoriesLoader的load方法

	public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {return load(factoryType, argumentResolver, null);}

factoryType指定要实例化的类型,这里为 org.springframework.context.ApplicationContextInitializer
argumentResolver 实例化需要的参数,这里为null

public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,@Nullable FailureHandler failureHandler) {Assert.notNull(factoryType, "'factoryType' must not be null");//从factories 中获取指定接口类型的所有实现//factories就是加载步骤中返回的resultList<String> implementationNames = loadFactoryNames(factoryType);logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));List<T> result = new ArrayList<>(implementationNames.size());//定义失败处理器FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER;//循环,实例化for (String implementationName : implementationNames) {//通过构造函数实例化T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);if (factory != null) {result.add(factory);}}//根据order 排序AnnotationAwareOrderComparator.sort(result);return result;
}

最终返回排序后的ApplicationContextInitializer 实例,赋值SpringApplication 的 initializers 变量。

执行

执行会在SpringApplication类的prepareContext(准备上下文)中进行调用,如图所示:
在这里插入图片描述

	//返回一个只读的有序,LinkedHashSet 类型public Set<ApplicationContextInitializer<?>> getInitializers() {return asUnmodifiableOrderedSet(this.initializers);}
	protected void applyInitializers(ConfigurableApplicationContext context) {//获取所有的ApplicationContextInitializer实例for (ApplicationContextInitializer initializer : getInitializers()) {Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),ApplicationContextInitializer.class);// 判断ApplicationContextInitializer实例泛型是否与context对象类型一致Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");// 调用ApplicationContextInitializer实例的initialize方法进行初始化操作initializer.initialize(context);}}

实例2加载原理

创建一个实现ApplicationContextInitializer接口的类,在SpringApplication 中使用addInitializers添加,如MyInitializer2

我们使用addInitializers 将ApplicationContextInitializer接口的实现加入到SpringApplication中。

	public void addInitializers(ApplicationContextInitializer<?>... initializers) {this.initializers.addAll(Arrays.asList(initializers));}

initializers 就是 SpringApplication中的initializers变量,执行点同实例1,在准备上下文的时候执行,由于执行前会进行一次排序,所以他们两的顺序是正确的。

实例3加载原理

创建一个实现ApplicationContextInitializer接口的类,在application.ymlapplication.properties中使用context.initializer.classes添加,如MyInitializer3

该处通过配置文件添加ApplicationContextInitializer实现类,并且通过DelegatingApplicationContextInitializer 初始化器进行加载和执行。

在这里插入图片描述
DelegatingApplicationContextInitializer 被定义在了spring-boot.jar 的 META-INF/spring.factories中,并且由于他的order是0,所以会在我们自定义MyInitializer和MyInitializer2 前执行,它是另外一种独立的初始化器,专门用于将配置文件中的ApplicationContextInitializer实现类加载到Spring容器中。

在这里插入图片描述
执行在DelegatingApplicationContextInitializer类的applyInitializers方法中

private void applyInitializers(ConfigurableApplicationContext context,List<ApplicationContextInitializer<?>> initializers) {//排序initializers.sort(new AnnotationAwareOrderComparator());for (ApplicationContextInitializer initializer : initializers) {//调用initialize方法initializer.initialize(context);}
}

实例4加载原理

创建一个实现EnvironmentPostProcessor接口的类,在spring.factories中添加,如MyEnvironmentPostProcessor
实例4是在所有的测试中最先打印日志的,是因为它是在prepareEnvironment(准备环境)中执行,而前面3个实例都是在prepareContext(准备上下文)中执行。
该实例中EventPublishingRunListener会调用prepareEnvironment方法,EventPublishingRunListener被定义在Spring Boot Jar包的META-INF/spring.factories中,用于发布各种SpringApplicationEvent事件。

EventPublishingRunListener类中

public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {//广播环境准备完成事件multicastInitialEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}private void multicastInitialEvent(ApplicationEvent event) {//刷新SimpleApplicationEventMulticaster中的事件列表refreshApplicationListeners();//广播事件this.initialMulticaster.multicastEvent(event);
}private void refreshApplicationListeners() {this.application.getListeners().forEach(this.initialMulticaster::addApplicationListener);
}

SimpleApplicationEventMulticaster类中

public void multicastEvent(ApplicationEvent event) {multicastEvent(event, null);
}@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));// 获取执行事件的线程池Executor executor = getTaskExecutor();//获取指定事件类型的事件集合for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {//如果定义了执行线程池,则用线程池调用if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {//同步调用监听器invokeListener(listener, event);}}
}protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//获取失败处理器ErrorHandler errorHandler = getErrorHandler();if (errorHandler != null) {try {doInvokeListener(listener, event);}catch (Throwable err) {errorHandler.handleError(err);}}else {doInvokeListener(listener, event);}
}@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {//此处执行事件监听器的onApplicationEvent方法listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||(event instanceof PayloadApplicationEvent payloadEvent &&matchesClassCastMessage(msg, payloadEvent.getPayload().getClass()))) {// Possibly a lambda-defined listener which we could not resolve the generic event type for// -> let's suppress the exception.Log loggerToUse = this.lazyLogger;if (loggerToUse == null) {loggerToUse = LogFactory.getLog(getClass());this.lazyLogger = loggerToUse;}if (loggerToUse.isTraceEnabled()) {loggerToUse.trace("Non-matching event type for listener: " + listener, ex);}}else {throw ex;}}
}

listener.onApplicationEvent(event);处,在本例中为EnvironmentPostProcessorApplicationListener

EnvironmentPostProcessorApplicationListener类中:

public void onApplicationEvent(ApplicationEvent event) {
//根据各个事件类型分别去处理if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);}if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent();}if (event instanceof ApplicationFailedEvent) {onApplicationFailedEvent();}
}private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {ConfigurableEnvironment environment = event.getEnvironment();SpringApplication application = event.getSpringApplication();// 获取所有的 EnvironmentPostProcessor,然后执行其 postProcessEnvironment 方法for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),event.getBootstrapContext())) {postProcessor.postProcessEnvironment(environment, application);}
}// 获取所有的 EnvironmentPostProcessor
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,ConfigurableBootstrapContext bootstrapContext) {ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;//postProcessorsFactory 是一个函数表达式EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}

EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);中的postProcessorsFactory是在EnvironmentPostProcessorApplicationListener实例化的时候初始化,根据前面的文章我们知道EnvironmentPostProcessorApplicationListener是一个监听器,会在SpringBoot初始化的时候初始化。

public EnvironmentPostProcessorApplicationListener() {this(EnvironmentPostProcessorsFactory::fromSpringFactories);
}private EnvironmentPostProcessorApplicationListener(Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory) {this.postProcessorsFactory = postProcessorsFactory;this.deferredLogs = new DeferredLogs();
}static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {return new SpringFactoriesEnvironmentPostProcessorsFactory(SpringFactoriesLoader.forDefaultResourceLocation(classLoader));
}

EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) 会在EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader); apply的时候调用,如果没有加载META-INF/spring.factories会再这里再次加载。

EnvironmentPostProcessorsFactory 的主要作用是实例化EnvironmentPostProcessorSpringFactoriesEnvironmentPostProcessorsFactory是其子类。

SpringFactoriesEnvironmentPostProcessorsFactory类中:

public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,ConfigurableBootstrapContext bootstrapContext) {ArgumentResolver argumentResolver = ArgumentResolver.of(DeferredLogFactory.class, logFactory);//向argumentResolver对象中添加ConfigurableBootstrapContext.class和bootstrapContext,获取更新后的argumentResolver对象argumentResolver = argumentResolver.and(ConfigurableBootstrapContext.class, bootstrapContext);// // 向argumentResolver对象中添加BootstrapRegistry.class和bootstrapContext,获取更新后的argumentResolver对象argumentResolver = argumentResolver.and(BootstrapContext.class, bootstrapContext); 通过this.loader.load方法加载EnvironmentPostProcessor类型的对象,参数为argumentResolverargumentResolver = argumentResolver.and(BootstrapRegistry.class, bootstrapContext);//加载EnvironmentPostProcessor类型的对象return this.loader.load(EnvironmentPostProcessor.class, argumentResolver);
}

最后循环调用postProcessor.postProcessEnvironment(environment, application);完成执行。

总结

同样的,用一张图来总结本文整个流程:
在这里插入图片描述


作者其他文章:
Prometheus 系列文章

  1. Prometheus 的介绍和安装
  2. 直观感受PromQL及其数据类型
  3. PromQL之选择器和运算符
  4. PromQL之函数
  5. Prometheus 告警机制介绍及命令解读
  6. Prometheus 告警模块配置深度解析
  7. Prometheus 配置身份认证
  8. Prometheus 动态拉取监控服务
  9. Prometheus 监控云Mysql和自建Mysql

Grafana 系列文章,版本:OOS v9.3.1

  1. Grafana 的介绍和安装
  2. Grafana监控大屏配置参数介绍(一)
  3. Grafana监控大屏配置参数介绍(二)
  4. Grafana监控大屏可视化图表
  5. Grafana 查询数据和转换数据
  6. Grafana 告警模块介绍
  7. Grafana 告警接入飞书通知

Spring Boot Admin 系列

  1. Spring Boot Admin 参考指南
  2. SpringBoot Admin服务离线、不显示健康信息的问题
  3. Spring Boot Admin2 @EnableAdminServer的加载
  4. Spring Boot Admin2 AdminServerAutoConfiguration详解
  5. Spring Boot Admin2 实例状态监控详解
  6. Spring Boot Admin2 自定义JVM监控通知
  7. Spring Boot Admin2 自定义异常监控
  8. Spring Boot Admin 监控指标接入Grafana可视化

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

团体程序设计天梯赛-练习集L2篇⑨

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

python用flask将视频显示在网页上

注意我们的return返回值必须是以下之一&#xff0c;否则会报错 from flask import Flask, render_template, Response import cv2app Flask(__name__)app.route(/) def index():return render_template(index.html)def gen(camera):while True:success, image camera.read(…

UE5 Motion Warping功能学习

MotionWarping&#xff08;运动扭曲&#xff09;可对角色根运动进行修改&#xff0c;从而让角色根运动动画结束时准确停在某一点&#xff0c;如图&#xff1a; 此外UE5还提供移动步幅、转向的Warping功能&#xff08;防滑步&#xff09;&#xff0c;之前写过一个简单的介绍可…

如何设计一个高并发系统?

其实所谓的高并发&#xff0c;如果你要理解这个问题呢&#xff0c;其实就得从高并发的根源出发&#xff0c;为啥会有高并 发&#xff1f;为啥高并发就很牛逼&#xff1f; 浅显一点&#xff0c;很简单&#xff0c;就是因为刚开始系统都是连接数据库的&#xff0c;但是要知道数据…

基於Hadoop HA 在kerberos中配置datax

概要 提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 概要 前言一、基於HADOOP HA 搭建datax二、基於HADOOP HA 配置好的datax去配置kerberos1.在datax的配置文件中進行配置2.在shell腳本中加入認證語句 总结 前言…

LENOVO联想笔记本拯救者Legion R7000P APH8 2023款(82Y9)原厂Windows11系统原装出厂状态预装系统

lenovo联想笔记本电脑&#xff0c;拯救者Legion R7000P APH8(2023款)(82Y9)原装出厂Windows11系统安装&#xff0c;预装系统重装镜像&#xff0c;恢复出厂状态 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上…

Mysql的主从复制和读写分离

一、Mysql读写分离的相关知识 1.什么是读写分离&#xff1f; 读写分离&#xff0c;基本的原理是让主数据库处理事务性增、改、删操作( INSERT、UPDATE、DELETE) &#xff0c;而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。 …

人体姿态估计技术的理解(Human Pose Estimination)

本人毕设题目是人体姿态估计技术的相关课题&#xff0c;本人按照自己对人体姿态估计技术的学习和理解进行论述&#xff0c;如有不足&#xff0c;请大家指正&#xff01;&#xff01;&#xff01; 首先讨论一个问题&#xff1a;什么是姿态估计? “姿势估计?……姿势这个词对…

C++基础-命名空间-缺省参数-函数重载-引用-内联-auto-范围for

C基础&#xff1a; 1. 命名空间命名空间如何定义命名空间如何使用 2.缺省参数2.1 缺省参数分类2.1.1全缺省参数2.1.2 半缺省参数 3. 函数重载4. 引用4.1 常引用4.2 引用使用场景4.2.1 做参数4.2.2 做返回值4.2.3 传值和传引用效率分析 4.3 引用和指针的区别4.4 指针和引用总结 …

ubuntu18修改源

1. 查看当前系统的源 系统的源 2. 将sources.list备份&#xff0c;sources-bak.list是备份文件 3. 选择要换的源 # 默认注释了源码镜像以提高 apt update 速度&#xff0c;如有需要可自行取消注释 deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted un…

kettle源码远程debug调试

一、kettle启动时指定debug端口号 windows下&#xff0c;修改bat执行文件&#xff0c;同理 linux修改sh执行文件 在java执行参数的末尾添加debug参数 address为debug端口 -Xdebug -Xnoagent -Djava.compilerNONE -Xrunjdwp:transportdt_socket,servery,suspendn,address9080然…

6 应用层-6.1【实验】【计算机网络】

6 应用层-6.1【实验】【计算机网络】 前言推荐6 应用层6.1 Web服务与FTP服务配置0 搭建拓扑图1 Web演示2 FTP演示6.1.1实验章节测验 最后 前言 2023-6-25 14:35:53 以下内容源自《创作模板三》 仅供学习交流使用 推荐 4端到端协议-4.3【实验】【计算机网络】 6 应用层 6.…