@Autowired与@Resource原理知识点详解

news2024/11/26 2:22:13

文章目录

  • 前言
  • springIOC
  • 依赖注入的三种方式
    • 属性注入(字段注入)
    • 构造方法注入
    • setter注入
    • 用哪个?
  • @Autowired
    • 实现原理
  • @Resource
    • 实现原理
    • 结论
  • @Autowired与@Resource的不同
    • 来源不同
    • 参数不同
    • 使用不同
    • 装配顺序

前言

现在spring可以说是一统天下了,而spring有两个核心部分:IOC和AOP,AOP之前可以看一下之前的文章:Spring AOP原理使用详解

springIOC

AOP的不多做赘述了,说下IOC:Spring IOC 解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系,而IOC有两个概念:控制反转及依赖注入。控制反转指的就是:把原有自己掌控的事交给别人去处理,更多的是一种思想或者可以理解为设计模式。
而依赖注入其实是控制反转的实现方式,通过依赖注入对象无需自行创建或者管理它的依赖关系,依赖关系将被自动注入到需要它们的对象当中去。

依赖注入的三种方式

其实还有一种基于XML的注入方式,但是现在这种方式过于繁琐且依赖冗余复杂的配置文件,再次不做讨论,仅说下常用的三种注入方式:

属性注入(字段注入)

该方式底层其实是基于Field注入的方式,也可以成为字段注入,因为注入方式实现比较简介,没有多于代码,所以这个在实际开发过程中比较常用:
在这里插入图片描述
该方式可以使用@Autowired、@Resource及@Inject来实现。后面会着重讲解这一部分。

构造方法注入

这种方式其实是spring官方比较推荐的方式,因为构造方法注入的时候会比较靠前,是在bean的实例化阶段就进行的:
在这里插入图片描述
但是有一点需要注意一下:如果只有一个构造方法的话可以不用加上@Autowired,但是如果有无参构造方法或者其他多个构造方法的话,就需要在指定的构造方法上面添加@Autowired注解了。

setter注入

我都不想列出来,但是又怕别人说我不知道这个(哈哈哈哈哈哈)。。。因为这种方式我觉得用的会越来越少的,因为这种代码太臃肿了,且好丑。。。
在这里插入图片描述

用哪个?

如果是开发的话,我建议直接属性(字段)注入完事儿,一般的业务场景基本不会有纰漏。但是如果想在bean实例化阶段进行一些处理操作或者是不在同一个IOC容器里面的话,我建议构造函数注入。
spring4以后推荐的是通过构造函数注入:

  • 依赖不可变:这个好理解,通过构造方法注入依赖,在对象创建的时候就要注入依赖,一旦对象创建成功,以后就只能使用注入的依赖而无法修改了,这就是依赖不可变(通过 set 方法注入将来还能通过 set 方法修改)。
  • 依赖不为空:通过构造方法注入的时候,会自动检查注入的对象是否为空,如果为空,则注入失败;如果不为空,才会注入成功。
  • 完全初始化:由于获取到了依赖对象(这个依赖对象是初始化之后的),并且调用了要初始化组件的构造方法,因此最终拿到的就是完全初始化的对象了。

idea里面为什么不推荐使用属性(字段)注入:

  • 对于 IOC 容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。因为该类没有提供该属性的 set 方法或者相应的构造方法来完成该属性的初始化。换言之,要是使用属性注入,那么你这个类就只能在 IOC 容器中使用,要是想自己 new 一下这个类的对象,那么相关的依赖无法完成注入。
  • 属性注入时机较晚。在一些是实列化阶段的一些操作,无法使用注入对象。

@Autowired

在这里插入图片描述
@Autowired注解,可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,@Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上,也可以放在任意方法上表示,自动执行当前方法,如果方法有参数,会在IOC容器中自动寻找同类型参数为其传值。在上面已经讲解过了。
这里必须明确:@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier使用。

实现原理

源码跟踪下bean的实例化及初始化,以下源码定位流程就先不截图了,等到关键重要地方再截图:

  1. SpringApplication#run()方法的this.refreshContext

  2. AbstractApplicationContext#refresh方法的this.finishBeanFactoryInitialization(beanFactory);该方法会初始化所有非延迟加载的单例bean,包括我们在应用中声明的各种业务Bean,Bean的实例化和初始化过程逻辑都在这个函数中。

  3. 上述finishBeanFactoryInitialization方法中有this.getBean(weaverAwareName)方法。

  4. AbstractBeanFactory#doGetBean()方法中有createBean()方法。

  5. 进入AbstractAutowireCapableBeanFactory#createBean()方法,该方法内有doCreateBean()方法:
    在这里插入图片描述
    doCreateBean()方法是用来构建bean的,bean的实例化及初始化逻辑都是在doCreateBean()方法里面。该方法代码截图一下吧,重点关注两个方法:
    在这里插入图片描述

  6. 先看下applyMergedBeanDefinitionPostProcessors方法:
    在这里插入图片描述
    可以看到该方法里面调用了所有的MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法,其中有一个我们一直在找的实现类:AutowiredAnnotationBeanPostProcessor,这个就是用来处理依赖注入的后置处理器。MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法如下:
    在这里插入图片描述
    AutowiredAnnotationBeanPostProcessor会对bean进行注解扫描,如果扫描到了@Autowired和@Value注解,就会把对应的方法或者属性封装起来,最终封装成InjectionMetadata对象。

  7. 我们再看第二个重要方法populateBean(),这是个非常重要的方法:填充Bean实例属性完成依赖对象的注入,此时Bean中需要依赖注入的成员已经在第六步的applyMergedBeanDefinitionPostProcessors中被对应的后置处理器(AutowiredAnnotationBeanPostProcessor)进行了存储。
    这个方法内部进行部分截图:
    在这里插入图片描述
    这段代码肯定不会return,因为postProcessAfterInstantiation是为true的?重点定位到postProcessPropertyValues方法:
    在这里插入图片描述

  8. 可以看到有两个实现类:一个就是AutowiredAnnotationBeanPostProcessor而另一个是CommonAnnotationBeanPostProcessor。@Resource是通过CommonAnnotationBeanPostProcessor的postProcessPropertyValues实现的,我们先以@Autowired为例说明下,进入到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法内:
    在这里插入图片描述
    在这里插入图片描述
    查看findAutowiringMetadata()方法:
    在这里插入图片描述
    查看下buildAutowiringMetadata()部分方法:
    在这里插入图片描述
    当调用findAutowiringMetadata()方法时,会根据autowiredAnnotationTypes这个全局变量中的元素类型来进行注解的解析,而这个全局变量在AutowiredAnnotationBeanPostProcessor的构造方法中进行了初始化:
    在这里插入图片描述
    构造方法初始化的时候会向autowiredAnnotationTypes全局集合变量里面加入@Autowired、@Inject、@Value
    以上说明了findAutowiringMetadata()方法只解析出bean中带有@Autowired注解、@Inject和@Value注解的属性和方法。然后调用metadata.inject()方法,进行属性填充,继续跟踪:
    在这里插入图片描述
    以上代码中:checkedElements用于检查元素防止重复注入,bean中@Autowired 注入点会被提前解析成元信息并保存到InjectionMetadata中(对于@Resource,@LookUp等注解被解析后,也会解析成对应的InjectedElement的子类:ResourceElement、LookUpElement,但是这两个注解是在CommonAnnotationBeanPostProcessor后置处理器进行处理的),遍历InjectedElement集合继续执行注入逻辑element.inject():
    在这里插入图片描述

  9. 该方法对应两个实现类分别为:AutowiredFieldElement及AutowiredMethodElement,字面意思对应分别为字段注入及方法注入实现,两者实现逻辑基本相同,我们以AutowiredFieldElement为例:
    在这里插入图片描述
    ReflectionUtils.makeAccessible(field);利用Java的反射,为属性进行赋值,我们重点看下resolveFieldValue():
    在这里插入图片描述

  10. 重点看下resolveDependency()方法,该方法主要调用了doResolveDependency()方法,接下来重点分析一下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;
		}

		Class<?> type = descriptor.getDependencyType();
		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()));
			}
		}

		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;
		}

		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);
	}
}

其中有个resolveMultipleBeans()方法,该方法用于处理注入时属性为数组集合等情况,比如我们在bean中使用注入如下:
在这里插入图片描述
可以看下该方法的关键代码:
在这里插入图片描述
该方法内调用了findAutowireCandidates()方法主要用于根据类型查找依赖,大致可以分为三步:先查找内部依赖,再根据类型查找,最后没有可注入的依赖则进行补偿。其中@Qualifier注解就是在这个函数中处理的。实现从容器中找到所有对应类型的bean,最终会调用getBean()方法,这样当对应类型的对象还没被创建时,就会去创建。
我们再返回doResolveDependency()方法,查看下面的逻辑:
在这里插入图片描述
可以看到又一次调用了findAutowireCandidates()方法,会查找对应类型的bean,如果没有查找到并且该属性是必须要要注入的,则会抛异常。
继续往下走,可以看到:
在这里插入图片描述
如果查找对应类型bean>1,则会执行determineAutowireCandidate()方法,从字面意思来看的话是决定选择注入属性。可以看到执行完该方法后,如果autowireedBeanName为null也就是说无法决定使用哪一个,则抛异常。我们重点看下determineAutowireCandidate()方法的逻辑:
在这里插入图片描述
这个逻辑就比较明确了,相同类型的选取规则就是:首先选取@Primary注解的对象;其次根据@Priority来选取优先级高的对象;最后根据属性名称与spring的beanName来判断。如果按照这个还是没有选择出来的话,就会抛出异常(throw new NoUniqueBeanDefinitionException))。
再次返回doResolveDependency()方法,在执行完determineAutowireCandidate()方法后,会执行如下逻辑:
在这里插入图片描述
在这里插入图片描述
可以看到通过beaName及类型,调用getBean()方法向容器获取依赖bean对象,若该对象还没有构建,则会构建该对象,直到依赖的所有对象都已构建好,然后进行属性注入。后面的就是一直校验及判断逻辑了,在此不做赘述。
11. 至此AutowiredAnnotationBeanPostProcessor通过postProcessPropertyValues()方法完成了自动装配。以上就是@Autowired注解的实现原理。

@Resource

实现原理

@Resource的实现原理与@Autowired原理基本类似,不做赘述,只说明一下不同点:
1.@Resource注解的实现是通过后置处理器CommonAnnotationBeanPostProcessor类的postProcessPropertyValues()方法实现的。
2. CommonAnnotationBeanPostProcessor的findResourceMetadata()方法只解析出bean中带有@Resource注解、@WebServiceRef和@EjB注解的属性和方法
在这里插入图片描述
在这里插入图片描述
3. 查找顺序,看源码:
在这里插入图片描述
在这里插入图片描述
首先会判断是否是属性(字段)注入,我们以属性输入为例,重点看下getResourceToInject(target, requestingBeanName):

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
    return this.lazyLookup ? CommonAnnotationBeanPostProcessor.this.buildLazyResourceProxy(this, requestingBeanName) : CommonAnnotationBeanPostProcessor.this.getResource(this, requestingBeanName);
}

(这个代码为啥不换行,截图都不好截取。)
看代码描述如果是@Lazy则会返回一个代理对象,如果不是的话就执行CommonAnnotationBeanPostProcessor.this.getResource(this, requestingBeanName)逻辑,我们看下该逻辑代码:
在这里插入图片描述
继续查看this.autowireResource(this.resourceFactory, element, requestingBeanName):
在这里插入图片描述
如图示:第二与第三的执行逻辑是一样的,所以关键就是第一个的判断条件,如果this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)为true,那么获取属性的逻辑与@Autowired的逻辑一样。如果为false,是根据resource = factory.getBean(name, element.lookupType)获取属性,此时是根据ByName注入属性。
如果 !factory.containsBean(name)为true的话,说明容器内按照名称查找该name的bean是没有的,所以会执行beanFactory.resolveDependency()方法,执行@Autowired相同逻辑。

结论

根据上面源码分析,严谨来说,@Resource并不是传统意义所说的先按照name再按照type注入属性的,有三个if分支,第一个if是先判断有没有name相关bean再执行ByType—@Primary—@Priority—byName。

@Autowired与@Resource的不同

简单描述下,能说上来就行:

来源不同

@Autowired是Spring定义的注解,而@Resource是JSR-250定义的注解。

参数不同

@Autowired只包含一个参数:required,表示是否开启自动准入,默认是true。而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。@Autowired如果要使用byName,需要使用@Qualifier一起配合。而@Resource如果指定了name,则用byName自动装配,如果指定了type,则用byType自动装配。

使用不同

@Autowired能够用在:构造器、方法、参数、成员变量和注解上,而@Resource能用在:类、成员变量和方法上。

装配顺序

@Autowired默认按byType自动装配,而@Resource默认byName自动装配。

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

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

相关文章

idea自带database连接mysql失败问题

idea2023.1版连接mysql失败 DBMS: MySQL (ver. 5.7.13) Case sensitivity: plainexact, delimitedexact Driver: MySQL Connector Java (ver. mysql-connector-java-5.1.47 ( Revision: fe1903b1ecb4a96a917f7ed3190d80c049b1de29 ), JDBC4.0) [08S01]Communications link fai…

【Python】漏斗模型--生成漏斗案例 Demo实例

文章目录 背景一、漏斗模型二、漏斗模型案例1. 电商购物流程2. AARRR模型 三、如何绘制漏斗模型&#xff1f;总结 背景 很早之前就知道漏斗模型&#xff0c;但没有做更多的了解和运用&#xff0c;后来对漏斗模型的了解稍加深入之后&#xff0c;觉得它不仅仅是一个模型&#xf…

优思学院:什么是快速改善方法(Kaizen Blitz)?

什么是快速改善方法&#xff08;Kaizen Blitz&#xff09;&#xff1f; Kaizen blitz是精益管理中的一种方法&#xff0c;指通过集中一段时间内的团队努力来实现快速改进的方法。 Kaizen是一个日语词汇&#xff0c;意为“改善”&#xff0c;是一种广泛应用于企业管理的哲学&a…

软考(中/高级)高频考点——进度管理

现在距离2023年上半年软考仅有一个多月时间了&#xff0c;相信很多考友都进入火热的备考状态了。 为了给备考的考友们减轻备考难点&#xff0c;小编特意为大家整理了软考&#xff08;中/高级&#xff09;的一些高频考点——进度管理&#xff0c;希望对正在备考软考的你有所帮助…

Redis在linux下安装

1.下载安装包 redis官网: Download | Redis 2.解压 2.1在目录下解压压缩包 tar -zxvf redis-7.0.11.tar.gz 2.2将redis移至另一目录下并改名为redis mv redis-7.0.11 /usr/local/redis 3.编译 进入到redis目录下&#xff0c;make命令编译 [rootVM-24-15-centos local]# cd…

直动式球形止回阀DCV-080-PB、DCV-100-PB

特性 导通型。 硬质阀芯和阀座寿命更长。 工业化通用阀腔。 紧凑的尺寸。 直动球形止回阀 ZCO-42 0类 直动提升止回阀 ZCP6 0类 直动球形止回阀 ZC-62 0类 直动式球形止回阀 DCV-080-B 系列8 直动式球形止回阀 DCV-100-B 系列10 直动式球形止回阀 DCV-080-PB 系列8 …

【HAL库】STM32F407----CAN通信----中断详解

CAN通信----基本原理 CAN通信----电路图 一、CAN通信----中断简介 STM32F407的CAN通信一共有四个专用中断&#xff0c;分别是&#xff1a; 发送中断FIFO0 接收中断FIFO1 接收中断错误中断 具体如下图所示&#xff1a; 二、CAN通信----中断寄存器 CAN中断使能寄存器&#x…

颠覆Android开发行业未来,让Kotlin成为您的新宠

会Java还要学习kotlin吗&#xff1f; 看看这位老哥的回答&#xff1a; kotlin语言有前景吗&#xff1f; 看看在职高级开发怎么说的&#xff1a; Kotlin是什么&#xff1f; Kotlin是一种基于Java虚拟机&#xff08;JVM&#xff09;的静态类型编程语言&#xff0c;可以与Java代…

天,地,人,伤寒六经概述

天&#xff0c;地&#xff0c;人&#xff0c;伤寒六经概述 人天地天地气机伤寒六经三阳:三阴:太阳&#xff0c;少阳&#xff0c;阳明&#xff0c;太阴&#xff0c;少阴&#xff0c;厥阴 人 人之所以是一个活着的人&#xff0c;就在于人能够不断地与外界进行物质交换&#xff0…

CSDN周赛第48期

不知不觉又过去两期周赛&#xff0c;相应地&#xff0c;题解也落下了。而当我再回去想下载考试报告时。。。 现在更新的速度有这么快了么&#xff1f; 可惜题目还是考过的旧题&#xff0c;尤其对我们这种老油子来说&#xff0c;最大的好处是省去了阅读理解的烦恼。 平心而论&…

【论文阅读】CubeSLAM: Monocular 3D Object SLAM

一、基于单目图像的3d目标检测 这一部分是论文中最难理解的一章&#xff0c;作者的主要想法&#xff0c;是利用2d图像来生成3d的目标包围框&#xff08;bounding box&#xff09;&#xff0c;一方面这个思路本身就不是很好懂&#xff0c;另一方面&#xff0c;作者写这一章还是…

【linux】虚拟机安装部署

找了很多教程&#xff0c;有的不成功&#xff0c;记录一下成功的参考&#xff0c;感谢大佬们&#xff01; 1.虚拟机VMware安装 安装教程&#xff1a;https://blog.csdn.net/qq_45793637/article/details/128159966 2.安装centos7 Linux镜像下载地址&#xff1a;centos-7-is…

使用多模态数据映射大脑网络

前言 人脑由解剖和功能性的网络组织而成&#xff0c;这些网络涉及大规模分布但相互作用的脑区。不同脑区之间的同步性已在手指敲击或视觉刺激等实验任务活动中观察到&#xff0c;但更重要的是&#xff0c;也在无任务条件下(即静息态)测量的内源性活动中被观察到。即使在休息时…

密歇根大学Python 系列之三:Python 数据科学应用项目

Python在数据科学领域的应用已经成为了趋势&#xff0c;同时也在不断地发展和演化。对于从事数据科学相关工作的从业者来说&#xff0c;熟练掌握Python已经成为了必备技能之一。而对于其他从业者来说&#xff0c;了解Python在数据科学领域的应用也可以帮助他们更好地理解数据科…

linux系统中的用户态和内核态

linux系统中的用户态和内核态 文章目录 linux系统中的用户态和内核态[TOC](文章目录) 定义一、Linux系统简介Linux内核结构 二、总结 定义 在Linux系统中&#xff0c;用户态和内核态是两种不同的运行模式&#xff0c;它们主要区别在于程序所处的权限和访问硬件资源的方式。 用…

NVIDIA-SMI has failed because it couldn‘t communicate with the NVIDIA driver.解决

利用ubuntu自带的“软件与更新”安装完nvidia显卡驱动之后出现了如下提示。 NVIDIA-SMI has failed because it couldnt communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.重启系统&#xff0c;在进行ubuntu启动引导的…

智能学习 | MATLAB实现PSO-SVM多输入单输出回归预测(粒子群算法优化支持向量机)

智能学习 | MATLAB实现PSO-SVM多输入单输出回归预测(粒子群算法优化支持向量机) 目录 智能学习 | MATLAB实现PSO-SVM多输入单输出回归预测(粒子群算法优化支持向量机)预测效果基本介绍模型原理程序设计参考资料预测效果 基本介绍 MATLAB实现PSO-SVM多输入单输出回归预测(粒…

adk部署win10

adksetup.exe安装 百度搜索adk 在这里插入图片描述 https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install下载adk 注意:要和操作系统版本对应上,我这里是1903版本的win10。

分布式事务处理方案及分布式锁相关

​ 本文偏理论 一、事务处理 1、事务处理的四个特性ACID Atomicity 原子性: 对于数据库的修改&#xff0c;全部执行or全部不执行 Consistency 一致性: Isolation 隔离性 : 亦称为串行化&#xff0c;防止事务间操作混淆&#xff0c;需要串行化或者序列化请求&#xff0c;使…

Java迭代

迭代(iterate)&#xff1a;简单来说&#xff0c; 它就是一个循环。 集合框架中的Iterator接口定义了迭代器的功能&#xff0c; 迭代器的用途是遍历集合&#xff08;容器&#xff09; 中的所有元素。 1. Iterator接口 public interface Iterator<E> {boolean hasNext();…