【SpringBoot:详解Bean装配】

news2025/5/23 5:47:23

🏡Java码农探花

 🔥 推荐专栏:<springboot学习>

🛸学无止境,不骄不躁,知行合一


文章目录
  • 前言
  • 一、IoC容器的简介
  • BeanFactory接口源码
  • 二、Bean装配
  • 扫描装配
  • 探索启动类
  • 条件装配
  • 自定义Bean
  • 总结


前言

IoC((Inversion of Control,控制反转)容器是 Spring 的核心,可以说 Spring 是一种基于 IoC容器编程的框架。因为Spring Boot 是基于注解的开发 Spring IoC, 所以我们就从全注解的方式来讲诉Bean装配。


一、IoC容器的简介

Spring IoC 容器是一个管理 Bean 的容器,在 Spring 的定义中,它要求所有的 IoC 容器都需要实现接口 BeanFactory,它是一个顶级容器接口。 我们从源码讲诉。

BeanFactory接口源码

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
	// 前缀
    String FACTORY_BEAN_PREFIX = "&";
	
	// 多个getBean方法
    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
	
	// 是否包含Bean
    boolean containsBean(String name);
	
	//是否单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	
	// 是否原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	
	// 是否类型匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
	
	// 获取Bean的类型
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	
	// 获取Bean的别名
    @Nullable
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

    String[] getAliases(String name);
}

分析:

  • 上诉源码中加入了中文注释,通过它们就可以理解这些方法的含义。
  • 这里值得注意的是接口中的几个方法:
    • 首先我们看到了多个getBean 方法,这也是IoC 容器最重要的方法之一, 它的意义是从IoC 容器中获取Bean而从多个getBean方法中可以看到有按类型(bytype)获取Bean 的,也有按名称(by name)获取 Bean 的,这就意味着在 Spring IoC 容器中,允许我们按类型或者名称获取 Bean。这对理解后面将讲到的Spring 的依赖注入(Dependency Injection, DI) 是十分重要的。
    • isSingleton 方法则判断 Bean 是否在 Spring IoC 中为单例。这里需要记住的是在 Spring IoC 容器中,默认的情况下, Bean 都是以单例存在的,也就是使用 getBean 方法返回的都是同一个对象。与isSingleton 方法相反的是 isPrototype 方法,如果它返回的是 true,那么当我们使用 getBean 方法获取Bean 的时候, Spring IoC 容器就会创建一个新的 Bean 返回给调用者。

由于BeanFactory 的功能还不够强大,因此 Spring 在 BeanFactory 的基础上, 还设计了一个更为高级的接口 ApplicationContext。 它是 BeanFactory 的子接口之一, 在 Spring 的体系中 BeanFactory 和ApplicationContext 是最为重要的接口设计,在现实中我们使用的大部分 Spring IoC 容器是ApplicationContext 接口的实现类。

在这里插入图片描述

  • 在图中可以看到, ApplicationContext 接口通过继承上级接口,进而继承 BeanFactory 接口, 但是在BeanFactory 的基础上,扩展了消息国际化接口(MessageSource)、环境可配置接口 (EnvironmentCapable)、应用事件发布接口(ApplicationEventPublish巳r) 和资源模式解析接口(ResourcePatternResolver),所以它的功能会更为强大
  • 在Spring Boot 当中我们主要是通过注解来装配Bean到 Spring IoC 容器中,为了贴近 SpringBoot 的需要, 这里不再介绍与 XML 相关的 IoC 容器,而主要介绍一个基于注解的 IoC 容器,它就是AnnotationConfigApplicationContext,从名称就可以看出它是一个基于注解的 IoC 容器。 之所以研究它, 是因为Spring Boot 装配和获取 Bean 的方法与它如出一辙。

例:创建一个User类,然后使用AnnotationConfigApplicationContext构建IoC容器

public class User {
	private Long id; 
	private String userName; 
/**setter and getter **/ 
}

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Con f工guration ;
import com.springboot.chapter3.po] o.User;
@Configuration 
public class AppConfig { 
	@Bean(name =”user” } 
	public User ini tUser () { 
		User user= new User (); 
		user. set Id (1L) ; 
		user.setUserName (”aa”); 
		return user;
	}
}

@Configuration 代表这是一个 Java 配置文件, Spring 的容器会根据它来生成IoC 容器去装配Bean;@Bean 代表将 initUser 方法返回的 POJO 装配到 IoC 容器中,而其属性name 定义这个 Bean 的名称如果没有配置它,则将方法名称“initUser”作为 Bean 的名称保存到Spring IoC 容器中。

import org.apache. log4j .Logger; 
import org. springframework.context.ApplicationContext; 
import org. springframework.context annotation.AnnotationConfigApplicat工onContext;
import com.springboot.chapter3.po] o .User;
public class IoCTest { 
	private static Logger log= Logger.getLogger(IoCTest.class); 
	publ工c static 飞roid main (String [] args) { 
		ApplicationContext ctx = new AnnotationConfigAppl丰cationContext(AppConfig. class);
 		User user= ctx.getBean(User.class); 
	}
}

代码中将Java 配置文件 AppConfig 传递给 AnnotationConfigApplicationContext 的构造方法,这样它就能够读取配置了。然后将配置里面的Bean装配到IoC容器中,于是可以使用 getBean方法获取对应的POJO。

二、Bean装配

扫描装配

上诉讲诉的User对象装配就是使用@Bean装配。但是如果一个个的 Bean 使用注解@Bean 注入 Spring loC 容器中,那将是一件很麻烦的事情。好在Spring 还允许我们进行扫描装配 Bean 到 loC 容器中,对于扫描装配而言使用的注解是@Component和@ComponentScan@Component 是标明l哪个类被扫描进入 Spring IoC 容器,而ComponentScan则是标明采用何种策略去扫描装配Bean

@Component(”user") 
public class User { 
	@Value("1") 
	private Long id; 
	@Value("aa"} 
	private String userName; 
/**setter and getter **/ 
}

这里的注解@Component表明这个类将被SpringIoC 容器扫描装配,其中配置的“user"则是作为Bean 的名称,当然你也可以不配置这个字符串,那么IoC容器就会把类名第一个字母作为小写其他不变作为Bean 名称放入到IoC 容器中注解@Value则是指定具体的值,使得Spring IoC给予对应的属性注入对应的值。为了让SpringIoC 容器装配这个类, 需要改造类AppConfig:

import org.springframework.context.annotat工on.ComponentScan;
import org.springframework.context.annotation Configuration; 
@Configuration 
@ComponentScan 
public class AppConfig {
}

这里加入了@ComponentScan,意味着它会进行扫描,但是它只会扫描类AppConfig所在的当前包和其子包。也就是@ComponentScan默认扫描当前类所在包及其子包。 所以User类的位置要注意。

测试:

Applicat工onContext ctx = new AnnotationConfigApplicationContext{AppConfig.class) ;
User user= ctx.getBean(User.class); 
log. info(user.getid());

为了更加合理,@ComponentScan还允许我们自定义扫描的包,我们看一下源码:

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//在一个类中可重复定义
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

	// 定义扫描的包
    @AliasFor("basePackages")
    String[] value() default {};
	
	//定义扫描的包
    @AliasFor("value")
    String[] basePackages() default {};
	
	//定义扫描的类
    Class<?>[] basePackageClasses() default {};
	
	//Bean name生成器
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
	
	//作用域解析器
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
	
	//作用域代理模式
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
	
	//资源匹配模式
    String resourcePattern() default "**/*.class";
	
	//是否启用默认的过滤器
    boolean useDefaultFilters() default true;
	
	//当满足过滤器的条件时扫描
    Filter[] includeFilters() default {};
	
	//当不满足过滤器的条件时扫描
    Filter[] excludeFilters() default {};
	
	//是否延迟初始化
    boolean lazyInit() default false;
	
	//定义过滤器
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
   		//过滤器类型,可以按注解类型或者正则式等过滤
        FilterType type() default FilterType.ANNOTATION;
		
		//定义过滤的类
        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};
		
		//匹配方式
        String[] pattern() default {};
    }
}

分析:

  • 首先可以通过配置项basePackages定义扫描的包名,在没有定义的情况下,它只会扫描当前包和其子包下的路径:还可以通过basePackageClasses 定义扫描的类;
  • 其中还有 includeFilters 和 excludeFilters, includeFilters 是定义满足过滤器(Filter)条件的 Bean 才去扫描, excludeFilters 则是排除过滤器条件的 Bean,它们都需要通过一个注解@Filter 去定义,它有一个type 类型,这里可以定义为注解或者正则式等类型。 classes定义注解类, pattern 定义正则式类

所以得出三个扫描路径表示:

@ComponentScan ("com.springboot.example.* ")
@ComponentScan(basePackages = {"com.springboot.example.pojo"})
@ComponentScan(basePackageClasses = {User.class} ) 

以及排除扫描包或类,让其不被装配:

//扫描example下所有包除了@Service装配的类
//这样,由于加入了 excludeFilters 的配置,使标注了@Service 的类将不被 IoC 容器扫描注入,这样就可以把它类排除到 Spring IoC容器中了。
@ComponentScan(basePackages = {"com.dragon.restart"},excludeFilters = {@ComponentScan.Filter(classes = Service.class)})
探索启动类

事实上,之前在 Spring Boot 的注解@SpringBootApplication 也注入了@ComponentScan,这里不妨探索其源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
//自定义排除的扫描类
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
	//通过类型排除自动配置
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};
	//通过名称排除自动配置类
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};
	//定义扫描包
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};
	//定义被扫描的类
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

显然,通过它就能够定义扫描哪些包。但是这里需要特别注意的是,它提供的exclude和excludeName两个方法是对于其内部的自动配置类才会生效的。为了能够排除其他类,还可以再加入@ComponentScan以达到我们的目的。

条件装配

  • 例如在数据库连接池的配置中漏掉一些配置会造成数据源不能连接上。 在这样的情况下, IoC容器如果还进行数据源的装配, 则系统将会抛出异常,导致应用无法继续。这时倒是希望IoC容器不去装配数据源。
  • 为了处理这样的场景, Spring 提供了@Conditional注解帮助我们,而它需要配合另外一个接口Condition(org.springframework.context.annotation.Condition )来完成对应的功能。

装配的Bean:

@Bean(name = "dataSource", destroyMethod = "close" ) 
@Conditional(DatabaseConditional.class)
public DataSource getDataSource ( 
@Value("${database.driverName}") String driver, 
@Value("${database.url}") String url, 
@Value("${database.username}") String username, 
@Value("{database.password}") String password 
){ 
	Properties props= new Properties(); 
	props.setProperty("driver", driver); 
	props setProperty("url", url); 
	props.setProperty("username", username); 
	props setProperty("password", password); 
	DataSource dataSource = null; 
	try { 
		dataSource = BasicDataSourceFactory.createDataSource(props) ; 
	) catch (Exception e) { 
		e.printStackTrace();
	}
	return dataSource;
}

自定义DatabaseConditional类:

public class DatabaseConditional implements Condition { 
/**
* 数据库装配条件
* 
* @param context 条件上下文
* @param metadata 注释类型的元数据
* @return true 装配 Bean,否则不装配
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
	//取出环境配置
	Environment env = context.getEnvironment(); 
	//判断属性文件是否存在对应的数据库配置
	return env.containsProperty(”database.driverName” ) 
		&& env.containsProperty(”database.url”) 
		&& env.containsProperty(” database.username”) 
		&& env.containsProperty (”database.password");

matches 方法首先读取其上下文环境, 然后判定是否已经配置了对应的数据库信息。这样,当这些都己经配置好后则返回true。这个时候Spring会装配数据库连接池的Bean,否则是不装配的。

自定义Bean

  • 现实的Java 的应用往往需要引入许多来自第三方的包, 并且很有可能希望把第三方包的类对象也放入到Spring IoC 容器中,这时@Bean注解就可以发挥作用了。
  • 例如,要引入一个DBCP数据源,我们先在pom.xml上加入项目所需要DBCP包和数据库MySQL驱动程序的依赖。
<dependency> 
	<groupid>org.apache.commons</groupid> 
	<artifactid>commons-dbcp2</artifactid> 
</dependency> 
<dependency> 
	<groupid>mysql</groupid> 
	<artifactid>mysql-connector-ava</artifactid>
</dependency>

这样 DBCP 和数据库驱动就被加入到了项目中,接着将使用它提供的机制来生成数据源:

@Bean(name = "dataSource") 
@Conditional(DatabaseConditional.class)
public DataSource getDataSource (){ 
	Properties props= new Properties(); 
	props.setProperty("driver", driver); 
	props setProperty("url", url); 
	props.setProperty("username", username); 
	props setProperty("password", password); 
	DataSource dataSource = null; 
	try { 
		dataSource = BasicDataSourceFactory.createDataSource(props) ; 
	) catch (Exception e) { 
		e.printStackTrace();
	}
	return dataSource;
}

这里通过@Bean 定义了其配置项 name 为“dataSource“,那么 Spring 就会把它返回的对象用名称“dataSource” 保存在 loC 容器中。当然, 你也可以不填写这个名称,那么它就会用你的方法名称作为Bean 名称保存到 IoC 容器中。通过这样,就可以将第三方包的类装配到SpringIoC容器中了。


总结

以上就是SpringBoot的Bean装配的详细讲解。

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

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

相关文章

springboot 人大金仓 kingbase-备份还原,命令中带密码

命令带密码参考 Java代码实现国产人大金仓数据库备份还原需求-CSDN博客文章浏览阅读818次&#xff0c;点赞16次&#xff0c;收藏12次。本人在一次项目中&#xff0c;遇到了需要在系统管理中提供给用户备份还原系统数据的功能&#xff0c;由于项目特殊性&#xff0c;项目底层数…

如何使用电商接口获取商品信息,价格,主图?

1、什么是电商接口&#xff1f; 电商接口是指用于实现电商平台与其他系统之间进行数据交互和功能调的一组规范和方法。通过电商接口&#xff0c;可以实现商品信息的获取、订单管理、支付处理、物流跟踪等功能。 常见的电商接口包括&#xff1a; 商品接口&#xff1a;用于获取…

BLDC的机械角度、电角度与换相时间

6步换相梯形控制&#xff0c;6步一个周期定义为电角度360 总电角度 极对数 * 机械角度 机械角度是指360&#xff0c;一个圆周角&#xff1b; 电角度度指转子转动的角度&#xff0c;从一个N极到下一个N极是360。从N极只到 S极&#xff0c;则转子只运行了180。由于一个电机中通…

Python数据分析案例40——电商直播间成交金额预测

承接上一篇案例电商直播间提取的特征&#xff0c;进而做一篇机器学习的案例&#xff0c;来预测直播间的成交金额。 Python数据分析案例39——电商直播间评论可视化分析&#xff08;LDA&#xff09; 1. 引言 1.1 直播电商与传统电商的比较 直播电商作为一种新兴的电子商务模式…

本地web项目启起来后,无法在浏览器(chrome)看到源码,从而无法打断点;Framework Ignore list

问题描述 本地web项目启起来后&#xff0c;无法在浏览器(chrome)看到源码&#xff0c;从而无法打断点 其他浏览器没看&#xff0c;开发环境一致专注于chrome&#xff08;其余浏览器有测试同事提缺陷了&#xff0c;才会去看&#xff09;&#xff0c;其余浏览器有没有这个问题&…

B端:拖拽功能有哪些框架扩展,用起来爽歪歪。

B端系统拖拽交互功能是指在企业级系统中&#xff0c;用户可以通过拖拽元素来实现交互操作的功能。这种交互方式可以提高用户的操作效率和用户体验&#xff0c;常见的应用场景包括拖拽排序、拖拽调整大小、拖拽改变位置等。 在实现拖拽交互功能时&#xff0c;可以使用多种技术和…

必备工具!16.8k star 的项目!帮助你快速清理重复文件

大家的笔记本是不是都使用了好多年&#xff1f;是不是硬盘里的文件越来越多&#xff0c;尤其是经常有一些重复的文件散落在系统的各个角落&#xff0c;不好找&#xff0c;也很占据空间。今天就给大家介绍一款好用的开源工具&#xff0c;帮助你简单快速的清理电脑里的重复文件—…

Vue3+.NET6前后端分离式管理后台实战(十三)

1&#xff0c;Vue3.NET6前后端分离式管理后台实战&#xff08;十三&#xff09;已经在公众号发布有兴趣的可以关注一下&#xff01; 有兴趣的可以扫码关注&#xff01;

探索网络爬虫:技术演进与学习之路

网络爬虫及IP代理池 前言爬虫技术的演进最新的爬虫技术爬虫技术学习路线 前言 在信息时代&#xff0c;网络爬虫技术作为获取和处理网络数据的重要手段&#xff0c;已经成为数据科学、机器学习和许多商业应用的基石。从简单的HTML页面抓取到复杂的动态内容采集&#xff0c;爬虫…

Flex弹性布局详解

详解Flex弹性布局 1. 什么是Flex布局2. Flex布局核心概念1&#xff09;容器和属性定义2&#xff09;主轴和交叉轴定义3&#xff09;容器属性4&#xff09;项目属性 3. 优缺点 1. 什么是Flex布局 Flex全称为 “Flexible Box Layout”&#xff0c;即 “弹性盒布局”&#xff0c;旨…

Java应用CPU飙升和死锁排查实战教程

引言 在日常开发中&#xff0c;我们可能会遇到Java应用CPU飙升和死锁的问题。本文将通过实际案例&#xff0c;为大家介绍如何排查这些问题 Java应用CPU飙升和死锁排查步骤 先执行top命令&#xff0c;找到CPU占用比较高的进程再执行jstack 进程id > dump.txt找到进程中CPU…

软考127-上午题-【软件工程】-McCabe度量法

一、McCabe度量法 1-1、定义 McCabe 度量法是通过定义环路复杂度&#xff0c;建立程序复杂性的度量。 它基于一个程序模块的程序图中环路的个数。计算有向图G的环路复杂性的公式为&#xff1a; V(G) m - n 2 闭合区域 1 其中V(G)是有向图 G 中的环路个数&#xff0c;m 是…

[全网最全]2024MathorCup妈妈杯ABCD题成品论文33页+配套完整代码数据汇总

所有题目的每一小问解答&#xff08;含配套代码和数据&#xff09;都已经更新完毕&#xff0c;其中C题成品论文33页更新&#xff0c;B题论文更新&#xff0c;A题半成品论文21页完整解答代码数据。 &#xff08;完整版的资料放在文末了&#xff09; A题 移动通信网络中PCI规划问…

蓝桥杯嵌入式(G431)备赛笔记——第十一届第二场真题

关键代码&#xff1a;、 user.c: u32 adc_tick 0; // 定义一个无符号32位整型变量 adc_tick&#xff0c;用于记录上次ADC处理的时间戳 u32 r37_value 0; // 定义一个无符号32位整型变量 r37_value&#xff0c;用于存储ADC通道2的采样值 u32 r38_value 0; // 定义一个无符号…

AI常见关键术语

哈喽&#xff0c;大家好&#xff0c;我是小码哥&#xff0c;人工智能技术的快速发展带来了许多专业术语&#xff0c;这些词汇对于理解AI的工作原理和应用至关重要。以下是一些关键的AI术语&#xff0c;以及它们的专业解释和通俗总结。 一、核心概念 人工智能 (AI) 专业解释&am…

轻量带屏解决方案之恒玄芯片移植案例

本文章基于恒玄科技BES2600W芯片的欧智通 Multi-modal V200Z-R开发板 &#xff0c;进行轻量带屏开发板的标准移植&#xff0c;开发了智能开关面板样例&#xff0c;同时实现了ace_engine_lite、arkui_ui_lite、aafwk_lite、appexecfwk_lite、HDF等部件基于OpenHarmony LiteOS-M内…

AI预测体彩排3第3弹【2024年4月14日预测--第1套算法开始计算第3次测试】

今天咱们继续测试第1套算法和模型&#xff0c;今天是第3次测试&#xff0c;目前的测试只是为了记录和验证&#xff0c;不建议大家盲目跟买。我的目标仍旧是10次命中3-4次!~废话不多说了&#xff0c;直接上结果&#xff01; 2024年4月14日排3的七码预测结果如下 第一套&…

mybatis的一对多

业务&#xff1a;通常主表从表 查询&#xff0c;一对多关系&#xff0c;通常是先查主表&#xff0c;然后拿主表的 关联字段与从表关联。在代码中 通常用for 循环等方法给 从表的数据赋值&#xff0c;很麻烦&#xff0c;&#xff0c;&#xff0c;很麻烦。。。。 用mybatis的…

软考中级--网络工程师-计算机基础与理论第二节无线基础知识

IEEE802.11 规定了多种 WLAN 通信标准&#xff0c;其中&#xff08; &#xff09;与其他标准采用的频段不同&#xff0c;因而不能兼容。 A IEEE802.11a B IEEE802.11b C IEEE802.11g D IEEE802.11n 试题答案 正确答案&#xff1a; A 答案解析 IEEE 802.11a规定采用5GHz的 ISM频…

Python | Leetcode Python题解之第25题K个一组翻转链表

题目&#xff1a; 题解&#xff1a; class Solution:# 翻转一个子链表&#xff0c;并且返回新的头与尾def reverse(self, head: ListNode, tail: ListNode):prev tail.nextp headwhile prev ! tail:nex p.nextp.next prevprev pp nexreturn tail, headdef reverseKGroup…