Practice in groovy

本文分享了作者在项目中使用Groovy语言的经验,包括引入依赖、变量声明、结构化判断、初始化、闭包使用及时间操作等内容。

2015年国庆过后,我所实习的公司DP与MT合并了,当时也是爆炸新闻,从相爱到相亲….着实让我们感动的内流满面。最主要的,在这个buffer空档期,原有的planning基本都做了delay,这也造就了公司Team间的技术大讨论,形成了难得的交流分享浪潮。

当时我们Team瞄准了新的语言groovy,它与java有异曲同工之处,而且对于java狗出身的我们来说,groovy学习曲线很快,能无缝的融合java代码。当时老大让我来试水重构了当时的一个项目模块,效果惊人,我记得当时LOC为600重构后不到200,这也让我屡试不爽。

下面主要分享下groovy的使用心得:

  1. Groovy引入:由于我们的项目使用pom依赖,因此使用groovy只需要引入相关的依赖即可。
   //groovy-all为groovy依赖组件名,spock-core是测试使用到到依赖组件名
      <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-core</artifactId>
            <version>0.7-groovy-2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.codehaus.groovy</groupId>
                    <artifactId>groovy-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <plugin>
                <groupId>org.codehaus.gmavenplus</groupId>
                <artifactId>gmavenplus-plugin</artifactId>
                <version>1.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

2.声明变量
可使用def关键字声明任何类型
不需要加分号;
默认生成setter和getter方法;
groovy 中变量不存在简单类型,万事万物皆对象,即使使用int声明也会转化成Integer

    Integer selectedDealGroupId
    Integer selectedDealId
    DealGroupInfoBean selectedDealGroupInfo
    String selectedBeginDate = ""

3.结构化方式判断是否为空

    if (isFirstDay || !hasOverLapDate)

4.初始化

    //相当于调用set方法
    new AClass(attribute1:value1,attribute2:value2 ...);
   //访问成员变量可以直接使用.,相当于调用get方法
    a.attribute

    //集合的初始化方式
    def List a = []
    def map  = [:]
    a << 1

5.闭包的使用(groovy的强大特性)

    //类似于for...each...,{}是具体的处理逻辑        
    list.each {
        dealIds << it.dealId

    //集合的排序
    finalDealSaleDTOList.sort { e1, e2 ->
            return Date.parse("yyyy-MM-dd", e1.calDate) -                   Date.parse("yyyy-MM-dd", e2.calDate)
        } 

    //生成新的集合
    def dates = item.collect {
            Date.parse("yyyy-MM-dd", string)
        }

6.时间的使用

    //Date类型的时间集合声明,..指定时间段,-可作相减,还可进行大小的比较
    def allDate = (beginDate..endDate)
    def differDate = allDate - saleDates
    date-1 //日期减一天

    //Date和String之间的转换
    string = date.format("yyyy-MM-dd")
    Date.parse("yyyy-MM-dd", string)

    //补注:java 中Date和String的转换
    DateFormat fmt =new SimpleDateFormat("yyyy-MM-dd");
    Date date = fmt.parse(string);
    string = fmt.format(date);

以上只是本人使用groovy的简单总结,当然还有很多特性需要我们更多的去实践。

翻译: Of course. After reviewing your Gradle files, it's clear the main problem is not a single error but a mix of structural issues, outdated practices, and critical typos. Your build script merges settings that should be in separate files and uses fragile methods that will cause constant problems. Here is a breakdown of the issues and a step-by-step guide to fix them. ----- ### \#\# 1. Critical Errors That Will Break the Build These issues need to be fixed first, as they will prevent Gradle from running correctly. #### **A. Typo in `targetSdk`** In your module's `build.gradle`, you have a typo. * **Problem:** `targetsdk 34` * **Fix:** It should be `targetSdk` (with a capital 'S'). <!-- end list --> ```groovy // In your module's build.gradle defaultConfig { minSdk 29 targetSdk 34 // FIX: Was 'targetsdk' } ``` #### **B. Typo in JaCoCo Configuration** In your `createOfflineTestCoverageReport` task, you have a typo in the word "configurations". * **Problem:** `classpath: configur3+ations.jacocoAnt.asPath` * **Fix:** It should be `configurations.jacocoAnt.asPath`. <!-- end list --> ```groovy // In your JaCoCo task ant.taskdef(name: 'report', classname: 'org.jacoco.ant.ReportTask', classpath: configurations.jacocoAnt.asPath) // FIX: Was 'configur3+ations' ``` ----- ### \#\# 2. Major Structural Problems Your project's build logic is incorrectly structured. You've mixed settings for the root project and the `model_base` module into one file. #### **A. Root Project and Module Logic are Mixed** You should have at least two `build.gradle` files: one in the project's root directory and one inside your `model_base` module. * **Project Root `build.gradle` or `build.gradle.kts`:** Should contain `buildscript` settings (like the Android Gradle Plugin version) and `allprojects` repository settings. It should **not** contain `apply plugin: 'com.android.library'` or Android-specific settings like `compileSdk`. * **Module `app/build.gradle` or `library/build.gradle.kts`:** Should contain `plugins { id 'com.android.library' }`, the `android { ... }` block, and the `dependencies { ... }` for that specific module. #### **B. Action Plan: Restructure Your Files** **Step 1: Create a `settings.gradle` file in your project root** (if you don't have one). It tells Gradle which modules are part of your project. ```groovy // settings.gradle include ':model_base' // include ':app' // if you have an app module ``` **Step 2: Clean up your Root `build.gradle` file.** It should only contain build-wide information. ```groovy // Project Root: build.gradle // The buildscript block is an older pattern, but if you keep it: buildscript { repositories { google() mavenCentral() } dependencies { // Use a consistent version for AGP classpath 'com.android.tools.build:gradle:8.1.0' // You don't need JaCoCo here; the plugin handles it. } } allprojects { repositories { google() mavenCentral() maven { url "https://jitpack.io" } } } ``` **Step 3: Create/Fix your Module `model_base/build.gradle` file.** This is where most of your current code should go. ```groovy // Module: model_base/build.gradle // Use the plugins block at the top plugins { id 'com.android.library' id 'jacoco' // Apply jacoco plugin here } android { namespace 'com.st.model_base' compileSdk 34 defaultConfig { minSdk 29 targetSdk 34 // Corrected typo } // ... your other android settings } dependencies { // ... your dependencies implementation 'androidx.appcompat:appcompat:1.6.1' // Updated version } // ... your JaCoCo tasks and configuration ``` ----- ### \#\# 3. Bad Practices and Recommendations These are issues that make your build slow, fragile, and hard to maintain. #### **A. Manual `bootstrapClasspath` Manipulation is Very Bad** Your use of `options.compilerArgs << '-Xbootclasspath/p:...'` and manually rebuilding the `bootstrapClasspath` to include `framework.jar` is an extremely fragile and outdated practice. * **Why it's bad:** It overrides the standard Android compile environment. It can break with any update to AGP, Gradle, or Java. It makes your build completely dependent on a local JAR file. * **Recommendation:** **Remove all three `gradle.projectsEvaluated` blocks.** Rely on the `compileSdk 34` to provide the Android APIs. If you absolutely need APIs not available in the public SDK, this approach is still not recommended and indicates a potential design flaw. #### **B. `dependsOn(clean)` is an Anti-Pattern** Your line `createOfflineTestCoverageReport.dependsOn(clean)` is very inefficient. * **Why it's bad:** It forces a complete project clean and recompile **every time** you want to generate a report. This will make your build process incredibly slow. * **Recommendation:** **Delete this line.** Gradle's tasks are designed to be incremental. #### **C. Redundant Configurations** You have multiple `tasks.withType(Test)` and `gradle.projectsEvaluated` blocks. This makes the script confusing. * **Recommendation:** Combine them into single blocks to avoid conflicts and make the logic clear. #### **D. Outdated Dependencies and Repositories** * **JCenter is Deprecated:** Remove `jcenter()` from your repositories. It was shut down years ago and can slow down your build as Gradle tries to connect to it. `mavenCentral()` has replaced it. * **Old AppCompat Library:** `androidx.appcompat:appcompat:1.4.1` is old for `compileSdk 34`. Update it to a more recent version like `1.6.1` to avoid potential incompatibilities.
07-24
build.gradle:plugins { id 'eclipse' id 'idea' id 'maven-publish' id 'net.minecraftforge.gradle' version '[6.0.36,6.2)' } version = mod_version group = mod_group_id base { archivesName = mod_id } // Mojang ships Java 21 to end users in 1.20.5+, so your mod should target Java 21. java.toolchain.languageVersion = JavaLanguageVersion.of(21) println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}" minecraft { // The mappings can be changed at any time and must be in the following format. // Channel: Version: // official MCVersion Official field/method names from Mojang mapping files // parchment YYYY.MM.DD-MCVersion Open community-sourced parameter names and javadocs layered on top of official // // Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge // Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started // // Simply re-run your setup task after changing the mappings to update your workspace. mappings channel: mapping_channel, version: mapping_version // Forge 1.20.6 and newer use official mappings at runtime, so we shouldn't reobf from official to SRG reobf = false // When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game. // In most cases, it is not necessary to enable. // enableEclipsePrepareRuns = true // enableIdeaPrepareRuns = true // This property allows configuring Gradle's ProcessResources task(s) to run on IDE output locations before launching the game. // It is REQUIRED to be set to true for this template to function. // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html copyIdeResources = true // When true, this property will add the folder name of all declared run configurations to generated IDE run configurations. // The folder name can be set on a run configuration using the "folderName" property. // By default, the folder name of a run configuration is the name of the Gradle project containing it. // generateRunFolders = true // This property enables access transformers for use in development, applied to the Minecraft artifact. // The access transformer file can be anywhere in the project. // However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge. // This default location is a best practice to automatically put the file in the right place in the final jar. // See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information. // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') // Default run configurations. // These can be tweaked, removed, or duplicated as needed. runs { // applies to all the run configs below configureEach { workingDirectory project.file('run') // Optional additional logging. The markers can be added/remove as needed, separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. // property 'forge.logging.markers', 'REGISTRIES' property 'forge.logging.console.level', 'debug' // Recommended for development - enables more descriptive errors at the cost of slower startup and registration. property 'eventbus.api.strictRuntimeChecks', 'true' // arg "-mixin.config=${mod_id}.mixins.json" } client { // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. property 'forge.enabledGameTestNamespaces', mod_id } server { property 'forge.enabledGameTestNamespaces', mod_id args '--nogui' } // This run config launches GameTestServer and runs all registered gametests, then exits. // By default, the server will crash when no gametests are provided. // The gametest system is also enabled by default for other run configs under the /test command. gameTestServer { property 'forge.enabledGameTestNamespaces', mod_id } data { // example of overriding the workingDirectory set in configureEach above workingDirectory project.file('run-data') // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') } } } // Include resources generated by data generators. sourceSets.main.resources { srcDir 'src/generated/resources' } repositories { // Put repositories for dependencies here mavenCentral() maven { name = 'Forge' url = 'https://maven.minecraftforge.net' } maven { name = 'Minecraft libraries' url = 'https://libraries.minecraft.net' } exclusiveContent { forRepository { maven { name = 'Sponge' url = 'https://repo.spongepowered.org/repository/maven-public' } } filter { includeGroupAndSubgroups('org.spongepowered') } } // If you have mod jar dependencies in ./libs, you can declare them as a repository like so. // See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver // flatDir { // dir 'libs' // } } dependencies { // Specify the version of Minecraft to use. // Any artifact can be supplied so long as it has a "userdev" classifier artifact and is a compatible patcher artifact. // The "userdev" classifier will be requested and setup by ForgeGradle. // If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"], // then special handling is done to allow a setup of a vanilla dependency without the use of an external repository. minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" // Forge 1.21.6+ uses EventBus 7, which shifts most of its runtime validation to compile-time via an annotation processor // to improve performance in production environments. This line is required to enable said compile-time validation // in your development environment, helping you catch issues early. annotationProcessor 'net.minecraftforge:eventbus-validator:7.0-beta.10' // Example mod dependency with JEI // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" // compileOnly "mezz.jei:jei-${mc_version}-forge-api:${jei_version}" // runtimeOnly "mezz.jei:jei-${mc_version}-forge:${jei_version}" // Example mod dependency using a mod jar from ./libs with a flat dir repository // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar // The group id is ignored when searching -- in this case, it is "blank" // implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}") // For more info: // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html // http://www.gradle.org/docs/current/userguide/dependency_management.html } // This block of code expands all declared replace properties in the specified resource targets. // A missing property will result in an error. Properties are expanded using ${} Groovy notation. // When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html tasks.named('processResources', ProcessResources) { var replaceProperties = [ minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range, forge_version: forge_version, forge_version_range: forge_version_range, loader_version_range: loader_version_range, mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, mod_authors: mod_authors, mod_description: mod_description, ] inputs.properties replaceProperties filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) { expand replaceProperties + [project: project] } } // Example for how to get properties into the manifest for reading at runtime. tasks.named('jar', Jar) { manifest { attributes([ 'Specification-Title' : mod_id, 'Specification-Vendor' : mod_authors, 'Specification-Version' : '1', // We are version 1 of ourselves 'Implementation-Title' : project.name, 'Implementation-Version' : project.jar.archiveVersion, 'Implementation-Vendor' : mod_authors ]) // attributes['MixinConfigs'] = "${mod_id}.mixins.json" } } // Example configuration to allow publishing using the maven-publish plugin publishing { publications { register('mavenJava', MavenPublication) { artifact jar } } repositories { maven { url ="file://${project.projectDir}/mcmodsrepo" } } } tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation } // IntelliJ no longer downloads javadocs and sources by default, this tells Gradle to force IntelliJ to do it. idea.module { downloadJavadoc = downloadSources = true } eclipse { // Run everytime eclipse builds the code //autoBuildTasks genEclipseRuns // Run when importing the project synchronizationTasks 'genEclipseRuns' } // Merge the resources and classes into the same directory, because Java expects modules to be in a single directory. // And if we have it in multiple we have to do performance intensive hacks like having the UnionFileSystem // This will eventually be migrated to ForgeGradle so modders don't need to manually do it. But that is later. sourceSets.each { def dir = layout.buildDirectory.dir("sourcesSets/$it.name") it.output.resourcesDir = dir it.java.destinationDirectory = dir }
最新发布
08-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值