Spring注解系列 - @Autowired注解

文章目录

  • 使用总结
  • 注入原理
  • @Autowired 注入过程
    • InjectionMetadata
    • InjectedElement
    • 依赖注入查找过程
      • findAutowireCandidates
    • 缓存注入信息
  • @Resource 注解

使用总结

@Autowired注解可以自动将所需的依赖对象注入到类的属性、构造方法或方法中,从而减少手动注入依赖的代码,并提高代码的可维护性和可测试性。它是Spring容器配置的一个重要注解,与@Required、@Primary、@Qualifier等注解同属容器配置范畴。

  1. @Autowired是按照类型注入依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照Bean的名称来装配,可以结合@Qualifier注解一起使用。如果通过类型匹配找到多个candidate,在没有@Qualifier、@Primary注解的情况下,会使用对象名作为最后的fallback匹配

使用场景

  1. 属性注入(Field Injection):
    在类的属性上使用@Autowired注解,Spring会自动注入一个相应类型的实例到该属性中。
    例如:@Autowired private MyRepository myRepository;
  2. 构造方法注入(Constructor Injection):
    在类的构造方法上使用@Autowired注解,Spring会在创建类实例时,通过构造方法注入相应的依赖。例如:
@Autowired 
public MyService(MyRepository myRepository) { 
	this.myRepository = myRepository; 
}

注意:从Spring 4.3开始,如果目标bean只有一个构造器,则无需再单独放置@Autowired注解,Spring会自动完成注入。但如果存在多个构造器,则需要选择一个并标注上@Autowired注解。

  1. 方法注入(Setter Injection):
    在类的setter方法上使用@Autowired注解,Spring会调用该setter方法并注入相应的依赖。
    例如:
@Autowired public void setMyRepository(MyRepository myRepository) { 
	this.myRepository = myRepository; 
}

高级用法

  1. 数组注入:
    如果需要将多个同类型的bean注入到一个数组中,可以在数组属性或方法上使用@Autowired注解。例如:T 为具体需要注入的类型
@Autowired 
private T[] autowiredElements;
  1. 集合注入:
    同样地,也可以将多个同类型的bean注入到一个集合(如Set、List、Collection等)中。例如:T 为具体需要注入的类型
@Autowired public void setMyRepositories(Set<T> set) { 
	this.autowiredSet = set; 
}
  1. Map注入:
    如果注入的Map中Key为String类型,并且代表每个bean的name,value为指定的bean类型,则可以直接注入。例如:T 为具体需要注入的类型
@Autowired public void setMyRepositoryMap(Map<String, T> map) { 
	this.autowiredMap = map; 
}

注意事项

  1. Bean的注册:
    被注入的依赖必须是Spring容器中的一个Bean。可以通过@Component@Service@Repository等注解将类注册为Spring容器中的Bean。

  2. 自动装配模式:
    默认情况下@Autowired按类型装配。如果有多个同类型的Bean,可以使用@Qualifier注解来指定具体的Bean。

  3. required属性:
    默认情况下@Autowired的required属性为true,这意味着Spring容器在启动时必须找到一个匹配的Bean。如果没有找到匹配的Bean,会抛出异常。可以通过将required属性设置为false来避免这种情况:

@Autowired(required=false) 
private MyRepository myRepository;

注入原理

实现@Autowired注解功能的是一个后置处理器org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
		MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
}

当一个Bean创建成功后,根据其生命周期,会进行Bean属性填充(populateBean)过程,源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
在这里插入图片描述

注意:这里@Autowired不是在postProcessAfterInstantiation里实现的
在这里插入图片描述
而是在postProcessProperties这个回调里实现字段的注入的
在这里插入图片描述

@Autowired 注入过程

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// 获取要进行注入的信息
	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
	try {
		metadata.inject(bean, beanName, pvs);
	} catch (XxxException ex) {
		// 省略异常处理
	}
	return pvs;
}

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
	// 从缓存中获取
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
	if (InjectionMetadata.needsRefresh(metadata, clazz)) {
		// 刷新缓存
		synchronized (this.injectionMetadataCache) {
			// Double-Check
			metadata = this.injectionMetadataCache.get(cacheKey);
			if (InjectionMetadata.needsRefresh(metadata, clazz)) {
				if (metadata != null) {
					metadata.clear(pvs);
				}
				// 构建缓存
				metadata = buildAutowiringMetadata(clazz);
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	return metadata;
}

InjectionMetadata

org.springframework.beans.factory.annotation.InjectionMetadata包含了一个Class的注入的元数据信息,哪些字段和哪些方法需要进行注入
在这里插入图片描述
InjectionMetadata#inject进行注入实际上是调用 InjectedElement#inject 方法进行注入

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Collection<InjectedElement> checkedElements = this.checkedElements;
	Collection<InjectedElement> elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
		for (InjectedElement element : elementsToIterate) {
			element.inject(target, beanName, pvs);
		}
	}
}

InjectedElement

InjectedElement包含以下几种类型:

  1. AutowiredFieldElement:@Autowire注解标注的字段
  2. AutowiredMethodElement:@Autowire注解标注的方法
  3. LookupElement:由CommonAnnotationBeanPostProcessor中定义的内部类LookupElement表示,这个CommonAnnotationBeanPostProcessor后面会提到

在这里插入图片描述

以字段注入为例

/**
 * Object bean: @Autowired所在类的bean实例
 * String beanName:@Autowired所在类的bean名称
 * PropertyValues pvs:属性值
 **/
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Field field = (Field) this.member;
	Object value;
	if (this.cached) {
		try {
			value = resolveCachedArgument(beanName, this.cachedFieldValue);
		} catch (BeansException ex) {
			// 缓存的对象和想要注入的不兼容,因为是按名称注入
			// 可能多次注入过程中不同类型的对象因为使用了同一个名称而导致覆盖,从而不兼容
			// 兜底操作
			this.cached = false;
			value = resolveFieldValue(field, bean, beanName);
		}
	} else {
		// 获取该Field要注入的值
		value = resolveFieldValue(field, bean, beanName);
	}
	if (value != null) { // 反射进行赋值
		ReflectionUtils.makeAccessible(field);
		field.set(bean, value);
	}
}

@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
	DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
	desc.setContainingClass(bean.getClass());
	Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
	Assert.state(beanFactory != null, "No BeanFactory available");
	TypeConverter typeConverter = beanFactory.getTypeConverter();
	Object value;
	try {
		value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
	} catch (BeansException ex) {
		throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
	}
	synchronized (this) {
		if (!this.cached) {
			if (value != null || this.required) {
				Object cachedFieldValue = desc;
				registerDependentBeans(beanName, autowiredBeanNames);
				if (value != null && autowiredBeanNames.size() == 1) {
					String autowiredBeanName = autowiredBeanNames.iterator().next();
					if (beanFactory.containsBean(autowiredBeanName) &&
							beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
						cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
					}
				}
				this.cachedFieldValue = cachedFieldValue;
				this.cached = true;
			} else {
				this.cachedFieldValue = null; // cached flag remains false
			}
		}
	}
	return value;
}

关键的就是beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)了,这里会去解析依赖,获取需要的对象实例

依赖注入查找过程

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

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

	InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
	try {
		Object shortcut = descriptor.resolveShortcut(this);
		if (shortcut != null) {
			return shortcut;
		}
		// 例如 UserService 这个Class
		Class<?> type = descriptor.getDependencyType();
		// 通过AutowireCandidateResolver获取一次值
		Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
		if (value != null) {
			if (value instanceof String) {
				String strVal = resolveEmbeddedValue((String) value);
				BeanDefinition bd = (beanName != null && containsBean(beanName) ?
						getMergedBeanDefinition(beanName) : null);
				value = evaluateBeanDefinitionString(strVal, bd);
			}
			TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
			try {
				return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
			} catch (UnsupportedOperationException ex) {
				// A custom TypeConverter which does not support TypeDescriptor resolution...
				return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
			}
		}
		// 针对多个值一起注入的场景,比如@Autowired的是一个集合类型
		Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
		if (multipleBeans != null) {
			return multipleBeans;
		}
		// 根据依赖描述符查找候选的Bean对象
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
		if (matchingBeans.isEmpty()) {
			if (isRequired(descriptor)) {
				// required 为 true,但是没找到,直接抛异常
				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
			}
			return null;
		}
		String autowiredBeanName;
		Object instanceCandidate;
		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);
		} else {
			// We have exactly one match.
			Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
			autowiredBeanName = entry.getKey();
			instanceCandidate = entry.getValue();
		}

		if (autowiredBeanNames != null) {
			autowiredBeanNames.add(autowiredBeanName);
		}
		if (instanceCandidate instanceof Class) {
			instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
		}
		Object result = instanceCandidate;
		if (result instanceof NullBean) {
			if (isRequired(descriptor)) {
				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
			}
			result = null;
		}
		if (!ClassUtils.isAssignableValue(type, result)) {
			throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
		}
		return result;
	} finally {
		ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
	}
}

findAutowireCandidates

根据类型查找所有候选的可注入的对象,下面就是@Autowired按类型注入的实现过程了

protected Map<String, Object> findAutowireCandidates(
		@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
	// 根据类型查找BeanFactory 中该类型对应的所有Bean名称
	String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this, requiredType, true, descriptor.isEager());
	Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
	// 首先从 this.resolvableDependencies 这个缓存中获取
	for (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;
			}
		}
	}
	for (String candidate : candidateNames) {
		// isSelfReference: 判断是否@Autowired的是beanName
		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;
}

BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class<?>, boolean, boolean)这个方法的作用是根据依赖的类型从BeanFactory中获取该类型所有的Bean名称,关键是String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);这句

public static String[] beanNamesForTypeIncludingAncestors(
		ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {

	Assert.notNull(lbf, "ListableBeanFactory must not be null");
	String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
	if (lbf instanceof HierarchicalBeanFactory) {
		HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
		if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
			String[] parentResult = beanNamesForTypeIncludingAncestors(
					(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
			result = mergeNamesWithParent(result, parentResult, hbf);
		}
	}
	return result;
}

缓存注入信息

当一个Bean创建时,populateBean这一步需要获取该类上有哪些元素是需要@Autowired的。AutowiredAnnotationBeanPostProcessor 同时实现了SmartInstantiationAwareBeanPostProcessor和MergedBeanDefinitionPostProcessor,解析注入信息这个过程就是在MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition实现的

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
	metadata.checkConfigMembers(beanDefinition);
}

@Resource 注解

@Resource默认按照名称自动注入,由J2EE提供,需要导入包javax.annotation.Resource(spring 6以后需要改成jakarta.annotation.Resource)。

@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用按名称注入的自动注入策略,而使用type属性时则使用按类型自动注入的策略。如果既不指定name也不指定type属性,这时将通过反射机制使用按名称自动注入策略进行注入。

@Resource装配顺序:

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
  2. 如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常。
  3. 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
  4. 如果既没有指定name,又没有指定type,则自动按照按名称匹配方式进行装配;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu 设置静态IP

一、 ip addresssudo nano /etc/netplan/50-cloud-init.yaml 修改前&#xff1a; 修改后&#xff1a; # This file is generated from information provided by the datasource. Changes # to it will not persist across an instance reboot. To disable cloud-inits # ne…

【重学 MySQL】五十、添加数据

【重学 MySQL】五十、添加数据 使用INSERT INTO语句添加数据基本语法示例插入多行数据注意事项 使用LOAD DATA INFILE语句批量添加数据其他插入数据的方式注意事项 在MySQL中&#xff0c;添加数据是数据库操作中的基本操作之一。 使用INSERT INTO语句添加数据 使用 INSERT IN…

单链表的增删改查(数据结构)

之前我们学习了动态顺序表&#xff0c;今天我们来讲一讲单链表是如何进行增删改查的 一、单链表 1.1、单链表概念 概念&#xff1a;链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 1.2、链表与顺序表的…

python的几个基本数据类型及其相关操作(字符串str,元组tuple,列表list,字典dict)

一、str及其相关操作 1、字符串的基本方法 字符串的索引、获取字符串长度、利用index获取索引位置&#xff0c;统计某字符在字符串中出现的次数。用法如下方代码。 python的变量在创建时不需要声明其数据类型&#xff0c;他会自动识别变量后的数据类型&#xff0c;所以创建一…

(undone) 阅读 MapReduce 论文笔记

参考&#xff1a;https://pdos.csail.mit.edu/6.824/papers/mapreduce.pdf 摘要&#xff1a;简单介绍了 MapReduce 是在大型分布式系统上工作的 Introduction 的内容总结&#xff1a; 1.介绍背景&#xff1a;为什么我们需要分布式系统&#xff1f;MapReduce 的意义是哪些 2.简…

运动耳机哪个牌子的好?5大质量不凡的运动耳机测评力荐!

在快节奏的生活中&#xff0c;无论是晨跑、健身还是户外探险&#xff0c;音乐都成了许多人不可或缺的陪伴。运动耳机&#xff0c;作为一种专为运动场景设计的音频设备&#xff0c;旨在提供高质量音频体验的同时&#xff0c;保证佩戴的舒适度和运动的安全性。 &#xff08;上图为…

YOLOv11改进 | 主干篇 | YOLOv11引入MobileNetV4

1. MobileNetV4介绍 1.1 摘要&#xff1a; 我们推出了最新一代的 MobileNet&#xff0c;称为 MobileNetV4 (MNv4)&#xff0c;具有适用于移动设备的通用高效架构设计。 在其核心&#xff0c;我们引入了通用倒瓶颈&#xff08;UIB&#xff09;搜索块&#xff0c;这是一种统一且…

小川科技携手阿里云数据库MongoDB:数据赋能企业构建年轻娱乐生态

随着信息技术的飞速发展&#xff0c;企业在处理海量数据时所面临的挑战日益严峻。特别是在年轻娱乐领域&#xff0c;用户行为的多样性和数据量的激增对数据存储与分析技术提出了更高的要求。在此背景下&#xff0c;小川凭借其前瞻性的技术视野&#xff0c;选择了MongoDB作为其数…

AlmaLinux 9 安装mysql8.0.38

文件下载 https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-linux-glibc2.12-x86_64.tar 选择合适系统版本 下载后解压 tar -xvf mysql-8.0.39-linux-glibc2.12-x86_64.tar解压后里面有三个文件夹 使用mysql-8.0.39-linux-glibc2.12-x86_64.tar.xz即可&#xff0c…

Redis中String类型的常用命令(append,getrenge,setrange等命令)

Redis----String命令 前言.常见的String存储类型. 常见命令1. set 命令2. get 命令3. mget命令与mset命令4. setnx命令5. setex与psetex命令6. incr与incrby与incrbyfloat命令7. decr与decrby命令8. append命令9. getrange和setrange命令10. strlen命令. 前言. 常见的String存…

【Kubernetes】常见面试题汇总(四十五)

目录 102.使用 Kubernetes 时可以采取的最佳安全措施是什么&#xff1f; 103.什么是联合集群&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目…

高中教辅汇总【35GB】

文章目录 一、资源概览二、资源亮点三、获取方式 一、资源概览 这份教辅资源汇总&#xff0c;精心搜集了高中各学科的海量教辅资料&#xff0c;总容量高达35GB&#xff0c;覆盖了语文、数学、英语、物理、化学、生物、历史、地理、政治等所有必修及选修科目。从基础知识点到难…

插槽slot在vue中的使用

介绍 在 Vue.js 中&#xff0c;插槽&#xff08;slot&#xff09;是一种用于实现组件内容分发的功能。通过插槽&#xff0c;可以让父组件在使用子组件时自定义子组件内部的内容。插槽提供了一种灵活的方式来组合和复用组件。 项目中有很多地方需要调用一个组件&#xff0c;比…

【H2O2|全栈】关于CSS(9)CSS3扩充了哪些新鲜的东西?(二)

目录 CSS3入门 前言 准备工作 伪元素补充 :before :after 文本溢出属性 转换效果 预告和回顾 后话 CSS3入门 前言 本系列博客主要介绍CSS相关的知识点。 这一期主要介绍以下几个CSS3的知识点&#xff1a; 伪元素补充文本溢出属性转换 没有基础的朋友&#xff…

【Docker】配置文件

问题 学习Docker期间会涉及到docker的很多配置文件&#xff0c;可能会涉及到的会有&#xff1a; /usr/lib/systemd/system/docker.service 【docker用于被systemd管理的配置文件】 /etc/systemd/system/docker.service.d【覆盖配置文件的存放处】 /etc/systemd/system/mul…

网页前端开发之Javascript入门篇(4/9):循环控制

Javascript循环控制 什么是循环控制&#xff1f; 答&#xff1a;其概念跟 Python教程 介绍的一样&#xff0c;只是语法上有所变化。 参考流程图如下&#xff1a; 其对应语法&#xff1a; var i 0; // 设置起始值 var minutes 15; // 设置结束值&#xff08;15分钟…

VMware Aria Operations for Networks 6.13 发布,新增功能概览

VMware Aria Operations for Networks 6.13 - 网络和应用监控工具 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-aria-operations-for-networks/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org VMware Aria Oper…

Golang | Leetcode Golang题解之第440题字典序的第K小数字

题目&#xff1a; 题解&#xff1a; func getSteps(cur, n int) (steps int) {first, last : cur, curfor first < n {steps min(last, n) - first 1first * 10last last*10 9}return }func findKthNumber(n, k int) int {cur : 1k--for k > 0 {steps : getSteps(cu…

Llama 系列简介与 Llama3 预训练模型推理

1. Llama 系列简介 1.1 Llama1 由 Meta AI 发布&#xff0c;包含 7B、13B、33B 和 65B 四种参数规模的开源基座语言模型 数据集&#xff1a;模型训练数据集使用的都是开源的数据集&#xff0c;总共 1.4T token 模型结构&#xff1a;原始的 Transformer 由编码器&#xff08…

C++入门基础 (超详解)

文章目录 前言1. C关键字2. C的第一个程序3. 命名空间3.1 namespace的定义3.2 命名空间的嵌套3.3 命名空间使用3.4 查找优先级总结 4. C输入和输出4.1 标准输入输出 (iostream库)4.2 文件输入输出 (fstream库)4.3 字符串流 (sstream库)4.4 C格式化输出4.5 std::endl和\n的区别 …