Spring源码分析 @Autowired 是怎样完成注入的?究竟是byType还是byName亦两者皆有

1. 五种不同场景下 @Autowired 的使用

第一种情况 上下文中只有一个同类型的bean

配置类

package org.example.bean;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FruitConfiguration {@Bean("apple")public Fruit apple(){return new Fruit("apple");}}

启动类

@ComponentScan(basePackages = "org.example.bean")
public class AutowiredTestDemo {@Autowiredprivate Fruit fruit;public static void main(String[] args) {AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AutowiredTestDemo.class);AutowiredTestDemo autowiredTestDemo = app.getBean(AutowiredTestDemo.class);System.out.println(autowiredTestDemo.fruit);}}

输出

Fruit{name='apple', price=null}

第二种情况 上下文中有两个同类型不同名的bean 且都与注入字段名称不一致

配置类

@Configuration
public class FruitConfiguration {@Bean("apple")public Fruit apple(){return new Fruit("apple");}@Bean("banana")public Fruit banana(){return new Fruit("banana");}}

启动类

@ComponentScan(basePackages = "org.example.bean")
public class AutowiredTestDemo {@Autowiredprivate Fruit fruit;public static void main(String[] args) {AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AutowiredTestDemo.class);AutowiredTestDemo autowiredTestDemo = app.getBean(AutowiredTestDemo.class);System.out.println(autowiredTestDemo.fruit);}}

异常结果

No qualifying bean of type 'org.example.bean.Fruit' available: expected single matching bean but found 2: apple,banana

第三种情况 上下文中有两个同类型不同名的bean 且其中一个与注入字段名称一致

配置类

@Configuration
public class FruitConfiguration {@Bean("apple")public Fruit apple(){return new Fruit("apple");}@Bean("banana")public Fruit banana(){return new Fruit("banana");}}

启动类

@ComponentScan(basePackages = "org.example.bean")
public class AutowiredTestDemo {@Autowiredprivate Fruit apple;public static void main(String[] args) {AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AutowiredTestDemo.class);AutowiredTestDemo autowiredTestDemo = app.getBean(AutowiredTestDemo.class);System.out.println(autowiredTestDemo.apple);}}

输出结果

Fruit{name='apple', price=null}

第四种情况 上下文中有两个同类型不同名的bean 且都与注入字段名称不一致但其中一个使用@Primary 注解

配置类

package org.example.bean;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;@Configuration
public class FruitConfiguration {@Bean("apple")public Fruit apple(){return new Fruit("apple");}@Primary@Bean("banana")public Fruit banana(){return new Fruit("banana");}}

启动类

package org.example;import org.example.bean.Fruit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;@ComponentScan(basePackages = "org.example.bean")
public class AutowiredTestDemo {@Autowiredprivate Fruit fruit;public static void main(String[] args) {AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AutowiredTestDemo.class);AutowiredTestDemo autowiredTestDemo = app.getBean(AutowiredTestDemo.class);System.out.println(autowiredTestDemo.fruit);}
}

输出结果

Fruit{name='banana', price=null}

第五种情况 上下文中有两个同类型不同名的bean 且都与注入字段名称不一致但注入时使用@Qualifier注解

配置类

package org.example.bean;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FruitConfiguration {@Bean("apple")public Fruit apple(){return new Fruit("apple");}@Bean("banana")public Fruit banana(){return new Fruit("banana");}}

启动类

package org.example;import org.example.bean.Fruit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;@ComponentScan(basePackages = "org.example.bean")
public class AutowiredTestDemo {@Autowired@Qualifier("apple")private Fruit fruit;public static void main(String[] args) {AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AutowiredTestDemo.class);AutowiredTestDemo autowiredTestDemo = app.getBean(AutowiredTestDemo.class);System.out.println(autowiredTestDemo.fruit);}}

输出结果

Fruit{name='apple', price=null}

2.源码分析

启动类

public static void main(String[] args) {//创建应用上下文的同时注册AutowiredTestDemoAnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AutowiredTestDemo.class);AutowiredTestDemo autowiredTestDemo = app.getBean(AutowiredTestDemo.class);System.out.println(autowiredTestDemo.fruit);}

org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(java.lang.Class<?>…)

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {//调用无参构造方法this();//注册组件//调用 BeanDefinitionReaderUtils.registerBeanDefinition 注册bdregister(componentClasses);//刷新容器refresh();}

无参构造创建了一个AnnotatedBeanDefinitionReader对象与ClassPathBeanDefinitionScanner对象,用于读取和扫描带有注解的Bean定义信息

public AnnotationConfigApplicationContext() {this.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);}

AnnotatedBeanDefinitionReader在初始化时将会往BeanFactory注册注解相关的处理器对象

org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.core.env.Environment)

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");Assert.notNull(environment, "Environment must not be null");this.registry = registry;this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));}// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition();try {def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,AnnotationConfigUtils.class.getClassLoader()));}catch (ClassNotFoundException ex) {throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);}def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));}if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));}

截屏2023-10-31 21.51.35

@Autowired注解是由 AutowiredAnnotationBeanPostProcessor 进行处理,而后者又实现了 InstantiationAwareBeanPostProcessorAdapter 与 MergedBeanDefinitionPostProcessor 两个扩展点接口

重写 MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition,在bean实例化前,合并定义信息后执行。将需要注入的字段和方法与之需要注入的bean建立映射关系并封装成InjectedElement集合,再与class对象建立映射关系封装为InjectionMetadata对象并存入缓冲中

@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {//遍历类中的字段与方法,如果需要依赖注入,将封装成InjectionMetadata并放入缓冲中InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);//将成员添加到 beanDefinition 中,以便在配置过程中由外部管理器处理该成员的生命周期和依赖注入metadata.checkConfigMembers(beanDefinition);}

重写 InstantiationAwareBeanPostProcessorAdapter.postProcessProperties,此方法将在bean实例化后,属性填充前执行,此时当前bean已经完成了实例化,因此可以通过之前缓冲起来的映射关系,一一找出需要注入的字段和方法以及其对应需要注入的值,通过反射进行赋值操作

	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {//从缓存中获取当前bean字节码对象的注解元信息InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {//进行属性注入metadata.inject(bean, beanName, pvs);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);}return pvs;}

大概了解了@Autowired的核心处理类AutowiredAnnotationBeanPostProcessor后,我们进一步分析上述五种情况,再源码底层是怎么处理的

情况一 有且仅有一个候选bean

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

Field field = (Field) this.member;Object value;//默认falseif (this.cached) {try {value = resolvedCachedArgument(beanName, this.cachedFieldValue);}catch (NoSuchBeanDefinitionException ex) {// Unexpected removal of target bean for cached argument -> re-resolvevalue = resolveFieldValue(field, bean, beanName);}}else {//处理当前bean所属字段的依赖注入,获取需要注入的对象value = resolveFieldValue(field, bean, beanName);}if (value != null) {//暴力访问ReflectionUtils.makeAccessible(field);//通过反射给当前需要注入的字段设置值field.set(bean, value);}

跟进resolveFieldValue(field, bean, beanName)方法一路往下直到doResolveDependency方法

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

resolveMultipleBeans 方法是处理集合类型的依赖注入,而我们当前是非集合类型,因此会调用

findAutowireCandidates 处理依赖注入

			//集合类型注入Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);if (multipleBeans != null) {return multipleBeans;}//非集合类型注入Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);if (matchingBeans.isEmpty()) {if (isRequired(descriptor)) {raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;}
protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {//根据需要注入的bean的类型 递归调用 beanNamesForTypeIncludingAncestors 获取父子BeanFactory中 类型的bean的名称// 注入 此处可能返回多个 候选的beanNameString[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);// 判断需要的类型是否是内建beanfor (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {Class<?> autowiringType = classObjectEntry.getKey();if (autowiringType.isAssignableFrom(requiredType)) {Object autowiringValue = classObjectEntry.getValue();autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);if (requiredType.isInstance(autowiringValue)) {result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);break;}}}// ,isSelfReference(beanName, candidate) 用于判断候选项是否是自身引用。// isAutowireCandidate(candidate, descriptor) 用于判断候选项是否符合自动装配的条件for (String candidate : candidateNames) {//调用DefaultListableBeanFactory.isAutowireCandidate() 完成@Qualifier注解过滤// 如果不满足将不会添加到候选Map中if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}if (result.isEmpty()) {boolean multiple = indicatesMultipleBeans(requiredType);// Consider fallback matches if the first pass failed to find anything...DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();for (String candidate : candidateNames) {if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {addCandidateEntry(result, candidate, descriptor, requiredType);}}if (result.isEmpty() && !multiple) {// Consider self references as a final pass...// but in the case of a dependency collection, not the very same bean itself.for (String candidate : candidateNames) {if (isSelfReference(beanName, candidate) &&(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&isAutowireCandidate(candidate, fallbackDescriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}}}return result;}

此时容器中只有一个Fruit类型的bean,回到doResolveDependency方法中,最终调用descriptor.resolveCandidate获取到bean实例,然后通过反射完成依赖注入

				//匹配到一个beanName 不满足if (matchingBeans.isEmpty()) {if (isRequired(descriptor)) {raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;}String autowiredBeanName;Object instanceCandidate;//只匹配到一个beanName 不满足if (matchingBeans.size() > 1) {autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);if (autowiredBeanName == null) {if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);}else {// In case of an optional Collection/Map, silently ignore a non-unique case:// possibly it was meant to be an empty collection of multiple regular beans// (before 4.3 in particular when we didn't even look for collection beans).return null;}}instanceCandidate = matchingBeans.get(autowiredBeanName);}//调用 beanFactory.getBean(beanName)  根据名称获取需要注入的bean对象instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);

因此,情况一 容器中只有一个目标类型的bean进行依赖注入就此结束

情况二,三,四 有两个同类型bean的两种情况

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

如果根据类型匹配到两个beanName,将进入此判断语句中

			if (matchingBeans.size() > 1) {autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);if (autowiredBeanName == null) {//是否必须注入if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);}else {// In case of an optional Collection/Map, silently ignore a non-unique case:// possibly it was meant to be an empty collection of multiple regular beans// (before 4.3 in particular when we didn't even look for collection beans).return null;}}instanceCandidate = matchingBeans.get(autowiredBeanName);}

由determineAutowireCandidate方法决定最终注入的beanName,如果无法选择出最合适的,将执行resolveNotUnique方法抛出NoUniqueBeanDefinitionException 异常,也就是我们情况二看到的异常

进入determineAutowireCandidate 方法

Class<?> requiredType = descriptor.getDependencyType();//@Primary 注解处理  是否有标记了@Primary注解String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);if (primaryCandidate != null) {return primaryCandidate;}//javax.annotation.Priority 优先级匹配 值越小 优先级越高String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);if (priorityCandidate != null) {return priorityCandidate;}// Fallbackfor (Map.Entry<String, Object> entry : candidates.entrySet()) {String candidateName = entry.getKey();Object beanInstance = entry.getValue();if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||//将候选的beanName与依赖注入的字段名称进行匹配matchesBeanName(candidateName, descriptor.getDependencyName())) {return candidateName;}}return null;

首先针对第四种情况,如果有标记了@Primary注解的bean,将优先采用,如果都没有 ,则根据javax.annotation.Priority 注解的值选最小,值越小优先级越高。如果没有使用@Priority注解,将采用兜底方法

matchesBeanName(),哪一个候选的beanName与需要依赖注入的字段名称一致就使用谁

protected boolean matchesBeanName(String beanName, @Nullable String candidateName) {return (candidateName != null &&(candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName)));}

此时就是情况三 候选的beanName的名称与需要依赖注入的字段名称一致,依然能完成注入。

情况五 注入时使用@Qualifier注解

回到

org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates

在获取到多个候选的beanName后,将会遍历集合,调用isSelfReference,isAutowireCandidate进行筛选,

而Qualifier注解的处理就在isAutowireCandidate方法中

for (String candidate : candidateNames) {//调用DefaultListableBeanFactory.isAutowireCandidate() 完成@Qualifier注解过滤// 如果不满足将不会添加到候选Map中if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}

跟进来到

org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate

public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {boolean match = super.isAutowireCandidate(bdHolder, descriptor);if (match) {//Qualifier 注解匹配 获取对象标准的Qualifier注解的值与候选的beanName进行匹配//有Qualifier注解才进行比较判断//否则直接返回truematch = checkQualifiers(bdHolder, descriptor.getAnnotations());if (match) {MethodParameter methodParam = descriptor.getMethodParameter();if (methodParam != null) {Method method = methodParam.getMethod();if (method == null || void.class == method.getReturnType()) {match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());}}}}return match;}
{if (ObjectUtils.isEmpty(annotationsToSearch)) {return true;}SimpleTypeConverter typeConverter = new SimpleTypeConverter();for (Annotation annotation : annotationsToSearch) {Class<? extends Annotation> type = annotation.annotationType();boolean checkMeta = true;boolean fallbackToMeta = false;//有Qualifier注解才进行比较判断if (isQualifier(type)) {if (!checkQualifier(bdHolder, annotation, typeConverter)) {fallbackToMeta = true;}else {checkMeta = false;}}if (checkMeta) {boolean foundMeta = false;for (Annotation metaAnn : type.getAnnotations()) {Class<? extends Annotation> metaType = metaAnn.annotationType();if (isQualifier(metaType)) {foundMeta = true;// Only accept fallback match if @Qualifier annotation has a value...// Otherwise it is just a marker for a custom qualifier annotation.if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||!checkQualifier(bdHolder, metaAnn, typeConverter)) {return false;}}}if (fallbackToMeta && !foundMeta) {return false;}}}return true;}

至此,Spring针对 @Autowired 注解在五种不同的情况下进行依赖注入我们已经分析完毕

3. 结论

@Autowired 首先会通过 BeanFactoryUtils.beanNamesForTypeIncludingAncestors (递归获取父子BeanFactory调用getBeanNamesForType方法),根据需要进行依赖注入的字段的类型或者方法参数的类型取获取相应的beanName,如果注入的是非集合类型的对象,并且找到了一个以上的候选beanName,则下一步会去判断是否有标记优先级,如果都没有则会进行名称匹配,也就是将候选的beanName与字段名或参数名进行equals比较

byType -> 优先级比较 -> byName

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

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

相关文章

推箱子小游戏

--print("开发流程步骤&#xff1a;I、绘制推箱子地图并初始化 ----- 几*几大小的地图 \n\n II、根据宏定义和推箱子地图上的数字来选择不同的图形\n\n III、获取玩家坐标 -----------重点\n\n …

html旋转相册

一、实验题目 做一个旋转的3d相册 二、实验代码 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&qu…

AtomHub 开源容器镜像中心开放公测,国内服务稳定下载

由开放原子开源基金会主导&#xff0c;华为、浪潮、DaoCloud、谐云、青云、飓风引擎以及 OpenSDV 开源联盟、openEuler 社区、OpenCloudOS 社区等成员单位共同发起建设的 AtomHub 可信镜像中心正式开放公测。AtomHub 秉承共建、共治、共享的理念&#xff0c;旨在为开源组织和开…

医保购药小程序:智能合约引领医疗数字革新

在医疗领域&#xff0c;医保购药小程序通过引入智能合约技术&#xff0c;为用户提供更为高效、安全的购药体验。本文将通过简单的智能合约代码示例&#xff0c;深入探讨医保购药小程序如何利用区块链技术中的智能合约&#xff0c;实现医保结算、购药监控等功能&#xff0c;为医…

AI大模型:未来科技的新篇章

目录 1AI大模型&#xff1a;未来科技的新篇章 2AI超越数学家攻克经典数学难题&#xff1b;非侵入式设备解码大脑思维 1AI大模型&#xff1a;未来科技的新篇章 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经成为了我们生活中不可或缺的一部分。而AI大…

Windows系统找不到xinput1_3.dll怎么办?

引言&#xff1a; 在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是xinput1_3.dll丢失。那么&#xff0c;xinput1_3.dll究竟是什么&#xff1f;为什么会出现丢失的情况&#xff1f;丢失后会对计算机产生什么影响&#xff1f;本文将详细介绍…

2023.12.21 关于 Redis 常用数据结构 和 单线程模型

目录 各数据结构具体编码方式 查看 key 对应 value 的编码方式 Reids 单线程模型 经典面试题 IO 多路复用 Redis 常用数据结构 Redis 中所有的 key 均为 String 类型&#xff0c;而不同的是 value 的数据类型却有很多种以下介绍 5 种 value 常见的数据类型 注意&#xff1…

计算机网络概述(下)——“计算机网络”

各位CSDN的uu们你们好呀&#xff0c;今天继续计算机网络概述的学习&#xff0c;下面&#xff0c;让我们一起进入计算机网络概述的世界吧&#xff01;&#xff01;&#xff01; 计算机网络体系结构 数据传输流程 计算机网络性能指标 计算机网络体系结构 两个计算机系统必须高度…

7.4组合总和(LC39-M)

算法: 组合问题&#xff0c;用回溯。 画树 回溯三部曲&#xff1a; 1.确定函数返回值和参数&#xff1a; 返回值&#xff1a;void 参数&#xff1a; candidates, target&#xff08;题目中给出的&#xff09; sum&#xff1a;统计每个组合的和&#xff0c;是否target …

鞋服用户运营策略如何实现有效闭环?

实现长期价值和业务闭环是企业经营的关键。对于鞋服行业来说&#xff0c;如何基于客户旅程编排&#xff08;Customer Journey Orchestration&#xff0c;简称 CJO&#xff09;实现用户运营策略的有效闭环&#xff0c;提升长期价值呢&#xff1f; 本文围绕该主题&#xff0c;从鞋…

C语言——小细节和小知识6

一、转义字符相关 \ 反斜杠&#xff0c;转义字符中的转义序列符 \? 将?转义&#xff0c;防止他被识别成三字母词(很早的东西)中的问号 //三字母词 //??(是[ //??)是] printf("%s","??(??)"); //打印结果是[] 二、fopen函数fc…

Vue2+Vue3组件间通信方式汇总(2)------$emit

组件间通信方式是前端必不可少的知识点&#xff0c;前端开发经常会遇到组件间通信的情况&#xff0c;而且也是前端开发面试常问的知识点之一。接下来开始组件间通信方式第二弹------$emit,并讲讲分别在Vue2、Vue3中的表现。 Vue2Vue3组件间通信方式汇总&#xff08;1&#xff0…

【C++】STL 容器 - stack 堆栈容器 ① ( stack 堆栈容器特点 | stack 堆栈容器与 deque 双端数组容器对比 | 简单示例 )

文章目录 一、 stack 堆栈容器简介1、stack 堆栈容器引入2、stack 堆栈容器特点3、stack 堆栈容器与 deque 双端数组容器对比 二、 代码示例 - stack 堆栈容器简单示例1、代码示例2、执行结果 一、 stack 堆栈容器简介 1、stack 堆栈容器引入 C 语言中的 STL 标准模板库 中的 s…

ABS210-ASEMI手机适配器整流桥ABS210

编辑&#xff1a;ll ABS210-ASEMI手机适配器整流桥ABS210 型号&#xff1a;ABS210 品牌&#xff1a;ASEMI 封装&#xff1a;ABS-4 特性&#xff1a;贴片、整流桥 最大平均正向电流&#xff1a;2A 最大重复峰值反向电压&#xff1a;1000V 恢复时间&#xff1a;&#xff…

步兵 cocos2dx 加密和混淆

文章目录 摘要引言正文代码加密具体步骤代码加密具体步骤测试和配置阶段IPA 重签名操作步骤 总结参考资料 摘要 本篇博客介绍了针对 iOS 应用中的 Lua 代码进行加密和混淆的相关技术。通过对 Lua 代码进行加密处理&#xff0c;可以确保应用代码的安全性&#xff0c;同时提高性…

Xcode15 iOS 17 Simulator 离线安装,模拟器安装

Xcode 15 安装包的大小相比之前更小&#xff0c;因为除了 macOS 的 Components&#xff0c;其他都需要动态下载安装&#xff0c;否则提示 iOS 17 Simulator Not Installed。 如果不安装对应的运行模拟库 无法真机和模拟器运行&#xff0c;更无法新建项目。但是由于模拟器安装包…

Jenkins 插件管理指南

目录 常用插件 插件安装 已安装插件 installed plugins 常用插件 Docker Plugin&#xff1a; 这个插件让Jenkins能够与Docker容器平台进行集成。它允许在Jenkins构建过程中创建、管理和销毁Docker容器&#xff0c;为需要Docker化的项目提供了极大的便利性。对于需要在容器中…

反序列化漏洞原理、成因、危害、攻击、防护、修复方法

反序列化漏洞是一种安全漏洞&#xff0c;它允许攻击者将恶意代码注入到应用程序中。这种漏洞通常发生在应用程序从不安全的来源反序列化数据时。当应用程序反序列化数据时&#xff0c;它将数据从一种格式&#xff08;例如JSON或XML&#xff09;转换为另一种格式&#xff08;例如…

Redis原理

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

广州华锐互动:VR元宇宙技术为汽车行业带来革命性变化

随着科技的飞速发展&#xff0c;VR元宇宙技术已经深入影响到我们生活的方方面面&#xff0c;汽车行业更是深受其益。这一新兴技术的出现&#xff0c;为汽车行业带来了前所未有的变化。广州华锐互动将VR技术应用于汽车行业&#xff0c;研发了VR汽修培训、3D汽车展厅、特种车辆3D…