Gradle基础学习(六) 认识任务Task

理解Gradle中的任务

Gradle的构建过程基于任务(Task)的概念,而每个任务都可以包含一个或多个动作(Action)。

任务是构建中执行的一些独立的工作单元,例如编译类、创建JAR、生成Javadoc或将存档发布到仓库。

我们使用gradle taskName或gradlew taskName来执行任务,比如build:

$ ./gradlew build 

项目中所有可用任务都是来自Gradle插件和构建脚本。

比如在子项目构建脚本中创建的任务:

可用任务清单

我们通过运行tasks任务来列出项目所有可用任务。

$ ./gradlew tasks

让我们以一个非常基本的Gradle项目为例,该项目具有以下结构:

 settings.gradle.kts文件定义了根项目名称和app子项目:

rootProject.name = "HelloGradle"

include("app")

目前app子项目的构建文件暂时是一个空文件。

为了看app子项目中的任务,运行./gradlew :app:tasks 

结果中只能看到少量的辅助任务(Help Tasks),它们是Gradle核心提供的用于分析构建的任务。其他任务,如构建项目或编译代码的任务,则是由插件来添加的。

我们接下来在app构建脚本里添加application插件:

//app/build.gradle.kts

plugins {

    id("application")

}

application插件会添加一些生命周期任务。 现在再运行./gradlew app:tasks,我们看到多了一些和构建相关的任务,比如assemble 和 build任务:

任务结果

当Gradle执行一个任务时,它会在控制台中显示任务的结果标签。

这些标签描述了一个任务是否有要执行的动作,Gradle是否执行了这些动作。动作包括但不限于编译代码、压缩文件和发布存档。

(no label) or EXECUTED

任务执行了动作。

任务有动作,Gradle执行了它们。

任务没有动作,有一些依赖项,Gradle执行了一个或多个依赖项。

UP-TO-DATE

Gradle会检查任务的输入和输出,如果输入没有变化,并且输出已经存在且是最新的,那么任务就不会被执行,并标记为UP-TO-DATE。

任务有输出和输入,但都没有改变。

任务有动作,但是告诉Gradle它不会改变输出。

任务没有动作,有一些依赖项,但所有依赖都是UP-TO-DATE, SKIPPED 或 FROM-CACHE的。

任务没有动作,也没有任何依赖项。

FROM-CACHE

Gradle检测到任务的输出已经在构建缓存中时,会直接从缓存中加载,比如build cache。 

SKIPPED

任务不执行他的动作

任务被跳过可能是因为某个先决条件未满足,或者任务被明确地配置为跳过,比如使用命令行选项--exclude-task排除。

NO_SOURCE

任务不需要执行他的动作

任务有输入和输出,但没有源文件。

FROM-CACHE和UP-TO-DATE都是Gradle优化构建过程的手段,都有助于减少不必要的任务执行,提高构建速度。可能会弄不明白什么时候是UP-TO-DATE或者FROM-CACHE,在本文后面介绍缓存任务的时候,我们再做进一步说明。 

任务组group和描述description

任务组和描述用于组织和描述任务。

Groups

任务组被用来对任务进行分类,当运行./gradlew tasks时,所有任务会被列在各自的组中,这样更容易理解它们的目的和与其他任务的关系,使用group属性设置组。

Descriptions
描述提供了任务功能的简要解释。当运行./gradlew tasks时,描述会显示在每个任务的旁边,帮助开发者了解它的用途以及如何使用它,使用description属性设置描述。

我们回头去看一下前面gradle-project执行tasks的输出结果:

:run任务属于Application分组, 对其描述是 "Runs this project as a JVM application"。 

这个任务在代码中的定义会像这样:

//app/build.gradle.kts

tasks.register("run") {

    group = "Application"

    description = "Runs this project as a JVM application."

}

私有和隐藏任务

Gradle不支持将任务标记为“私有”。

当我们运行:tasks时,默认只会显示那些分配了任务组的任务,即所谓的可见任务,而那些没有分配group的任务,就是隐藏任务, 需要注意,隐藏任务依旧可以被Gradle执行,只是不显示而已。

如下所示,我们创建了一个任务helloTask,执行./gradlew :app:tasks,任务列表里并没有找到helloTask任务。

//app/build.gradle.kts

tasks.register("helloTask") {

    println("Hello")

}

给它分配一个group 

tasks.register("helloTask") {

    group = "Other"

    description = "Hello task"

    println("Hello")

}

任务就出现在了指定的Other分组下 :

执行./gradlew tasks --all 可以显示所有任务,包括隐藏的。 

比如上面的helloHiddenTask,我们没有设置group属性,也显示在了Other分组下。 

分组任务

如果我们想要自定义执行tasks时向用户显示哪些任务,可以对任务分组并设置每个组的可见性。

示例gradle-project虽然只是一个简单的Java应用,列出的可用的任务却非常多,使用构建的开发人员很少直接需要其中的许多任务。

我们可以通过配置tasks任务,限制任务显示到一个特定的分组。

我们修改一下构建脚本,创建一个自己的分组,使用displayGroup属性来指定要显示的任务组。

//app/build.gradle.kts

val myBuildGroup = "my app build"               // Create a group name

tasks.register<TaskReportTask>("tasksAll") {    // Register the tasksAll task

    group = myBuildGroup

    description = "Show additional tasks."

    setShowDetail(true)

    println("register tasksAll")

}

tasks.named<TaskReportTask>("tasks") { 

    displayGroup = myBuildGroup

}

在Gradle中,我们执行tasks任务时,会使用此类型的一个实例TaskReportTask,其中displayGroup属性用来控制要显示的任务组,默认值是null, 可以使用命令行选项 '--group'设置,设置后就只显示这个分组的任务。

任务类别 

Gradle中的任务分为两类:

1. Actionable tasks(可操作任务)

2. Lifecycle tasks(生命周期任务)

可执行任务定义了Gradle应该执行的具体操作。例如,:compileJava 任务,它编译项目的Java代码。这些操作包括创建JAR文件、压缩文件、发布归档文件等。当你应用了一个插件,如java-library,Gradle会自动添加与该插件相关的可执行任务。

生命周期任务定义了一系列的目标(targets),你可以调用这些目标来执行一系列的操作。例如,:build 就是一个常见的生命周期任务,用于构建整个项目。这类任务本身不执行具体的操作(actions)。相反,它们捆绑了可执行任务,当调用生命周期任务时,会触发与之关联的可执行任务。 基础Gradle插件(base Gradle plugin)只添加生命周期任务。这意味着如果你没有添加任何插件,Gradle仍然会提供这些基本的生命周期任务。

我们再看一下之前例子的:tasks结果

如果我们执行:build任务,会看到有好几个任务都被执行了,包括:app:compileJava任务。

可以表述为可执行任务:compileJava捆绑到了生命周期任务:build中。 

增量任务

Gradle任务的一个关键特征是它们的增量性。

Gradle可以重用之前构建的结果。因此,如果我们之前构建过项目,并且只进行了小幅更改,那么重新运行:build将不需要Gradle执行大量工作。

例如,如果我们只修改项目中的测试代码,保持生产代码不变,执行构建将仅重新编译测试代码。Gradle将生产代码的任务标记为UP-TO-DATE,表明自上次成功构建以来,它保持不变:

缓存任务

Gradle可以使用构建缓存来重用过去构建的结果。

要启用此功能,请使用--build-cache命令行参数或在gradle.properties文件中设置org.gradle.caching=true来激活构建缓存。

此优化有可能显著加速项目的构建:

当Gradle可以从缓存中获取一个任务的输出时,它会给这个任务贴上FROM-CACHE的标签。如果经常在分支之间切换,构建缓存很方便。Gradle支持本地和远程构建缓存。

我们通过实际执行其中的compileJava源码编译任务来加深对任务结果为UP-TO-DATE和FROM-CACHE的理解:

我们把之前的gradle-project例子修改一下

//app/build.gradle.kts

//添加执行入口类

application {

    mainClass = "gradle.project.App"

}

//app/src/main/java/gradle/project/App.java

package gradle.project;

public class App {

    public static void main(String[] args) {

        System.out.println("Hello World!");

   }

}

现在app子项目是一个可执行的Java 应用程序,App类是应用的执行入口。

先./gradlew clean删除各子项目build目录,确保项目是干净的状态。

执行compileJava任务编译java,  --build-cache告诉Gradle本次使用构建缓存,需要--info选项显示一些额外的构建信息:

./gradlew --build-cache compileJava --info

Settings evaluated using settings file '/Users/roy/Downloads/gradle-project/settings.gradle'.

Using local directory build cache for the root build (location = /Users/xxx/.gradle/caches/build-cache-1, removeUnusedEntriesAfter = 7 days).

Projects loaded. Root project using build file '/Users/xxx/Downloads/gradle-project/build.gradle'.

> Task :app:compileJava

Stored cache entry for task ':app:compileJava' with cache key db35214e1886f8b0ebbcc16e2fa7a618

BUILD SUCCESSFUL in 1s

1 actionable task: 1 executed

 执行成功后,app子项目里多了build目录,输出的信息里也看到了build cache目录默认的位置Gradle User Home/caches/build-cache-1/,在其中缓存了任务的输出。

再次执行

./gradlew --build-cache compileJava --info

> Task :app:compileJava UP-TO-DATE

Build cache key for task ':app:compileJava' is 3804aa4dacefba7c96c077f8de82ae3d

Skipping task ':app:compileJava' as it is up-to-date.

BUILD SUCCESSFUL in 1s

1 actionable task: 1 up-to-date

因为我们没有做任何改动,build目录里已经最新的输出了,所以compileJava任务会跳过,此时任务被标记为UP-TO-DATE。

执行./gradlew clean删除app的build目录,然后再执行

./gradlew --build-cache compileJava --info 

> Task :app:compileJava FROM-CACHE

Build cache key for task ':app:compileJava' is db35214e1886f8b0ebbcc16e2fa7a618

Task ':app:compileJava' is not up-to-date because:

  Output property 'destinationDirectory' file /Users/xx/Downloads/gradle-project/app/build/classes/java/main has been removed.

  Output property 'destinationDirectory' file /Users/xx/Downloads/gradle-project/app/build/classes/java/main/gradle has been removed.

  Output property 'destinationDirectory' file /Users/xx/Downloads/gradle-project/app/build/classes/java/main/gradle/project has been removed.

  Output property 'options.generatedSourceOutputDirectory' file /Users/xx/Downloads/gradle-project/app/build/generated/sources/annotationProcessor/java/main has been removed.

Loaded cache entry for task ':app:compileJava' with cache key db35214e1886f8b0ebbcc16e2fa7a618

BUILD SUCCESSFUL in 1s

1 actionable task: 1 from cache

任务标记为FROM-CACHE,并且从缓存中加载了上次执行的输出,日志中也有提示:

Loaded cache entry for task ':app:compileJava' with cache key db35214e1886f8b0ebbcc16e2fa7a618

 另外,app的build目录也有了,里面的输出应该就是直接从build cache中拿过来的。

 再再一次执行./gradlew --build-cache compileJava --info, 任务又是UP-TO-DATE了。

> Task :app:compileJava UP-TO-DATE

所以,对于要执行的任务,只要项目里已经存在最新输出,它就是UP-TO-DATE;否则如果任务启用构建缓存,并且在缓存里有最新输出,就是FROM-CACHE。 

可缓存任务和不可缓存任务

在Gradle中并非所有任务都可以或应该被缓存,除了少数内置任务是可缓存外,大部分任务由于各种原因(如不可预测的输出、外部依赖、任务配置等)可能不适合缓存。这些任务通常被标记为non-cacheable。

使用--build-cache选项可以让Gradle启用构建缓存功能。当这个选项被启用时,Gradle会尝试缓存可缓存任务的输出,并在后续构建中重用这些输出。

对于non-cacheable任务,Gradle会忽略构建缓存机制,并总是执行这些任务。通常是因为这些任务的输出可能依赖于不可预测或不可重复的因素,或者任务本身的配置不允许缓存。

开发者也可以显式地配置这类任务使其可缓存。

要确定一个任务是否可缓存,你可以查看任务的输出。如果任务有一个buildCacheable属性,并且它被设置为true,那么该任务就是可缓存的。如果任务没有明确的buildCacheable属性设置,或者它被设置为false,那么该任务就是non-cacheable的。

构建缓存的有效性还取决于构建环境的稳定性和一致性。如果构建环境经常变化(例如,使用了不同的构建机器或文件系统),那么构建缓存的效果可能会受到限制。因此,在使用构建缓存时,确保构建环境的一致性是非常重要的。

开发任务

在开发Gradle任务时,我们有两个选择:

1.使用现有的Gradle任务类型,比如Zip,Copy或Delete

2.创建自己的任务类型,比如MyResolveTask或者CustomTaskUsingToolchains

任务类型就是是Gradle Task类的子类。

对于Gradle任务,有三种状态需要考虑:

1.注册任务 - 在构建逻辑中使用一个任务(由您实现或由Gradle提供)。

2.配置任务 - 定义任务的输入和输出。

3.实现任务 - 创建一个自定义任务类(即自定义类型)

注册通常使用register()方法完成。

配置任务通常使用named()方法完成。

实现任务通常是通过扩展Gradle的DefaultTask类来完成的:

①: 注册Copy类型的myCopy任务。

②: 根据Copy API为注册的myCopy任务配置它所需的输入和输出。

③: 实现一个名为MyCopyTask的自定义任务类型,它扩展了DefaultTask并定义了copyFiles任务操作。

1.注册任务

我们可以通过在构建脚本和插件中注册任务来定义Gradle要执行的操作。

使用字符串作为任务名来定义任务:

//build.gradle.kts

tasks.register("myCopy") {

   doFirst {

        // Task action = Execution code

        // Run before exiting actions

    }

    doLast {

        // Task action = Execution code

        // Run after existing actions

    }

}

register()方法会将myCopy任务添加到TaskCollection中。

2.配置任务

Gradle任务必须经过配置来完成他们的操作。比如一个任务需要ZIP一个文件,我们就必须要配置文件名和文件位置。这可以参考Gradle Zip任务提供的API,学习如何正确进行配置。

在上图示例中,我们注册了一个myCopy任务

tasks.register<Copy>("myCopy")

我们可以在注册的时候就立即用代码块配置任务:

tasks.register<Copy>("myCopy") {

   from("resources")

   into("target")

   include("**/*.txt", "**/*.xml", "**/*.properties")

}

因为这个任务是Copy类型, 是Gradle支持的任务类型,所以可以使用Copy API,如from、to。

之后在需要的地方,都可以通过named()方法查找对应名字的任务来配置:

//build.gradle.kts

tasks.named<Copy>("myCopy") {

    from("resources")

    into("target")

    include("**/*.txt", "**/*.xml", "**/*.properties")

}

 注意,在named()调用时,如果指定的任务还没有注册,就会构建失败。

3.实现任务

Gradle提供了很多任务类型,包括 Delete, Javadoc, Copy, Exec, Tar和Pmd等等,如果都满足不了我们的构建逻辑需求,我们可以实现一个自定义的任务类型。

要创建一个任务类型,需要扩展DefaultTask,然后定义为一个抽象类(不用是具体实现类):

//app/build.gradle.kts
abstract class MyCopyTask extends DefaultTask {

}

Gradle 会通过解析 build.gradle 文件中的配置动态创建任务实例,这些实例是任务类的具体实现,它们包含了在配置中设置的所有参数和指定的动作。

task("taskName")与tasks.register("taskName")

在 Gradle 中,task("taskName") 和 tasks.register("taskName") 都被用来创建新的任务,但它们属于不同的 API 并具有一些细微的差别和用法上的考虑。

传统 DSL(领域特定语言)方式:

task("taskName") 是 Gradle 的传统 DSL(领域特定语言)方法,用于在 build.gradle 文件中声明一个任务。这种方式相对直观和简单,适用于简单的任务定义。

任务注册(Tasks API)方式:

tasks.register("taskName") 是 Gradle Tasks API 的一部分,用于以编程方式注册任务。它提供了更多的灵活性和控制,尤其是在需要基于其他任务或项目配置动态创建任务时。

主要区别:传统 DSL 方法在配置阶段就执行了任务的配置代码,而 Tasks API 则允许延迟配置,直到执行阶段才执行配置代码。这有助于避免在配置阶段发生不必要的副作用。

随着 Gradle 的不断进化,Tasks API 被认为是更现代和推荐的方式来创建和注册任务。

tasks.register("taskName") 实现延迟配置的原理主要基于 Gradle 的任务生命周期和任务注册机制。

在 Gradle 构建的生命周期中,任务(Task)的创建和配置是分开的。传统的 task("taskName") 语法在项目的配置阶段(configuration phase)就立即创建并配置了任务。这意味着,即使在任务从未被执行的情况下,其配置代码也会被执行。这有时可能会导致不必要的副作用,比如提前计算了某些值,或者执行了只在任务执行时才需要的逻辑。

相比之下,tasks.register("taskName") 使用了一种不同的方法。这个方法实际上并没有立即创建任务,而是注册了一个任务工厂(TaskFactory)。这个工厂会在任务首次执行时(execution phase)被调用,从而创建任务实例并执行其配置,这就是所谓的“延迟配置”(lazy configuration)。

具体来说,当你调用 tasks.register("taskName") 时,Gradle 会创建一个 TaskRegistration 对象,该对象封装了任务的配置逻辑(即你传递给 register 方法的闭包)。这个 TaskRegistration 对象会被添加到 Gradle 的任务容器中,但不会立即创建任务实例。

当 Gradle 执行阶段到来,并且需要执行名为 "taskName" 的任务时,Gradle 会从任务容器中检索相应的 TaskRegistration 对象,并调用其工厂方法来创建任务实例。此时,闭包中的配置逻辑才会被执行,从而配置新创建的任务实例。

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

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

相关文章

62-USB转JTAG or SPI电路设计

视频链接 USB转JTAG or SPI电路设计01_哔哩哔哩_bilibili USB 转 JTAG or SPI电路设计 第07课---USB转串口电路设计第 34&#xff5e;40课---USB硬件电路设计 第22课---SPI Flash电路设计 第31课---JTAG电路设计&#xff08;JLINK&XILINX&ALTERA&#xff09; 第…

代码随想录-算法训练营day31【贪心算法01:理论基础、分发饼干、摆动序列、最大子序和】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第八章 贪心算法 part01● 理论基础 ● 455.分发饼干 ● 376. 摆动序列 ● 53. 最大子序和 贪心算法其实就是没有什么规律可言&#xff0c;所以大家了解贪心算法 就了解它没有规律的本质就够了。 不用花心思去研究其…

5.Git

Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码文件&#xff08;Java类、xml文件、html文件等&#xff09;。通过Git仓库来存储和管理这些文件&#xff0c;Git仓库分为两种 本地仓库&#xff1a;开发人员自己电脑上的Git仓库远程仓库&#xff1a;远程…

360手机去除广告 360手机关闭弹窗广告 360手机刷机

360手机去除广告 360手机关闭弹窗广告 360手机刷机 360手机去广告 360手机刷机 360手机弹窗广告 永久去除360手机的各种广告教程 360手机禁止更新 360手机关闭广告 360手机去除内部广告 360手机资源网 360手机刷机资源下载链接&#xff1a;360rom.github.io 参考&#xff1a;…

如何高效封装App?小猪APP分发平台一站式解决方案

在移动应用开发领域&#xff0c;App封装&#xff08;App Packaging&#xff09;是一个至关重要的环节&#xff0c;它不仅关乎应用的安全性&#xff0c;还直接影响到最终用户体验和市场推广策略。本文旨在通过实战指南&#xff0c;揭示如何高效完成App封装&#xff0c;并介绍如何…

【图书推荐】《图神经网络基础、模型与应用实战》

本书目的 详解PyTorch 图神经网络基础理论、模型与十多个应用案例&#xff0c;带领读者掌握图神经网络在自然语言处理、计算机视觉、推荐系统、社交网络4个领域的应用开发方法&#xff0c;丰富读者利用深度学习算法解决实际问题的能力。 本书案例 图卷积网络实现图注意力网络…

Python量化炒股的统计数据图

Python量化炒股的统计数据图 单只股票的收益统计图 查看单只股票的收盘价信息 单击聚宽JoinQuant量化炒股平台中的“策略研究/研究环境”命令&#xff0c;进入Jupyter Notebook的研究平台。然后单击“新建”按钮&#xff0c;创建Python3文件&#xff0c;输入如下代码如下&am…

知到java笔记(4.1--继承的用法以及this和super的用法)

格式&#xff1a; 例子&#xff1a; get set获取父类的私有变量 private属性 this和super区别&#xff1a; this用法 super用法 例子

星戈瑞CY7-COOH荧光探针,助力生物医学研究

CY7-COOH是一种近红外荧光染料&#xff0c;具有优异的光稳定性、高量子产率和强烈的荧光信号。此外&#xff0c;CY7-COOH还具有较长的激发和发射波长&#xff0c;使其在生物医学成像中具有较高的穿透力和较低的背景干扰。这使得CY7-COOH荧光探针在生物医学研究中具有诸多应用前…

弹性云服务器给用户带来了哪些便利

什么是弹性云服务器&#xff1f; 弹性云服务器&#xff08;ECS&#xff0c;Elastic Cloud Server&#xff09;简单地说&#xff0c;是指运行在云计算环境中的虚拟服务器。弹性云服务器可以说是虚拟专用服务器(VPS)&#xff0c;但VPS却不能说是云服务器。这是因为两者有着本质的…

南京观海微电子---电源,从微观角度观看电功率是怎么产生

从微观角度看看无功功率是怎么产生的&#xff0c;在此之前&#xff0c;我们得先知道引起无功功率的元器件是储能器件&#xff0c;主要是电感和电容。 首先&#xff0c;在宏观上&#xff0c;我们知道电感能导致电压超前电流90&#xff0c;可从如下公式推出&#xff1a; 由此可以…

场景文本检测识别学习 day09(Swin Transformer论文精读)

Patch & Window 在Swin Transformer中&#xff0c;不同层级的窗口内部的补丁数量是固定的&#xff0c;补丁内部的像素数量也是固定的&#xff0c;如上图的红色框就是不同的窗口&#xff08;Window&#xff09;&#xff0c;窗口内部的灰色框就是补丁&#xff08;Patch&#…

量子力学(入门通俗版,转述)

/仅作参考和学习&#xff0c;勿作他用/ 量子力学 量子力学无非就是物理理论。 物理理论就是对自然现象的归纳。------不太容易理解的自然现象。 我们面对的世界&#xff0c;宏观和微观之分。宏观和微观的分界线就是原子。 微观世界和宏观世界没有什么共同点。 牛顿力学用于宏…

14、深入探讨JVM中令人头痛的‘Stop the World’难题

14.1、前文回顾 上一篇文章通过一个实际案例,深入剖析了新生代的对象分配机制,以及对象如何被迁移到老年代。我们还探讨了一个会频繁触发Full GC的场景,并提供了针对性的优化策略,相信大家对JVM的核心运行原理已经理解得相当透彻。 在本文中,我们将讨论一个让Java工程师…

鸿蒙内核源码分析(互斥锁篇) | 互斥锁比自旋锁丰满多了

内核中哪些地方会用到互斥锁?看图: 图中是内核有关模块对互斥锁初始化,有文件,有内存,用消息队列等等,使用面非常的广.其实在给内核源码加注的过程中,会看到大量的自旋锁和互斥锁,它们的存在有序的保证了内核和应用程序的正常运行.是非常基础和重要的功能. 概述 自旋锁 和…

Revit模型移动设备加载优化

BIM/CAD 模型可能包含大量细节&#xff0c;在智能手机和移动 VR 设备上加载时需要特别注意。以下是保持Revit模型整洁的一些步骤&#xff0c;以便任何人都可以毫无问题地加载它们。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 -…

亏了亏了!双向孟德尔随机化阴性结果居然发了SCI二区(IF=6.7)

‍ 今天为诸位介绍的这篇文章是一项双向孟德尔随机化研究&#xff08;MR&#xff09;&#xff0c;惊讶的是&#xff0c;双向因果均为阴性结果发了SCI二区&#xff01;我们一起来看看&#xff01; 2024年4月17日&#xff0c;广东医科大学附属医院的学者做了一项双向两样本孟德尔…

绝地求生:PUBG杜卡迪联名进入倒计时3天!

大家好&#xff0c;我是闲游盒。 杜卡迪联名已经进入倒计时3天&#xff01;喜欢的朋友要注意结束时间可千万别错过&#xff01; 杜卡迪6色车辆 随着五一小长假的结束&#xff0c;本次混沌漫彩通行证也即将结束&#xff0c;本次通行证31级之后没升1级可额外领取1500BP和挑战者纪…

Spring与Mybatis-增删改查(注解方式与配置文件方式)

Spring框架下Mybaits的使用 准备数据库配置application.propertiespom.xml添加lombok依赖创建Emp实体类准备Mapper接口&#xff1a;EmpMapper预编译SQL根据id查询数据Mapper接口方法配置application.properties开启自动结果映射单元测试 条件模糊查询Mapper接口方法单元测试 根…

大模型时序预测初步调研20240506

AI预测相关目录 AI预测流程&#xff0c;包括ETL、算法策略、算法模型、模型评估、可视化等相关内容 最好有基础的python算法预测经验 EEMD策略及踩坑VMD-CNN-LSTM时序预测对双向LSTM等模型添加自注意力机制K折叠交叉验证optuna超参数优化框架多任务学习-模型融合策略Transform…