一道经典面试题:@Configuration 和 @Component 有何区别?

news2025/1/19 11:17:36

关于 @Configuration 注解有一个特别经典的面试题:

  • @Configuration 和 @Component 有什么区别?

无论小伙伴们之前是否背过相关的面试题,今天这篇文章学完之后相信大家对这个问题都会有更深一层的理解,废话不多少,咱们开始分析。

1. 情景展现

@Configuration 和 @Component 到底有何区别呢?我先通过如下一个案例,在不分析源码的情况下,小伙伴们先来直观感受一下这两个之间的区别。

@Configuration
public class JavaConfig01 {
}
@Component
public class JavaConfig02 {
}

首先,分别向 Spring 容器中注入两个 Bean,JavaConfig01 和 JavaConfig02,其中,JavaConfig01 上添加的是 @Configuration 注解而 JavaConfig02 上添加的则是 @Component 注解。

现在,在 XML 文件中配置包扫描:

<context:component-scan 
base-package="org.javaboy.demo.p6"/>

最后,加载 XML 配置文件,初始化容器:

public class Demo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans_demo.xml");
        JavaConfig01 config01 = ctx.getBean(JavaConfig01.class);
        JavaConfig02 config02 = ctx.getBean(JavaConfig02.class);
        System.out.println("config01.getClass() = " + config01.getClass());
        System.out.println("config02.getClass() = " + config02.getClass());
    }
}

最终打印出来结果如下:

从上面这段代码中,我们可以得出来两个结论:

  1. @Configuration 注解也是 Spring 组件注解的一种,通过普通的 Bean 扫描也可以扫描到 @Configuration。
  2. @Configuration 注解注册到 Spring 中的 Bean 是一个 CGLIB 代理的 Bean,而不是原始 Bean,这一点和 @Component 不一样,@Component 注册到 Spring 容器中的还是原始 Bean。

一个问题来了,@Configuration 标记的类为什么注册到 Spring 容器之后就变成了代理对象了呢?闭着眼睛大家也能猜到,肯定是为了通过代理来增强其功能,那么究竟增强什么功能呢?接下来我们通过源码分析来和小伙伴们梳理一下这里的条条框框。

2. 源码分析

要理解这个问题,首先得结合我们前面的文章@Configuration 注解的 Full 模式和 Lite 模式!,在该文中,松哥提到了 @Configuration 模式分为了 Full 模式和 Lite 模式,所以,对于 @Configuration 注解的处理,在加载的时候,就需要首先区分出来是 Full 模式还是 Lite 模式。

负责 @Configuration 注解的是 ConfigurationClassPostProcessor,这个处理器是一个 BeanFactoryPostProcessor,BeanFactoryPostProcessor 的作用就是在 Bean 定义的时候,通过修改 BeanDefinition 来重新定义 Bean 的行为,这个松哥之前有过专门的文章介绍,不熟悉的小伙伴可以先看看这里:

  • Spring 中 BeanFactory 和 FactoryBean 有何区别?

同时,ConfigurationClassPostProcessor 也是 BeanDefinitionRegistryPostProcessor 的实例,BeanDefinitionRegistryPostProcessor 是干嘛的呢?

BeanDefinitionRegistryPostProcessor 是 Spring 框架中的一个接口,它的作用是在应用程序上下文启动时,对 BeanDefinitionRegistry 进行后置处理。具体来说,BeanDefinitionRegistryPostProcessor 可以用于修改或扩展应用程序上下文中的 BeanDefinition,即在 Bean 实例化之前对 BeanDefinition 进行修改。它可以添加、删除或修改 BeanDefinition 的属性,甚至可以动态地注册新的 BeanDefinition。通过实现 BeanDefinitionRegistryPostProcessor 接口,我们可以在 Spring 容器启动过程中干预 Bean 的定义,以满足特定的需求。这使得我们可以在应用程序上下文加载之前对 Bean 进行一些自定义的操作,例如动态注册 Bean 或者修改 Bean 的属性。需要注意的是,BeanDefinitionRegistryPostProcessor 在 BeanFactoryPostProcessor 之前被调用,因此它可以影响到 BeanFactoryPostProcessor 的行为。

BeanFactoryPostProcessor 中的方法是 postProcessBeanFactory,而 BeanDefinitionRegistryPostProcessor 中的方法是 postProcessBeanDefinitionRegistry,根据前面的介绍,postProcessBeanDefinitionRegistry 方法将在 postProcessBeanFactory 方法之前执行。

所以,我们就从 postProcessBeanDefinitionRegistry 方法开始看起吧~

2.1 postProcessBeanDefinitionRegistry

@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 方法,这个方法就是用来决策配置类是 Full 模式还是 Lite 模式的。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	String[] candidateNames = registry.getBeanDefinitionNames();
	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}
	//省略。。。
}

我省略了其他代码,大家看,这个方法中,会首先根据 beanName 取出来 BeanDefinition,然后判断 BeanDefinition 中是否包含 ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE 属性,这个属性上记录了当前配置类是 Full 模式还是 Lite 模式,不同模式将来的处理方案肯定也是不同的。如果是第一次处理,显然 BeanDefinition 中并不包含该属性,因此就会进入到 ConfigurationClassUtils.checkConfigurationClassCandidate 方法中,正是在该方法中,判断当前配置类是 Full 模式还是 Lite 模式,并进行标记,checkConfigurationClassCandidate 方法的逻辑也挺长的,我这里挑出来跟我们感兴趣的部分:

static boolean checkConfigurationClassCandidate(
		BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
        //省略。。。
	Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
	if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	else if (config != null || isConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		return false;
	}
    //省略
}

Full 模式情况很简单,就是如果配置类上存在 @Configuration 注解,并且该注解的 proxyBeanMethods 属性值不为 false,那么就是 Full 模式,这个跟松哥在 @Configuration 注解的 Full 模式和 Lite 模式!一文中的介绍是一致的。

Lite 模式就情况多一些,首先 config!=null 就是说现在也存在 @Configuration 注解,但是 proxyBeanMethods 属性值此时为 false,那么就是 Lite 模式(proxyBeanMethods 属性值为 true 的话就进入到 if 分支中了)。

另外就是在 isConfigurationCandidate 方法中有一些判断逻辑去锁定是否为 Lite 模式:

static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
	// Do not consider an interface or an annotation...
	if (metadata.isInterface()) {
		return false;
	}
	// Any of the typical annotations found?
	for (String indicator : candidateIndicators) {
		if (metadata.isAnnotated(indicator)) {
			return true;
		}
	}
	// Finally, let's look for @Bean methods...
	return hasBeanMethods(metadata);
}

这个方法的判断逻辑是这样:

  1. 首先注解要是标记的是接口,那就不能算是 Lite 模式。
  2. 遍历 candidateIndicators,判断当前类上是否包含这个 Set 集合中的注解,这个 Set 集合中的注解有四个,分别是 @Component、@ComponentScan、@Import、@ImportResource 四个,也就是,如果类上标记的是这四个注解的话,那么也按照 Lite 模式处理。
  3. 判断当前类中是否有 @Bean 标记的方法,如果有则按照 Lite 模式处理,否则就不是 Lite 模式。

如果小伙伴们看过松哥之前的 @Configuration 注解的 Full 模式和 Lite 模式!一文,那么上面这些代码应该都很好理解,跟松哥在该文章中的介绍都是一致的。

好了,经过上面的处理,现在就已经标 BeanDefinition 中标记了这个配置类到底是 Full 模式还是 Lite 模式了。

2.2 postProcessBeanFactory

接下来我们就来看 postProcessBeanFactory 方法。

/**
 * Prepare the Configuration classes for servicing bean requests at runtime
 * by replacing them with CGLIB-enhanced subclasses.
 */
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	int factoryId = System.identityHashCode(beanFactory);
	if (this.factoriesPostProcessed.contains(factoryId)) {
		throw new IllegalStateException(
				"postProcessBeanFactory already called on this post-processor against " + beanFactory);
	}
	this.factoriesPostProcessed.add(factoryId);
	if (!this.registriesPostProcessed.contains(factoryId)) {
		// BeanDefinitionRegistryPostProcessor hook apparently not supported...
		// Simply call processConfigurationClasses lazily at this point then.
		processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
	}
	enhanceConfigurationClasses(beanFactory);
	beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

首先大家看一下这个方法的注释,注释说的很明确了,将 Configuration 类通过 CGLIB 进行增强,以便在运行时较好的处理 Bean 请求。

这个方法中还会再次确认一下 postProcessBeanDefinitionRegistry 方法已经处理过了,如果没有处理的话,则会在该方法中调用 processConfigBeanDefinitions 去确认 Bean 使用的是哪种模式。

该方法的关键在于 enhanceConfigurationClasses,这个就是用来通过动态代理增强配置类的,当然这个方法也是比较长的,我这里列出来一些关键的逻辑:

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
	StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
	Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
	for (String beanName : beanFactory.getBeanDefinitionNames()) {
		BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
		Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
		if ((configClassAttr != null || methodMetadata != null) &&
				(beanDef instanceof AbstractBeanDefinition abd) && !abd.hasBeanClass()) {
			// Configuration class (full or lite) or a configuration-derived @Bean method
			// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
			// or component class without @Bean methods.
			boolean liteConfigurationCandidateWithoutBeanMethods =
					(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
						annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
			if (!liteConfigurationCandidateWithoutBeanMethods) {
				try {
					abd.resolveBeanClass(this.beanClassLoader);
				}
			}
		}
		if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
			configBeanDefs.put(beanName, abd);
		}
	}
	
	ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
	for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
		AbstractBeanDefinition beanDef = entry.getValue();
		// If a @Configuration class gets proxied, always proxy the target class
		beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
		// Set enhanced subclass of the user-specified bean class
		Class<?> configClass = beanDef.getBeanClass();
		Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
		if (configClass != enhancedClass) {
			beanDef.setBeanClass(enhancedClass);
		}
	}
	enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}

这个方法的逻辑,我整体上将之分为两部分:

第一部分就是先找到 Full 模式的配置类的名称,存入到 configBeanDefs 集合中。

具体寻找的逻辑就是根据配置类的模式去寻找,如果配置类是 Full 模式,就将之存入到 configBeanDefs 中。如果配置类是 Lite 模式,且里边没有 @Bean 标记的方法,那就说明这可能并不是一个配置类,就是一个普通 Bean,那么就在这里加载类就行了。

第二步则是遍历 configBeanDefs 集合,增强配置类。

这个如果大家了解 CGLIB 动态代理的话,这个就很好懂了,关于 CGLIB 动态代理松哥这里不啰嗦,最近更新的 Spring 源码视频中都有详细讲到。那么这里主要是通过 enhancer.enhance 方法来生成代理类的,如下:

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
	if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
		return configClass;
	}
	Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
	return enhancedClass;
}
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
	Enhancer enhancer = new Enhancer();
	enhancer.setSuperclass(configSuperClass);
	enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
	enhancer.setUseFactory(false);
	enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	enhancer.setAttemptLoad(true);
	enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
	enhancer.setCallbackFilter(CALLBACK_FILTER);
	enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
	return enhancer;
}

小伙伴们看到,增强类中的 setCallbackFilter 是 CALLBACK_FILTER,这个里边包含了几个方法拦截器,跟我们相关的是 BeanMethodInterceptor,我们来看下:

private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
	
	@Override
	@Nullable
	public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
				MethodProxy cglibMethodProxy) throws Throwable {
		ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
		String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
		// Determine whether this bean is a scoped-proxy
		if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
			String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
			if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
				beanName = scopedBeanName;
			}
		}
		// To handle the case of an inter-bean method reference, we must explicitly check the
		// container for already cached instances.
		// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
		// proxy that intercepts calls to getObject() and returns any cached bean instance.
		// This ensures that the semantics of calling a FactoryBean from within @Bean methods
		// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
		if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
				factoryContainsBean(beanFactory, beanName)) {
			Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
			if (factoryBean instanceof ScopedProxyFactoryBean) {
				// Scoped proxy factory beans are a special case and should not be further proxied
			}
			else {
				// It is a candidate FactoryBean - go ahead with enhancement
				return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
			}
		}
		if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
			return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
		}
		return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
	}
}

自己写过 CGLIB 动态代理的小伙伴都知道这里 intercept 方法的含义,这就是真正的拦截方法了,也就是说,如果我们的配置类是 Full 模式的话,那么将来调用 @Bean 注解标记的方法的时候,调用的其实是这里的 intercept 方法。

上面方法,首先会判断当前代理是否为作用域代理,我们这里当然不是。

接下来判断请求的 Bean 是否是一个 FactoryBean,如果是,则需要去代理其 getObject 方法,当执行到 getObject 方法的时候,就去 Spring 容器中查找需要的 Bean,当然,我们这里也不属于这种情况。

接下来判断当前正在执行的方法,是否为容器中正在调用的工厂方法。

例如我有如下代码:

@Configuration
public class JavaConfig {

    @Bean
    User user() {
        User user = new User();
        user.setDog(dog());
        return user;
    }

    @Bean
    Dog dog() {
        return new Dog();
    }
}

那么如果是直接调用 dog() 方法,则 isCurrentlyInvokedFactoryMethod 返回 true,如果是在 user() 方法中调用的 dog() 方法,则 isCurrentlyInvokedFactoryMethod 返回 false。

当 isCurrentlyInvokedFactoryMethod 返回 true 的时候,就执行 invokeSuper 方法,也就是真正的触发 dog() 方法的执行。

当 isCurrentlyInvokedFactoryMethod 返回 false 的时候,则执行下面的 resolveBeanReference 方法,这个方法会先去 Spring 容器中查找相应的 Bean,如果 Spring 容器中不存在该 Bean,则会触发 Bean 的创建流程。

现在,小伙伴们应该明白了为什么 Full 模式下,调用 @Bean 注解标记的方法并不会导致 Bean 的重复创建了吧~

好啦,本文结合上文 @Configuration 注解的 Full 模式和 Lite 模式! 一起食用效果更佳哦~

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

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

相关文章

地理空间数据的定义、收集和应用

简介 地理空间数据是指与地球表面特定位置相关的数据。它包含多种数据类型&#xff0c;包括地图、卫星图像和GIS&#xff08;地理信息系统&#xff09;数据。近年来&#xff0c;由于技术的进步以及对有关物理世界的准确和最新信息的需求不断增长&#xff0c;地理空间数据的…

9、监测数据采集物联网应用开发步骤(7)

监测数据采集物联网应用开发步骤(6) 串口(COM)通讯开发 本章节测试使用了 Configure Virtual Serial Port Driver虚拟串口工具和本人自写的串口调试工具&#xff0c;请自行baidu下载对应工具 在com.zxy.common.Com_Para.py中添加如下内容 #RS232串口通讯列表 串口号,波特率,…

恒运资本:股票佣金怎么算?

股票佣钱是指证券买卖中券商向出资者收取的手续费。股票买卖中&#xff0c;佣钱是不可避免的一项本钱&#xff0c;出资者需求清楚地了解佣钱的收费规范和怎么核算&#xff0c;以便更好地掌握出资本钱&#xff0c;做出更正确的出资决策。 佣钱收费规范 股票佣钱收费规范&#x…

来文心中国行!专家面对面解读大模型产业实践及AI场景突围

9月1日&#xff0c;文心中国行将走进武汉。政府、高校及企业AI专家将现场分享人工智能与大模型最新政策、趋势、人才培养方案及产业实践案例&#xff0c;深入解读如何抓住大模型时代新机遇&#xff0c;高效实现智能化转型升级弯道超车。 加快实现高水平科技自立自强&#xff0c…

融云获评「创业邦 · 最具创新价值出海服务商」

点击报名&#xff0c;9 月 21 日融云直播课 8 月 22 日 - 23 日&#xff0c;创业邦主办的“2023 DEMO WORLD 全球开放式创新大会暨企业出海未来大会”在上海举行&#xff0c;会上发布了“创业邦 2023 出海企业创新价值 100 强”&#xff0c;融云荣登榜单&#xff0c;获评“最具…

如何安装和使用TypeScript。

目录 安装nodenpm下载typescript使用TypeScript总结 TypeScript是一种流行的编程语言&#xff0c;它是JavaScript的超集&#xff0c;具有更多的扩展功能和类型安全性。在本文中&#xff0c;我们将介绍如何安装和使用TypeScript。 安装node 要安装TypeScript&#xff0c;您需要…

Linux 查看当前文件夹下的文件大小

1.直接查看: ll 或者 ls -la #查看文件大小&#xff0c;以kb为单位 ll#查看文件大小&#xff0c;包含隐藏的文件&#xff0c;以kb为单位 ls -la2.以 M 或者 G 为单位查看&#xff0c;根据文件实际大小进行合适的单位展示 du -sh *

第62步 深度学习图像识别:多分类建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 上期我们基于TensorFlow环境做了图像识别的多分类任务建模。 本期以健康组、肺结核组、COVID-19组、细菌性&#xff08;病毒性&#xff09;肺炎组为数据集&#xff0c;基于Pytorch环境&#xff0c;构建SqueezeNet多分类模型&#xff0…

蓝牙模块产品认证-国际市场准入准则之BQB认证认证基础知识

蓝牙模块产品认证-国际市场准入准则之BQB认证认证基础知识 前言 BQB认证介绍 Bluetooth SIG Bluetooth SIG 由八大无线通讯行业巨头成立的一家公司&#xff0c;专门负责蓝牙规格开发、 技术推广及资格认证工作,成立于1998年。 Bluetooth SIG拥有Bluetooth Trademarks 免费地授…

宇瞳转债上市价格预测

宇瞳转债 基本信息 转债名称&#xff1a;宇瞳转债&#xff0c;评级&#xff1a;A&#xff0c;发行规模&#xff1a;6.0亿元。 正股名称&#xff1a;宇瞳光学&#xff0c;今日收盘价&#xff1a;13.04元&#xff0c;转股价格&#xff1a;15.29元。 当前转股价值 转债面值 / 转股…

JavaScript this、闭包和箭头函数

this this是函数内部的特殊对象之一&#xff08;其他还有arguments、caller、new.target&#xff09;。 this的 指向 或 值 是不确定的&#xff0c;取决于函数的调用方式。 在JavaScript中&#xff0c;this的指向有以下几种情况&#xff1a; 作为对象的方法调用作为普通函数…

avalonia、WPF使用ScottPlot动态显示ECG心电图

文章目录 avalonia、WPF使用ScottPlot动态显示ECG心电图实现效果&#xff0c;动态效果懒得录视频了安装代码部分UpdateData方法就是用来更新心电图表的方法&#xff0c; 根据消息队列数据去更新是视图中的ScottPlot 图表 avalonia、WPF使用ScottPlot动态显示ECG心电图 avalonia…

linux系统离线安装python以及python包

效果 1、下载python 从华为镜像网站下载 https://mirrors.huaweicloud.com/python/ 我下载的是python3.7.12版本的 2、安装python 按一系列的命令安装 mkdir /usr/local/python3 cd /usr/local/python3 tar -xvf Python-3.7.12.tar

LeetCode第16~20题解

CONTENTS LeetCode 16. 最接近的三数之和&#xff08;中等&#xff09;LeetCode 17. 电话号码的字母组合&#xff08;中等&#xff09;LeetCode 18. 四数之和&#xff08;中等&#xff09; LeetCode 16. 最接近的三数之和&#xff08;中等&#xff09; 【题目描述】 给你一个…

request+python操作文件导入

业务场景&#xff1a; 通常我们需要上传文件或者导入文件如何操作呢&#xff1f; 首先通过f12或者通过抓包查到请求接口的参数&#xff0c;例如&#xff1a; 图中标注的就是我们需要的参数&#xff0c;其中 name是参数名&#xff0c;filename是文件名&#xff0c;Content-Type是…

服务器数据恢复-重组RAID导致RAID6数据丢失的数据恢复案例

服务器数据恢复环境&#xff1a; 一台存储设备中有一组由12块硬盘组建的RAID6磁盘阵列&#xff0c;上层采用EXT3文件系统&#xff0c;共划分3个LUN。 服务器故障&分析&#xff1a; 存储设备在运行过程中RAID6阵列突然不可用&#xff0c;管理员对故障存储进行了重新分配RAI…

算法设计 || 第7题:TSP问题的成本矩阵

&#xff08;一&#xff09;TSP问题学习 看不懂可以观看这个老师视频学习&#xff1a;分支限界法(TSP问题,多段图的最短路径问题,任务分配问题,批处理作业调度问题)(算法设计第十周二节)_哔哩哔哩_bilibili &#xff08;二&#xff09; 考试例题 画出计算求解最优解的分支界限过…

CentOS7关闭防火墙的操作方法

使用命令&#xff1a;systemctl status firewalld.service 查看防火墙状态 执行后能看到绿色字样标注的“active(running)”&#xff0c;说明防火墙是开启状态 使用命令&#xff1a;systemctl stop firewalld.service 关闭运行的防火墙 关闭后&#xff0c;使用命令systemctl st…

恒运资本:重磅利好预期升温!央行大动作,成长股关键变量生变

尽管上周末利好齐发&#xff0c;但应该还有利好在路上&#xff01; 从银行间商场拆借利率来看&#xff0c;最近银行的资金好像在收紧。而据财政部音讯&#xff0c;本年新增专项债券力求在9月底前根本发行结束&#xff0c;用于项目建造的专项债券资金力求在10月底前使用结束。这…