『SpringBoot 源码分析』run() 方法执行流程:(2)刷新应用上下文-准备阶段

news2024/11/19 13:27:47

『SpringBoot 源码分析』run() 方法执行流程:(2)刷新应用上下文-准备阶段

  • 基于 2.2.9.RELEASE
  • 问题:当方法进行了注释标记之后,springboot 又是怎么注入到容器中并创建类呢?
  1. 首先创建测试主程序
package com.lagou;

@SpringBootApplication//标注在类上说明这个类是`SpringBoot`的主配置类
public class SpringBootMytestApplication{

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMytestApplication.class, args);
	}
}
  1. 创建测试 Controller
package com.lagou.controller;

@RestController
public class TestController {

	@RequestMapping("/test")
	public String test(){
		System.out.println("源码环境构建成功...");
		return "源码环境构建成功";
	}
}

准备阶段

  1. 当准备完成应用上下文环境,以及应用上下文以后,需要为应用上下文做个准备阶段,简单来说其实就是要配置应用上下文,把需要的类装配上
public class SpringApplication {
	...
	public ConfigurableApplicationContext run(String... args) {
		...
		try {
			// 将运行时参数封装
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 构造应用上下文环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			// 处理需要忽略的 Bean
			configureIgnoreBeanInfo(environment);
			// 打印 banner
			Banner printedBanner = printBanner(environment);
			// 刷新应用上下文前的准备阶段
			context = createApplicationContext();
			// 实例化 SpringBootExceptionReporter.class,用来支持报告关于启动的错误
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 1. 刷新应用上下文前的准备阶段
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			...
		}
		catch (Throwable ex) {
			...
		}
		...
	}
}
  1. 在对应用上下文进行处理时,主要执行了下面几步的装配
  • 把上下文环境设置到应用上下文中
  • 执行容器后置处理
  • 把应用上下文交给 SpringApplication 初始化收集的 org.springframework.context.ApplicationContextInitializer 所有实现类进行初始化工作
  • 利用 org.springframework.boot.context.event.EventPublishingRunListenerorg.springframework.context.ApplicationListener 发布容器准备好事件
public class SpringApplication {
	...
	// 完成属性设置 bean对象创建
	private void  prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// 1. 设置容器环境
		context.setEnvironment(environment);
		// 2. 执行容器后置处理
		postProcessApplicationContext(context);
		// 3. 执行一些初始化器
		applyInitializers(context); 
		// 4. 向各个监听器发送容器已经准备好的事件
		listeners.contextPrepared(context);
		...
	}
}
  • 执行容器后置处理:其实只是往 BeanFactory 添加了基础的转换器
public class SpringApplication {

	private BeanNameGenerator beanNameGenerator;
	private ResourceLoader resourceLoader;
	private boolean addConversionService = true;
	...
	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
		
		if (this.addConversionService) { 
			// 1. 设置了转换器,例如平时能把整数字符串转换为整形,设置转换器
			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
		}
	}
}
  • 执行一些初始化器:就是遍历 org.springframework.context.ApplicationContextInitializer 执行 initialize() 方法
public class SpringApplication {
	...
	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}
}
  • 向各个监听器发送容器已经准备好的事件:就是 org.springframework.boot.context.event.EventPublishingRunListenerorg.springframework.context.ApplicationListener 发布容器准备好事件
public class SpringApplication {
	...
	// 1. 当 ApplicationContext 构建完成时,该方法被调用
	void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}
}
  1. 发布完监听之后,从上下文中获取 IOC 工厂,并设置允许 bean 定义被覆盖参数
public class SpringApplication {
	...
	// 完成属性设置 bean 对象创建
	private void  prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// 设置容器环境
		context.setEnvironment(environment);
		// 执行容器后置处理
		postProcessApplicationContext(context);
		// 执行一些初始化器
		applyInitializers(context); 
		// 向各个监听器发送容器已经准备好的事件
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// 1. 获取 IOC 容器
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 将 main() 函数中的 args 参数封装成单例 Bean,注册进容器
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			// 将 printedBanner 也封装成单例,注册进容器
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		// 2. 设置允许 bean 定义被覆盖参数
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		...
	}
}
  1. 然后加载启动类,并将启动类注入到容器当中,然后发布容器已加载事件
public class SpringApplication {
	...
	// 完成属性设置 bean 对象创建
	private void  prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// 设置容器环境
		context.setEnvironment(environment);
		// 执行容器后置处理
		postProcessApplicationContext(context);
		// 执行一些初始化器
		applyInitializers(context); 
		// 向各个监听器发送容器已经准备好的事件
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// 获取 IOC 容器
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 将 main() 函数中的 args 参数封装成单例 Bean,注册进容器
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			// 将 printedBanner 也封装成单例,注册进容器
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		// 设置允许 bean 定义被覆盖参数
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) { // 是否需要进行懒加载,这里不是
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// 1. 加载源,这里拿到的是主类,com.lagou.SpringBootMytestApplication
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 2. 加载我们的启动类,将启动类注入容器 重点关注
		load(context, sources.toArray(new Object[0]));// 取出第一个元素,就是主类,要先实例化主类,灌入容器中
		// 3. 发布容器已加载事件
		listeners.contextLoaded(context);
	}
}
  • 其中将启动类注入到容器当中是比较关键的一步,首先先把 ApplicationContext 转换成 BeanDefinitionRegistry,然后创建 bean 定义加载器
public class SpringApplication {
	...
	protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		// 2. 创建 BeanDefinitionLoader
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		...
	}
	
	private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
		// 1. 将 ApplicationContext 转换成 BeanDefinitionRegistry
		if (context instanceof BeanDefinitionRegistry) {
			return (BeanDefinitionRegistry) context;
		}
		if (context instanceof AbstractApplicationContext) {
			return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
		}
		throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
	}
}
  • 创建 BeanDefinitionLoader 主要是把 BeanDefinitionRegistry 以及主类的 sources 进行赋值初始化
public class SpringApplication {

	private final Object[] sources;
	private final AnnotatedBeanDefinitionReader annotatedReader;
	private final XmlBeanDefinitionReader xmlReader;
	private BeanDefinitionReader groovyReader;
	private final ClassPathBeanDefinitionScanner scanner;
	private ResourceLoader resourceLoader;
	...
	protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
		// 1. 创建 bean 定义加载器
		return new BeanDefinitionLoader(registry, sources);
	}

	BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		// 其中,sources 就是主类 com.lagou.SpringBootMytestApplication
		Assert.notNull(registry, "Registry must not be null");
		Assert.notEmpty(sources, "Sources must not be empty");
		this.sources = sources;
		// 2. 注解形式的 Bean 定义读取器 比如:@Configuration @Bean @Component @Controller @Service等等
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
		// 3. XML 形式的 Bean 定义读取器
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
		// 4. 类路径扫描器
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		// 5. 扫描器添加排除过滤器
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
	}
}
  1. 创建 BeanDefinitionLoader 成功后,就可以开始执行 load(),这里主要是先把主类注册到 IOC 容器中去
public class SpringApplication {

	private BeanNameGenerator beanNameGenerator;
	...
	protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		// 创建 BeanDefinitionLoader
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
		// 1. 执行 load()
		loader.load();
	}
}
  • 其中 load() 通过 AnnotatedBeanDefinitionReader 将主类 source 注册进 beanDefinitionMap
class BeanDefinitionLoader {
	
	private final AnnotatedBeanDefinitionReader annotatedReader;
	...
	int load() {
		int count = 0;
		for (Object source : this.sources) {
			count += load(source);
		}
		return count;
	}
	// 根据加载不同类型调用不同的方法
	private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		// 1. 从 Class 加载
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		...
	}

	private int load(Class<?> source) {
		// source 就是 class com.lagou.SpringBootMytestApplication
		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
			// Any GroovyLoaders added in beans{} DSL can contribute beans here
			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
			load(loader);
		}
		if (isComponent(source)) { // 判断方法有没有标记 Component 注解
			// 1. 将启动类的 BeanDefinition 注册进 beanDefinitionMap
			this.annotatedReader.register(source);
			return 1;
		}
		return 0;
	}
}
  • 调用 register() 方法时,真正会传到 doRegisterBean() 进行执行。首先会把主类转换成 AnnotatedGenericBeanDefinition,然后获取主类的名称,把名称和 AnnotatedGenericBeanDefinition 封装成 BeanDefinitionHolder 后,注册到上下文中
public class AnnotatedBeanDefinitionReader {
	...
	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}

	public void registerBean(Class<?> beanClass) {
		doRegisterBean(beanClass, null, null, null, null);
	}

	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {
		// 1. 把主类转换成 AnnotatedGenericBeanDefinition,其中 beanClass 为 com.lagou.SpringBootMytestApplication
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(supplier);
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		// 2. 获取类名 springBootMytestApplication
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		if (customizers != null) {
			for (BeanDefinitionCustomizer customizer : customizers) {
				customizer.customize(abd);
			}
		}
		// 3. 封装成 BeanDefinitionHolder 
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		// 4. 注册到容器中		
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
}
  • 注册到容器中,简单而言就是调用 BeanDefinitionRegistryBeanDefinitionHolder 的 name 和 AnnotatedGenericBeanDefinition 装配进去
public abstract class BeanDefinitionReaderUtils {
	...
    public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
    	// 1. 获取名称 springBootMytestApplication
        String beanName = definitionHolder.getBeanName();
        // 2. 注册到 IOC 容器中
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);
            }
        }
    }
}
  • 最后会把 name 和 AnnotatedGenericBeanDefinition 存入到 beanDefinitionMap
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
	...
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
        // 1. 先校验 beanDefinition 是否合法
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition)beanDefinition).validate();
            } catch (BeanDefinitionValidationException var8) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);
            }
        }

        BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            if (!this.isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }

            if (existingDefinition.getRole() < beanDefinition.getRole()) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
                }
            } else if (!beanDefinition.equals(existingDefinition)) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
                }
            } else if (this.logger.isTraceEnabled()) {
                this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
            }

            this.beanDefinitionMap.put(beanName, beanDefinition);
        } else {
            if (this.hasBeanCreationStarted()) {
                synchronized(this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    this.removeManualSingletonName(beanName);
                }
            } else {
                // 2. 再把 beanDefinition 放到 beanDefinitionMap 中
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.removeManualSingletonName(beanName);
            }

            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition == null && !this.containsSingleton(beanName)) {
            if (this.isConfigurationFrozen()) {
                this.clearByTypeCache();
            }
        } else {
            this.resetBeanDefinition(beanName);
        }
    }	
}
  1. 总结
    在这里插入图片描述

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

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

相关文章

第49节:cesium 倾斜模型osgb转3dtiles,并加载(含源码+视频)

结果示例: 完整步骤: 1、启动并登陆cesiumlab 2、准备OSGB模型数据(含下载地址) 链接:https://pan.quark.cn/s/46ac7b0b2bed 提取码:TvWL3、倾斜模型切片 选择倾斜模型data文件夹 空间参考、零点坐标 默认 强制双面关闭、无光照 打开

天津和则百顺国际贸易有限公司被选为中国自主创新企业

在2023年4月,天津和则百顺国际贸易有限公司凭借在全国自主创新企业宣传推广活动中的出色表现,经过相关单位审核,正式被评选为中国自主创新企业,并荣获《中国自主创新企业》荣誉证书。 作为始终走在中国自主创新前沿的企业,天津和则百顺国际贸易有限公司,以下简称和则百顺,对于获…

console.log封装,显示调用的位置

背景 一般我们都哦说直接调用console.log&#xff0c;但是有时候console.log太多了&#xff0c;非常影响效率&#xff0c;我们想统一开启console.log&#xff08;不是生产环境移除console.log&#xff09; 可能封装的代码是这样 window.mylog()>conosle.log但是控制面板哪…

【大数据】Flink 详解(六):源码篇 Ⅰ

Flink 详解&#xff08;六&#xff09;&#xff1a;源码篇 Ⅰ 55、Flink 作业的提交流程&#xff1f;56、Flink 作业提交分为几种方式&#xff1f;57、Flink JobGraph 是在什么时候生成的&#xff1f;58、那在 JobGraph 提交集群之前都经历哪些过程&#xff1f;59、看你提到 Pi…

命令执行漏洞复现攻击:识别威胁并加强安全

环境准备 这篇文章旨在用于网络安全学习&#xff0c;请勿进行任何非法行为&#xff0c;否则后果自负。 一、攻击相关介绍 原理 主要是输入验证不严格、代码逻辑错误、应用程序或系统中缺少安全机制等。攻击者可以通过构造特定的输入向应用程序或系统注入恶意代码&#xff…

sql:SQL优化知识点记录(七)

&#xff08;1&#xff09;索引优化5 &#xff08;2&#xff09;索引优化6 &#xff08;3&#xff09;索引优化7 查询*&#xff0c; 百分号加右边&#xff0c;否则索引会失效 没建立索引之前都是全表扫描 没建立索引 建立索引&#xff1a; 建立索引 id是主键&#xff0c;他也…

零信任安全模型详解:探讨零信任安全策略的原理、实施方法和最佳实践,确保在网络中实现最小特权原则

在当今日益复杂和危险的网络环境中&#xff0c;传统的网络安全模型已经不再能够满足对抗不断进化的威胁。零信任安全模型应运而生&#xff0c;以其强调“不信任&#xff0c;始终验证”的理念&#xff0c;成为了当今信息技术领域中的热门话题。本文将深入探讨零信任安全模型&…

(leetcode802,拓扑排序,深搜+三色标记)-------------------Java实现

&#xff08;leetcode802&#xff0c;拓扑排序&#xff0c;深搜三色标记&#xff09;找到最终的安全状态-------------------Java实现 题目表述 有一个有 n 个节点的有向图&#xff0c;节点按 0 到 n - 1 编号。图由一个 索引从 0 开始 的 2D 整数数组 graph表示&#xff0c;…

【RPC 协议】序列化与反序列化 | lua-cjson | lua-protobuf

文章目录 RPC 协议gRPCJSON-RPC 数据序列化与反序列化lua-cjsonlua-protobuf RPC 协议 在分布式计算&#xff0c;远程过程调用&#xff08;英语&#xff1a;Remote Procedure Call&#xff0c;缩写为 RPC&#xff09;是一个计算机通信协议。该协议允许运行于一台计算机的程序调…

Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录

Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录 目录 Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录 一、简单介绍 二、OKHttp 4.xx 的 SDK 封装 aar 给 Unity 的使用注意 三、附录 OKHttp 的…

一米ip流量池系统

PC端快速切换移动网络IP 支持全网通sim卡槽&#xff0c;国内三大运营商IP池动态切换&#xff0c;实现真实移动端IP切换。从此换IP再也不用vpn或代理&#xff0c;一个设备搞定 1.兼容国内电信&#xff0c;移动&#xff0c;联通三网通的sim卡4G连接&#xff0c;快速稳定2.可直接…

《算法竞赛·快冲300题》每日一题:“简化农场”

《算法竞赛快冲300题》将于2024年出版&#xff0c;是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码&#xff0c;以中低档题为主&#xff0c;适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 简…

SpringBoot自定义消息总线

一、前言 在现代的分布式系统中&#xff0c;消息传递已成为一个非常流行的模式。它使得系统内的不同部分可以松耦合地通信&#xff0c;从而实现更高效、更可靠的应用程序。本博客将介绍SpringBoot如何提供简单易用的消息传递机制&#xff0c;并展示如何自定义消息总线以满足特定…

安装使用 d3graph 时出现 TypeError 的解决方法

使用 python 3.7 pip 22.3.1 在清华镜像源 https://pypi.tuna.tsinghua.edu.cn/simple 安装 d3blocks 1.3.2 时&#xff0c;安装成功后导入包时出错&#xff1a; 观察报错信息可以看到出错的代码&#xff08;902 行&#xff09;使用了类型指定语法&#xff0c;这是最新的 pyth…

stable diffusion实践操作-电脑硬件查看

本文专门开一节写电脑硬件相关的内容&#xff0c;在看之前&#xff0c;可以同步关注&#xff1a; stable diffusion实践操作 正文 1、检查电脑显存的方法&#xff08;win10&#xff09;&#xff1a; 鼠标放在工具栏&#xff0c;单击右键打开“任务管理器”&#xff0c;选择顶…

jmeter 固定定时器

固定定时器&#xff08;Constant Timer&#xff09;是一个定时器元件&#xff0c;可以在线程组中的每个线程之间添加固定的延迟时间。固定定时器会对每个线程的执行进行一定的暂停。 聊一下和线程组中的调度器对线程组执行时长的影响&#xff1a; 相同&#xff1a; 都会影响线…

【线性代数】矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇)

矩阵求导的本质与分子布局、分母布局的本质&#xff08;矩阵求导——本质篇&#xff09; 说在前面一. 函数与标量、向量、矩阵二. 矩阵求导的本质三. 矩阵求导结果的布局四. 分子布局、分母布局的本质五. 向量变元的实值标量函数 说在前面 我将严谨地说明矩阵求导的本质与分子布…

游戏思考30(补充版):关于逆水寒铁牢关副本、白石副本和技能的一些注释(2023/0902)

前期介绍 我是一名逆水寒的玩家&#xff0c;做一些游戏的笔记当作攻略记录下来&#xff0c;荣光不朽-帝霸来源视频连接 传送门 一、旧版铁牢关&#xff08;非逆水寒老兵服&#xff09; &#xff08;1&#xff09;老一&#xff1a;巨鹰 1&#xff09;机制一&#xff1a;三阵风…

多通道振弦数据记录仪应用桥梁安全监测的关键要点

多通道振弦数据记录仪应用桥梁安全监测的关键要点 随着近年来桥梁建设和维护的不断推进&#xff0c;桥梁安全监测越来越成为公共关注的焦点。多通道振弦数据记录仪因其高效、准确的数据采集和处理能力&#xff0c;已经成为桥梁安全监测中不可或缺的设备。本文将从以下几个方面…

JavaScript基础02

JavaScript 基础 文章目录 JavaScript 基础运算符算术运算符赋值运算符自增/自减运算符比较运算符逻辑运算符运算符优先级 语句表达式和语句分支语句if 分支语句if双分支语句if 多分支语句三元运算符&#xff08;三元表达式&#xff09;switch语句&#xff08;了解&#xff09;…