当前位置: 首页 > news >正文

@SpringBootApplication源码解析

1 简介

1.1 什么是自动装配?

自动装配是指 Spring Boot 在启动时,根据类路径上的依赖项自动配置应用程序。例如,如果你的应用程序依赖于 Spring Data JPA,Spring Boot 会自动配置一个 DataSourceEntityManagerFactory 和其他必要的 Bean,而无需你手动编写这些配置。

1.2 自动装配的工作原理
  1. 依赖管理:Spring Boot 使用 Maven 或 Gradle 管理依赖项。当你在 pom.xml 或 build.gradle 文件中添加依赖项时,Spring Boot 会检测这些依赖项。

  2. 自动配置类:Spring Boot 提供了一组自动配置类,这些类位于 spring-boot-autoconfigure 模块中。每个自动配置类都对应一个特定的技术栈或功能模块。

  3. 条件注解:自动配置类使用条件注解(如 @ConditionalOnClass@ConditionalOnMissingBean 等)来决定是否应用某个配置。这些注解确保只有在满足特定条件时才会应用配置。

2 源码解读

入口:@SpringBootApplication

//以下是@SpringBootApplication注解的内部信息 其中最核心的是后3条
@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) })

2.1 @SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
//这个注解的主要主要表明这是一个配置类 需要解析
public @interface SpringBootConfiguration {/*** Specify whether {@link Bean @Bean} methods should get proxied in order to enforce* bean lifecycle behavior, e.g. to return shared singleton bean instances even in* case of direct {@code @Bean} method calls in user code. This feature requires* method interception, implemented through a runtime-generated CGLIB subclass which* comes with limitations such as the configuration class and its methods not being* allowed to declare {@code final}.* <p>* The default is {@code true}, allowing for 'inter-bean references' within the* configuration class as well as for external calls to this configuration's* {@code @Bean} methods, e.g. from another configuration class. If this is not needed* since each of this particular configuration's {@code @Bean} methods is* self-contained and designed as a plain factory method for container use, switch* this flag to {@code false} in order to avoid CGLIB subclass processing.* <p>* Turning off bean method interception effectively processes {@code @Bean} methods* individually like when declared on non-{@code @Configuration} classes, a.k.a.* "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore behaviorally* equivalent to removing the {@code @Configuration} stereotype.* @return whether to proxy {@code @Bean} methods* @since 2.2*/@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}
2.2 @EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//自动注册一个basepackage的bean 作用是存储扫描路径 给其他组件使用
@AutoConfigurationPackage
//实现自动装配
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {/*** Environment property that can be used to override when auto-configuration is* enabled.*/String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/String[] excludeName() default {};}

2.2.1 @AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//引入注册器注册basepackage的bean
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {/*** Base packages that should be registered with {@link AutoConfigurationPackages}.* <p>* Use {@link #basePackageClasses} for a type-safe alternative to String-based package* names.* @return the back package names* @since 2.3.0*/String[] basePackages() default {};/*** Type-safe alternative to {@link #basePackages} for specifying the packages to be* registered with {@link AutoConfigurationPackages}.* <p>* Consider creating a special no-op marker class or interface in each package that* serves no purpose other than being referenced by this attribute.* @return the base package classes* @since 2.3.0*/Class<?>[] basePackageClasses() default {};}static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {//注册basepackage的beanregister(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}
2.2.2 @Import(AutoConfigurationImportSelector.class)
//这个我们先看实现的接口
//1 实现了DeferredImportSelector 而DeferredImportSelector继承了ImportSelector 作用是用来注册bean
//2 DeferredImportSelector对ImportSelector进行了增强 只有其他配置类扫描完了后 再解析这个引入的bean 为了确保条件注解的正确性
//3 重写了DeferredImportSelector的getImportGroup方法 调用好 优先会调用getImportGroup方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}public Class<? extends Group> getImportGroup() {//spring为注册分组的类 会自动调用该类的process方法return AutoConfigurationGroup.class;
}       public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,() -> String.format("Only %s implementations are supported, got %s",AutoConfigurationImportSelector.class.getSimpleName(),deferredImportSelector.getClass().getName()));//获取自动配置类            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);this.autoConfigurationEntries.add(autoConfigurationEntry);for (String importClassName : autoConfigurationEntry.getConfigurations()) {this.entries.putIfAbsent(importClassName, annotationMetadata);}
}protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}//1 获取注解的属性AnnotationAttributes attributes = getAttributes(annotationMetadata);//2 扫描自动装配类List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//3 去重configurations = removeDuplicates(configurations);//4 获取过滤的bean集Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);//5 移除过滤的beanconfigurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {//加载配置文件获取配置List<String> configurations = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);Assert.notEmpty(configurations,"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
}public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();//加载配置文件获取配置return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {Map<String, List<String>> result = (Map)cache.get(classLoader);if (result != null) {return result;} else {Map<String, List<String>> result = new HashMap();try {//从META-INF/spring.factories获取所有的自动装配类Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);Iterator var6 = properties.entrySet().iterator();while(var6.hasNext()) {Map.Entry<?, ?> entry = (Map.Entry)var6.next();String factoryTypeName = ((String)entry.getKey()).trim();String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());String[] var10 = factoryImplementationNames;int var11 = factoryImplementationNames.length;for(int var12 = 0; var12 < var11; ++var12) {String factoryImplementationName = var10[var12];((List)result.computeIfAbsent(factoryTypeName, (key) -> {return new ArrayList();})).add(factoryImplementationName.trim());}}}result.replaceAll((factoryType, implementations) -> {return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));});cache.put(classLoader, result);return result;} catch (IOException var14) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);}}
}

2.3 @ComponentScan()

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {private BeanFactory beanFactory;private Collection<TypeExcludeFilter> delegates;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {//去容器中获取所有类型为TypeExcludeFilter的bean 去执行过滤//我们可以实现这个方法 重写match方法 完成过滤if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {for (TypeExcludeFilter delegate : getDelegates()) {if (delegate.match(metadataReader, metadataReaderFactory)) {return true;}}}return false;}private Collection<TypeExcludeFilter> getDelegates() {Collection<TypeExcludeFilter> delegates = this.delegates;if (delegates == null) {delegates = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();this.delegates = delegates;}return delegates;}@Overridepublic boolean equals(Object obj) {throw new IllegalStateException("TypeExcludeFilter " + getClass() + " has not implemented equals");}@Overridepublic int hashCode() {throw new IllegalStateException("TypeExcludeFilter " + getClass() + " has not implemented hashCode");}}

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {private ClassLoader beanClassLoader;private volatile List<String> autoConfigurations;@Overridepublic void setBeanClassLoader(ClassLoader beanClassLoader) {this.beanClassLoader = beanClassLoader;}@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {//1 是否是一个配置类//2 是否有AutoConfiguration或者在自动装配类已配置//同时满足上面两个条件就不扫描这个beanreturn isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);}private boolean isConfiguration(MetadataReader metadataReader) {return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());}private boolean isAutoConfiguration(MetadataReader metadataReader) {boolean annotatedWithAutoConfiguration = metadataReader.getAnnotationMetadata().isAnnotated(AutoConfiguration.class.getName());return annotatedWithAutoConfiguration|| getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());}protected List<String> getAutoConfigurations() {if (this.autoConfigurations == null) {List<String> autoConfigurations = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader));ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader).forEach(autoConfigurations::add);this.autoConfigurations = autoConfigurations;}return this.autoConfigurations;}}

相关文章:

@SpringBootApplication源码解析

1 简介 1.1 什么是自动装配&#xff1f; 自动装配是指 Spring Boot 在启动时&#xff0c;根据类路径上的依赖项自动配置应用程序。例如&#xff0c;如果你的应用程序依赖于 Spring Data JPA&#xff0c;Spring Boot 会自动配置一个 DataSource、EntityManagerFactory 和其他必…...

【实战篇】requests库 - 有道云翻译爬虫 【附:代理IP的使用】

目录 〇、引言一、目标二、请求参数分析三、响应分析四、编写爬虫脚本【隧道代理的使用】 〇、引言 无论是学习工作、旅游出行、跨境电商、日常交流以及一些专业领域都离不开翻译工具的支持。本文就带大家通过爬虫的方式开发一款属于自己的翻译工具~ 一、目标 如下的翻译接口…...

法语动词变位

法语动词变位是法语语法的核心内容之一&#xff0c;因为法语动词的形式会根据人称&#xff08;谁做某事&#xff09;、时态&#xff08;动作发生的时间&#xff09;、语气&#xff08;说话人的态度&#xff09;和语态&#xff08;动作的执行者和接受者&#xff09;发生变化。接…...

Excel:vba实现批量插入图片

实现的效果&#xff1a; 实现的代码&#xff1a; Sub InsertImageNamesAndPictures()Dim PicPath As StringDim PicName As StringDim PicFullPath As StringDim RowNum As IntegerDim Pic As ObjectDim Name As String 防止表格里面有脏数据Cells.Clear 遍历工作表中的每个图…...

Vue3的router和Vuex的学习笔记整理

一、路由的基本搭建 1、安装 npm install vue-router --registryhttps://registry.npmmirror.com 2、配置路由模块 第一步&#xff1a;src/router/index.js创建文件 第二步&#xff1a;在src/view下面创建两个vue文件&#xff0c;一个叫Home.vue和About.vue 第三步&#x…...

设置JAVA以适配华为2288HV2服务器的KVM控制台

华为2288HV2服务器比较老旧了&#xff0c;其管理控制台登录java配置比较麻烦&#xff0c;华为的ibmc_kvm_client_windows客户端测试了几个版本&#xff0c;连接控制台也有问题&#xff0c;最终安装JDK解决。 一、测试环境 主机为WindowsServer2012R2,64位系统 二、Java软件包…...

掌握Qt调试技术

文章目录 前言一、Qt调试的基本概念二、Qt调试工具三、Qt调试实践四、Q调试技巧五、总结前言 在软件开发中,调试是一个至关重要的环节。Qt作为一个广泛使用的跨平台C++图形用户界面应用程序开发框架,其调试技术也显得尤为重要。本文将深入探讨Qt调试技术,帮助读者更好地掌握…...

使用NVM自由切换nodejs版本

一、NVM介绍 在日常开发中&#xff0c;我们可能需要同时进行多个不同NodeJS版本的项目开发&#xff0c;每个项目所依赖的nodejs版本可能不一致&#xff0c;我们如果只安装一个版本的nodejs&#xff0c;就可能出现node版本冲突问题&#xff0c;导致项目无法启动。这种情况下&am…...

同三维T610UHK USB单路4K60采集卡

USB单路4K60HDMI采集卡,支持1路4K60HDMI输入和1路4K60HDMI环出&#xff0c;1路MIC输入1路Line IN音频输入和1路音频输出&#xff0c;录制支持4K60、1080P120,TYPE-C接口&#xff0c;环出支持1080P240 HDR 一、产品简介&#xff1a; 同三维T610UHK是一款USB单路4K60HDMI采集卡,…...

Git超详细笔记包含IDEA整合操作

git超详细笔记 文章目录 git超详细笔记第1章Git概述1.1、何为版本控制1.2、为什么需要版本控制1.3、版本控制工具1.4 、Git简史1.5、Git工作机制1.6 、Git和代码托管中心 第2章Git安装第3章Git常用命令3.1、设置用户签名3.2、初始化本地库本地库&#xff08;Local Repository&a…...

摩尔线程嵌入式面试题及参考答案(2万字长文)

说一下你对 drm 框架的理解。 DRM(Direct Rendering Manager)是 Linux 系统中用于管理图形显示设备的一个重要框架。 从架构层面来讲,它处于内核空间,主要目的是为用户空间的图形应用程序提供一个统一的接口来访问图形硬件。DRM 包括内核态的驱动模块和用户态的库。内核态的…...

C++ 编程基础(3)数据类型 | 3.1、指针

文章目录 一、指针1、定义2、解引用3、指针的运算4、指针与数组4.1、通过指针操作数据4.2、指针与数组名的区别4.3、数组名作为函数形参 5、指针作为函数参数5.1、作为函数参数5.2、常指针与指针常量 6、指针与动态内存分配7、注意事项8、总结 前言&#xff1a; 在C编程中&…...

nacos本地虚拟机搭建切换wiff问题

背景 在自己的电脑上搭建了vm虚拟机&#xff0c;安装上系统&#xff0c;设置网络连接。然后在vm的系统上安装了中间件nacos&#xff0c;mysql&#xff0c;redis等&#xff0c;后续用的中间件都是在虚拟机系统上安装的&#xff0c;开发在本地电脑上。 我本地启动项目总是请求到…...

打造完整 Transformer 编码器:逐步实现高效深度学习模块

11. encoder 打造完整 Transformer 编码器&#xff1a;逐步实现高效深度学习模块 在深入理解了编码器块的核心结构后&#xff0c;下一步就是实现一个完整的 Transformer 编码器。该编码器将输入序列转换为高级语义向量&#xff0c;并为后续的解码或其他任务模块提供高质量的特…...

软件对象粒度控制与设计模式在其中作用的例子

在软件设计中&#xff0c;确定对象的粒度&#xff08;Granularity&#xff09;是一个重要的考量因素&#xff0c;它决定了对象的职责范围和复杂程度。粒度过细或过粗都可能影响系统的可维护性和性能。设计模式可以帮助我们在不同层面控制粒度和管理对象之间的交互。以下是对每种…...

代码随想录算法训练营Day.3| 移除链表元素 设计链表 反转链表

长沙出差ing&#xff0c;今天的核心是链表&#xff0c;一个比较基础且重要的数据结构。对C的指针的使用&#xff0c;对象的创建&#xff0c;都比较考察&#xff0c;且重要。 203.移除链表元素 dummyNode虚拟头节点很重要&#xff0c;另外就是一个前后节点记录的问题。但是Leet…...

基于SSM的学生考勤管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…...

制作gif动图并穿插到CSDN文章中

在我们编写文档时&#xff0c;需要放一些动图来增加我们文章的阅读性&#xff0c;在这里为大家推荐一款好用的软件LICEcap 一、下载LICEcap软件 安装包以百度网盘的形式放在了文章末尾&#xff0c;下载完成后&#xff0c;会出现下面的图标 二、如何操作 双击图标运行 会出现…...

字段值为null就不返回的注解

1. 导包 <dependency><groupId>com.fasterxml.jackson.module</groupId><artifactId>jackson-module-kotlin</artifactId> </dependency>2. 类上加注解 JsonInclude(value JsonInclude.Include.NON_NULL)3. 示例 Data JsonInclude(valu…...

spring-boot(整合aop)

第一步导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 日志依赖 <dependency><groupId>org.springframework.boot</groupI…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...