SpringBoot的自动装配

news2025/4/19 20:17:35

前言

众所周知,SpringBoot的自动装配是其核心功能之一,SpringBoot提供了许多自动配置类,我们通常会有这样的一个概念:“当应用程序启动时,SpringBoot会扫描路径上的自动配置类进行加载,从而大大简化了项目配置的工作”,在这里,我们来从代码来学习下自动化装配的原理以及流程;文章将尽力地解答如下两个问题:

  • 关于自动装配的bean,尤其是非开发人员所开发的外部资源,是在哪里配置的?
  • 而这些配置信息,是在哪里读取解析,并注册到工程中的?

这两个问题很快会有答案~

关于在哪里配置的问题

首先关于第一个问题,我们需要了解下SpringBoot的SpringFactoriesLoader,SpringFactoriesLoader是SpringBoot定义的通用工厂加载机制,我们可以从源码上看下相关的介绍:

/**
 * General purpose factory loading mechanism for internal use within the framework.
 *
 * <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates
 * factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which
 * may be present in multiple JAR files in the classpath. The {@code spring.factories}
 * file must be in {@link Properties} format, where the key is the fully qualified
 * name of the interface or abstract class, and the value is a comma-separated list of
 * implementation class names. For example:
 *
 * <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>
 *
 * where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1}
 * and {@code MyServiceImpl2} are two implementations.
 *
 * @author Arjen Poutsma
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 3.2
 */

即,SpringBoot会在classpath下的多个jar包的特定位置(META-INF目录),读取配置文件spring.factories文件,spring.factories的文件内容需要严格地遵循KV配置文件的格式 ;

有了初步的了解之后,那么SpringFactoriesLoader是在哪里使用的呢?

SpringFactoriesLoader在自动装配中有不止一个地方会使用,两个重要的使用点,其一是在SpringApplication的构造阶段;其二是针对EnableAutoConfiguration注解,在后续的postProcessor处理时(会在问题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));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

接下来我们追踪到私有方法getSpringFactoriesInstances

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

这里,我们看到了SpringFactoriesLoader的调用!

接下来我们来到SpringFactoriesLoader的内部看看:

	/**
	 * Load the fully qualified class names of factory implementations of the
	 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
	 * class loader.
	 * @param factoryClass the interface or abstract class representing the factory
	 * @param classLoader the ClassLoader to use for loading resources; can be
	 * {@code null} to use the default
	 * @throws IllegalArgumentException if an error occurs while loading factory names
	 * @see #loadFactories
	 */
	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

而方法中配置的静态常量,就是META-INF/spring.factories了:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

接下来我们可以观察下手中的SpringBoot项目中已经包含的META-INF/spring.factories中配置的内容,我们通常会很容易地找到EnableAutoConfiguration的配置,例如:

不难发现,org.springframework.boot.autoconfigure.EnableAutoConfiguration的values,大多都是SpringBoot的配置类(@Configuration),那么这些配置类,是在哪里被调用,进而对其配置的bean进行加载的呢?下面我们来回答文章开始部分提到的第二个问题。

 

关于在哪里解析的问题

关于第二个问题,我们先从结论出发,它的实现是在ConfigurationClassPostProcessor这个类中的postProcessBeanDefinitionRegistry方法。ConfigurationClassPostProcessor是一个关键的后置处理器,它的主要作用是:能够解析和处理配置类中的注解和配置信息,包括@Bean方法的注册,@Import注解的处理,条件注解的判断和依赖注入的解析。

 如果我们在SpringBoot的工程中进行断点调试,我们可以发现它的源头其实也是源自Spring/SpringBoot源码中的AbstractApplicationContext#refresh方法,这个大名鼎鼎的refresh方法是spring/springboot源码学习最核心的部分了,其内容十分地丰富,网上也有不少相关的学习资源,关于refresh方法本人在学习的过程中也进行了一些记录 https://mp.csdn.net/mp_blog/creation/editor/104491404;自动化装配,是在refresh方法的invokeBeanFactoryPostProcessors方法中被执行的

refresh方法的源码如下:

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

而自动化装配的核心步骤:invokeBeanFactoryPostProcessors方法,我们主要对它来进行跟踪:

	/**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before singleton instantiation.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
		if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

我们发现这个方法直接调用了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors

方法;

	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

		// Invoke BeanDefinitionRegistryPostProcessors first, if any.
		Set<String> processedBeans = new HashSet<>();

		if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}

			// Do not initialize FactoryBeans here: We need to leave all regular beans
			// uninitialized to let the bean factory post-processors apply to them!
			// Separate between BeanDefinitionRegistryPostProcessors that implement
			// PriorityOrdered, Ordered, and the rest.
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();

			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();

			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
			boolean reiterate = true;
			while (reiterate) {
				reiterate = false;
				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				for (String ppName : postProcessorNames) {
					if (!processedBeans.contains(ppName)) {
						currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
						processedBeans.add(ppName);
						reiterate = true;
					}
				}
				sortPostProcessors(currentRegistryProcessors, beanFactory);
				registryProcessors.addAll(currentRegistryProcessors);
				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
				currentRegistryProcessors.clear();
			}

			// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
		}

		else {
			// Invoke factory processors registered with the context instance.
			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

		// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
			if (processedBeans.contains(ppName)) {
				// skip - already processed in first phase above
			}
			else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
		for (String postProcessorName : orderedPostProcessorNames) {
			orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

		// Finally, invoke all other BeanFactoryPostProcessors.
		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
		for (String postProcessorName : nonOrderedPostProcessorNames) {
			nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

		// Clear cached merged bean definitions since the post-processors might have
		// modified the original metadata, e.g. replacing placeholders in values...
		beanFactory.clearMetadataCache();
	}

其中有一行方法:invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 我们继续进行跟踪

	/**
	 * Invoke the given BeanDefinitionRegistryPostProcessor beans.
	 */
	private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanDefinitionRegistry(registry);
		}
	}

可以发现,这个方法中,会将所有注册的BeanDefinitionRegistryPostProcessor类进行遍历,并执行它们的postProcessBeanDefinitionRegistry方法。

在作者的调试的时候,我们可以看到此处注册的BeanDefinitionRegistryPostProcessor有且只有一个,那就是ConfigurationClassPostProcessor。

 在这里,我们成功地跟踪到了BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法

	/**
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);

		processConfigBeanDefinitions(registry);
	}

其中在最后,会执行一个processConfigBeanDefinitions方法,而这个方法就是处理配置类的核心方法了。这个方法比较复杂,内容很多,下一篇博文中,我们再来对这个方法进行下更深入的分享。

 在前文我们提到@Import注解会在BeanDefinitionRegistryPostProcessor处理,这里有一个关键的注解,相信我们对@EnableAutoConfiguration这个注解并不陌生;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

@Import(AutoConfigurationImportSelector.class)这里会引入一个AutoConfigurationImportSelector类,我们看下它的getAutoConfigurationEntry方法

	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

它会调用同类的getCandidateConfigurations方法

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

在这里我们再一次看到了SpringFactoriesLoader的调用,这里要和上一个问题中SpringApplication对象构造时对SpringFactoriesLoader的调用进行区分哦。

 

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

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

相关文章

小白量化《穿云箭集群量化》(6)巡航导弹策略

小白量化《穿云箭集群量化》&#xff08;6&#xff09;响尾蛇导弹 响尾蛇导弹是非常著名的武器装备&#xff0c;响尾蛇导弹发射者只需雷达瞄准和发射动作&#xff0c;发射动作完成尽快脱离战场保全自身安全。响尾蛇导弹会自动追踪敌机&#xff0c;直至击毁敌机。 证券交易犹如…

MySQL基础(三十一)数据库其它调优策略

1 数据库调优的措施 1.1 调优的目标 尽可能 节省系统资源 &#xff0c;以便系统可以提供更大负荷的服务。&#xff08;吞吐量更大&#xff09;合理的结构设计和参数调整&#xff0c;以提高用户操作 响应的速度 。&#xff08;响应速度更快&#xff09;减少系统的瓶颈&#xf…

day37_jdbc

今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 零、 复习昨日 见晨考 一、作业 package com.qf.homework;import com.qf.model.User;import java.sql.Connection; import java.sql.DriverManager; impo…

【笔试强训】(红与黑,五子棋,走迷宫)DFS+BFS算法解析

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: 笔试强训专栏 深度优先遍历&#xff08;Depth First Search, 简称 DFS&#xff09; 与广度优先遍历&#xff08;Breath First Search&#xff09;是图论中两种非常重要的算法。 本文就以习题的方式来给…

STM32F10X--EXTI--外部中断/事件控制器

一、EXTI是什么&#xff1f; EXTI&#xff08;External interrupt/event controller&#xff09;—外部中断/事件控制器&#xff0c;管理了控制器的20 个中断/事 件线。每个中断/事件线都对应有一个边沿检测器&#xff0c;可以实现输入信号的上升沿检测和下降沿的 检测。EXTI 可…

SpringMVC的基础知识

创建SpringMVC项目 SpringMVC项目其实和SpingBoot项目差不多,就多引入了一个SpringWeb项目而已拉 可以看这篇博客,创建的就是一个SpringMVC项目--创建项目の博客 SpringMVC是啥 Spring是啥相信大家都了解 啥是MVC呢?MVC是Model View Controller的缩写 我们分开看这三个词Model…

【框架源码】Spring源码核心注解@Conditional原理及应用

1.什么是Conditional注解 Conditional来源于spring-context包下的一个注解。通过Conditional配置一些条件判断&#xff0c;当所有条件都满足时&#xff0c;被该Conditional注解标注的目标才会被Spring处理。 例如根据当前环境、系统属性、配置文件等条件来决定是否注册某个Bea…

PostgreSQL-如何创建并发索引

索引简介 索引是数据库中一种快速查询数据的方法。索引中记录了表中的一列或多列值与其物理位置之间的对应关系&#xff0c;就好比一本书前面的目录&#xff0c;通过目录中页码就能快速定位到我们需要查询的内容。 建立索引的好处是加快对表中记录的查找或排序&#xff0c;但…

Mysql进阶-索引事务相关

文章目录 数据库存储引擎INNODBMYISAM 索引索引分类索引语法SQL性能分析SQL执行频率慢查询profile详情explain 执行计划**Etrax**(额外信息&#xff09;using index conditionusing where&#xff1b;using indexusing where 索引使用规则最左前缀法则范围查询 索引失效情况1.索…

conda故障处理

【已解决】subprocess-exited-with-error 准备元数据(setup.py)…错误 错误:subprocess-exited-with-error python setup.py egg_info运行失败。 │退出代码:10 <╰>[1行输出] 请指定——curl-dir/path/to/built/libcurl [输出结束] 注意:此错误源自子进程&#xf…

Halcon 形态学(膨胀(Dilation)、腐蚀(Erosion))

文章目录 1 形态学概念2 膨胀(Dilation) 算子介绍3 腐蚀(Erosion)算子介绍4 膨胀腐蚀 示例15 腐蚀膨胀 示例26 示例原图7 补充:结构元素概念1 形态学概念 图像的形态学处理是对图像的局部像素进行处理,用于从图像中提取有意义的局部图像细节。 通过改变局部区域的像素形态…

单链表OJ题:LeetCode--面试题:02.04 分割链表

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;今天给大家带来的是LeetCode面试题&#xff1a;02.04.分割链表 数 据 结 构&#xff1a;数据结构专栏 作 者&#xff1a;stackY、 LeetCode &#xff1a;LeetCode刷题训练营 LeetCode面试题&#xff1a;02.04.分割…

H5微信授权登录弹窗提示

如下图&#xff1a;用户授权登录前&#xff0c;先通过静默授权&#xff0c;拿到token&#xff0c;展示部分信息&#xff0c;用户通过授权后拿到头像昵称&#xff0c;该弹窗让用户有个比较好的体验 1、标签 <template><!--遮罩--><view v-if"showAuth"…

MD-MTSP:遗传算法GA求解多仓库多旅行商问题(提供MATLAB代码,可以修改旅行商个数及起点)

一、多仓库多旅行商问题 多旅行商问题&#xff08;Multiple Traveling Salesman Problem, MTSP&#xff09;是著名的旅行商问题&#xff08;Traveling Salesman Problem, TSP&#xff09;的延伸&#xff0c;多旅行商问题定义为&#xff1a;给定一个&#x1d45b;座城市的城市集…

开发一个自定义“套壳“浏览器的开源方案

一.项目概述 二.技术选型 三.项目介绍 1.项目地址:​​​​​​https​​​​​​://github.com/keyxh/TLC_Browers 2.项目目录介绍: 3.项目后期 开发语言:VB6 浏览器内核:webview2 项目目的:在vb6调用h5&#xff0c;实现自定义的浏览器 参考资料: https://github.com…

从 Spring 的创建到 Bean 对象的存储、读取

目录 创建 Spring 项目&#xff1a; 1.创建一个 Maven 项目&#xff1a; 2.添加 Spring 框架支持&#xff1a; 3.配置资源文件&#xff1a; 4.添加启动类&#xff1a; Bean 对象的使用&#xff1a; 1.存储 Bean 对象&#xff1a; 1.1 创建 Bean&#xff1a; 1.2 存储 B…

BUUCTF--reverse1,reverse2--WP

文章目录 一.BUUCTF--reverse1二.BUUCTF--reverse2 一.BUUCTF–reverse1 这道题目也是非常简单&#xff0c;主要考察IDA Pro的使用&#xff0c;分析代码&#xff1a; 发现是64位exe&#xff0c;直接拖到IDA Pro中&#xff0c;发现没有找到主函数&#xff1a; 那就直接ShiftF12…

【操作符详解 2023-05-13】

#include <stdio.h>int main() {int a 1;int b 2;int c (a > b, a b 10, a, b a 1);//逗号表达式printf("%d\n", a);printf("%d\n", b);printf("%d\n", c);return 0; }int main() {int arr[10] { 1,2,3,4,5 };// …

等差数列求和,轻松解决力扣“K 个元素的最大和”问题

本篇博客会讲解力扣“2656. K 个元素的最大和”的解题思路&#xff0c;这是题目链接。 先来审题&#xff1a; 以下是输出示例&#xff1a; 以下是提示&#xff1a; 本题有2种思路&#xff0c;一种是直接按照题目所说的方式模拟&#xff0c;另一种是直接使用数学公式。 显然…

STM32f103时钟树详解

一、概述 stm32有四种时钟信号源&#xff0c;HSE(高速外部时钟)、HSI&#xff08;高速内部时钟&#xff09;、LSE&#xff08;低速外部时钟&#xff09;、LSI&#xff08;低速内部时钟&#xff09;。HSE通常接8M晶振&#xff0c;经由内部分频和倍频&#xff0c;最大可得到72MH…