本节介绍Gradle提供的直接影响依赖关系解析引擎行为的机制. 与本章中涉及的其他概念(例如依赖项约束组件元数据规则)不同 ,它们都是解析的输入 ,以下机制使您可以编写直接注入解析引擎的规则. 因此,它们可以看作是蛮力解决方案,可能会隐藏未来的问题(例如,如果添加了新的依赖项). 因此,一般建议仅在其他手段不足的情况下使用以下机制. 如果您正在编写 ,则应始终首选依赖项约束,因为它们是为您的使用者发布的.

Using dependency resolve rules

为每个已解析的依赖项执行一个依赖项解析规则,并提供了一个功能强大的api,用于在解析依赖项之前处理请求的依赖项. 该功能当前提供了更改请求的依赖项的组,名称和/或版本的功能,从而允许在解析过程中将依赖项替换为完全不同的模块.

依赖关系解析规则提供了一种非常强大的方式来控制依赖关系解析过程,并且可用于实现依赖关系管理中的各种高级模式. 下面概述了其中一些模式. 有关更多信息和代码示例,请参阅API文档中的ResolutionStrategy类.

Implementing a custom versioning scheme

在某些公司环境中,可以在Gradle构建中声明的模块版本列表由外部维护和审核. 依赖性解析规则为这种模式提供了一种简洁的实现方式:

  • 在构建脚本中,开发人员使用模块组和名称声明依赖关系,但使用占位符版本,例如: default .

  • 通过依赖关系解析规则将default版本解析为特定版本,该规则在已批准模块的公司目录中查找该版本.

可以将规则实施完整地封装在公司插件中,并在组织内的所有内部版本之间共享.

示例1.使用自定义版本控制方案
build.gradle
configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.version == 'default') {
            def version = findDefaultVersionInCatalog(details.requested.group, details.requested.name)
            details.useVersion version.version
            details.because version.because
        }
    }
}

def findDefaultVersionInCatalog(String group, String name) {
    //some custom logic that resolves the default version into a specific version
    [version: "1.0", because: 'tested by QA']
}
build.gradle.kts
configurations.all {
    resolutionStrategy.eachDependency {
        if (requested.version == "default") {
            val version = findDefaultVersionInCatalog(requested.group, requested.name)
            useVersion(version.version)
            because(version.because)
        }
    }
}

data class DefaultVersion(val version: String, val because: String)

fun findDefaultVersionInCatalog(group: String, name: String): DefaultVersion {
    //some custom logic that resolves the default version into a specific version
    return DefaultVersion(version = "1.0", because = "tested by QA")
}

Denying a particular version with a replacement

依赖性解析规则提供了一种机制,用于拒绝依赖性的特定版本并提供替代版本. 如果某个依赖项版本已损坏并且不应使用,当依赖项解析规则导致该版本被已知的良好版本替换时,这将很有用. 损坏的模块的一个示例是声明对某个库的依赖关系,该依赖关系在任何公共存储库中都找不到,但是还有许多其他原因导致不需要特定的模块版本,而首选其他版本.

在下面的示例中,想象1.2.1版包含重要的修复程序,应该始终优先于1.2 . 提供的规则将强制执行此操作:每当遇到1.2版时,它将被1.2.1替换. 请注意,这与如上所述的强制版本不同,因为此模块的任何其他版本都不会受到影响. 这意味着,"最新的"冲突解决策略将仍然选择1.3版, 1.3是该版本也被以可传递方式拉出.

例子2.例子:将一个带有替换版本的版本列入黑名单
build.gradle
configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.group == 'org.software' && details.requested.name == 'some-library' && details.requested.version == '1.2') {
            details.useVersion '1.2.1'
            details.because 'fixes critical bug in 1.2'
        }
    }
}
build.gradle.kts
configurations.all {
    resolutionStrategy.eachDependency {
        if (requested.group == "org.software" && requested.name == "some-library" && requested.version == "1.2") {
            useVersion("1.2.1")
            because("fixes critical bug in 1.2")
        }
    }
}

使用具有丰富版本约束reject指令有一个区别:如果在图中找到被拒绝的版本,则丰富的版本将导致构建失败,或者在使用动态依赖项时选择非被拒绝的版本. 在这里,我们处理请求的版本 ,以便在找到被拒绝的版本时选择其他版本. 换句话说,这是针对拒绝版本的解决方案 ,而丰富版本限制允许声明意图 (您不应使用此版本).

Using module replacement rules

功能冲突而言,最好表达模块冲突 . 但是,如果没有声明这样的规则,或者您正在使用不支持功能的Gradle版本,则Gradle提供了解决这些问题的工具.

模块替换规则允许构建声明旧库已被新库替换. 新库取代旧库的一个很好的例子是google-collections > guava migration. 创建Google集合的团队决定将模块名称从com.google.collections:google-collections更改为com.google.guava:guava . 这在业界是一种合法的情况:团队需要能够更改其维护的产品名称,包括模块坐标. 重命名模块坐标会影响冲突解决.

为了解释对解决冲突的影响,让我们考虑google-collections > guava场景. 这两个库都可能被拉到同一个依赖图中. 例如, 我们的项目依赖于guava我们的某些依赖项引入了旧版google-collections . 这可能会导致运行时错误,例如在测试或应用程序执行期间. Gradle不会自动解决google-collections > guava冲突,因为它不被视为版本冲突 . 这是因为两个库的模块坐标完全不同,并且当groupmodule坐标相同时会激活冲突解决方案,但是在依赖关系图中有不同的版本可用(有关更多信息,请参阅冲突解决方案一节). 解决此问题的传统方法是:

  • 声明排除规则,以避免将google-collections拉到图表中. 这可能是最流行的方法.

  • 避免引入旧式库的依赖项.

  • 如果新版本不再引入旧版库,请升级依赖版本.

  • 降级为google-collections . 不建议这样做,只是为了完整起见.

传统方法行得通,但是它们不够笼统. 例如,一个组织要解决所有项目中的google-collections > guava冲突解决问题. 可以声明某些模块已被其他模块替代. 这使组织能够将有关模块更换的信息包括在公司插件套件中,并全面解决企业中所有由Gradle支持的项目的问题.

示例3.声明模块更换
build.gradle
dependencies {
    modules {
        module("com.google.collections:google-collections") {
            replacedBy("com.google.guava:guava", "google-collections is now part of Guava")
        }
    }
}
build.gradle.kts
dependencies {
    modules {
        module("com.google.collections:google-collections") {
            replacedBy("com.google.guava:guava", "google-collections is now part of Guava")
        }
    }
}

有关更多示例和详细API,请参阅DSL参考ComponentMetadataHandler .

当我们声明用guava代替google-collections时会发生什么? Gradle可以使用此信息来解决冲突. Gradle会考虑比任何版本的google-collections更新/更好的每个版本的guava . 另外,Gradle将确保在类路径/已解析文件列表中仅存在guava jar. 请注意,如果依赖关系图中仅显示google-collections (例如,没有guava ),则Gradle不会急切地将其替换为guava . 模块更换是Gradle用于解决冲突的信息. 如果没有冲突(例如,图中只有google-collections或只有guava ),则不使用替换信息.

当前,不可能声明给定的模块被一组模块替换. 但是,可以声明多个模块被单个模块替换.

Using dependency substitution rules

依赖关系替换规则的工作方式与依赖关系解决规则相似. 实际上,可以使用依赖关系替换规则来实现依赖关系解析规则的许多功能. 它们允许将项目和模块依赖项透明地替换为指定的替换项. 与依赖关系解析规则不同,依赖关系替换规则允许项目和模块依赖关系可以互换替换.

向配置中添加依赖项替换规则会更改解析该配置的时间. 在构造任务图时,无需解析首次使用的配置,而是解析配置. 如果在任务执行期间对配置进行了进一步修改,或者配置依赖于在执行另一任务期间发布的模块,则可能会产生意想不到的后果.

解释:

  • 可以将Configuration声明为任何任务的输入,并且在解决配置时,该配置可以包括项目依赖项.

  • 如果项目依赖项是任务的输入(通过配置),则必须将用于构建项目工件的任务添加到任务依赖项中.

  • 为了确定作为任务输入的项目依赖关系,Gradle需要解析Configuration输入.

  • 由于Gradle任务图在任务执行开始后便是固定的,因此Gradle需要在执行任何任务之前执行此解决方案.

在没有依赖替换规则的情况下,Gradle知道外部模块依赖永远不会传递引用项目依赖. 这样就可以通过简单的图形遍历轻松确定配置的完整项目依赖项集合. 使用此功能,Gradle不能再进行此假设,并且必须执行完全解析才能确定项目依赖项.

Substituting an external module dependency with a project dependency

一种替代依赖的用例是使用模块的本地开发版本代替从外部资源库下载的模块. 这对于测试依赖项的本地修补版本可能很有用.

可以在指定版本或不指定版本的情况下声明要替换的模块.

例子4.用一个项目代替一个模块
build.gradle
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute module("org.utils:api") using project(":api") because "we work with the unreleased development version"
        substitute module("org.utils:util:2.5") using project(":util")
    }
}
build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(module("org.utils:api"))
            .using(project(":api")).because("we work with the unreleased development version")
        substitute(module("org.utils:util:2.5")).using(project(":util"))
    }
}

请注意,替代项目必须包含在多项目构建中(通过settings.gradle ). 依赖关系替换规则负责将模块依赖关系替换为项目依赖关系,并连接所有任务依赖关系,但不将项目隐式包含在构建中.

Substituting a project dependency with a module replacement

使用替换规则的另一种方法是用多项目构建中的模块替换项目依赖项. 通过允许从存储库中下载而不是构建项目依赖项的子集,这对于加快大型多项目构建的开发速度可能非常有用.

必须使用指定的版本声明要用作替换模块的模块.

例子5.用一个模块代替一个项目
build.gradle
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute project(":api") using module("org.utils:api:1.3") because "we use a stable version of org.utils:api"
    }
}
build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(project(":api"))
            .using(module("org.utils:api:1.3")).because("we use a stable version of org.utils:api")
    }
}

当项目依赖关系已替换为模块依赖关系时,该项目仍将包含在整个多项目构建中. 但是,为了解决依赖的Configuration将不会执行构建替换后的依赖项的任务.

Conditionally substituting a dependency

依赖项替换的一个常见用例是允许在多项目构建中更灵活地组装子项目. 这对于开发外部依赖的本地修补版本或在大型多项目构建中构建模块的子集很有用.

以下示例使用依赖项替换规则将任何模块依赖项替换为org.example组,但org.example是必须找到与依赖项名称匹配的本地项目.

例子6.有条件地替换一个依赖
build.gradle
    configurations.all {
        resolutionStrategy.dependencySubstitution.all { DependencySubstitution dependency ->
            if (dependency.requested instanceof ModuleComponentSelector && dependency.requested.group == "org.example") {
                def targetProject = findProject(":${dependency.requested.module}")
                if (targetProject != null) {
                    dependency.useTarget targetProject
                }
            }
        }
    }
build.gradle.kts
    configurations.all {
        resolutionStrategy.dependencySubstitution.all {
            requested.let {
                if (it is ModuleComponentSelector && it.group == "org.example") {
                    val targetProject = findProject(":${it.module}")
                    if (targetProject != null) {
                        useTarget(targetProject)
                    }
                }
            }
        }
    }

Note that a project that is substituted must be included in the multi-project build (via settings.gradle). Dependency substitution rules take care of replacing the module dependency with the project dependency, but do not implicitly include the project in the build.

Substituting a dependency with another variant

Gradle的依赖性管理引擎具有变体感知功能,这意味着对于单个组件,该引擎可以选择不同的工件和传递性依赖性.

选择什么取决于使用者配置的属性和在生产者端找到的变体的属性. 但是,某些特定的依赖关系可能会覆盖配置本身的属性. 使用Java Platform插件时通常是这种情况:该插件构建一种特殊的组件,称为"平台",可以通过将组件类别属性设置为platform来解决,这与作为目标库的典型依赖项相反.

因此,您可能会遇到想要用常规依赖项替换平台依赖项或其他方式的情况.

Substituting a dependency with attributes

假设您要用常规依赖项替换平台依赖项. 这意味着您正在使用的库声明如下:

例子7.对平台的不正确依赖
lib/build.gradle
dependencies {
    // This is a platform dependency but you want the library
    implementation platform('com.google.guava:guava:28.2-jre')
}
lib/build.gradle.kts
dependencies {
    // This is a platform dependency but you want the library
    implementation(platform("com.google.guava:guava:28.2-jre"))
}

platform关键字实际上是带有attribute依赖项的简写形式. 如果要用常规依赖关系替换此依赖关系,则需要精确选择具有platform属性的依赖关系.

这可以通过使用替换规则来完成:

例子8.用常规依赖替换平台依赖
build.gradle
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(platform(module('com.google.guava:guava:28.2-jre'))).
            using module('com.google.guava:guava:28.2-jre')
    }
}
build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(platform(module("com.google.guava:guava:28.2-jre")))
            .using(module("com.google.guava:guava:28.2-jre"))
    }
}

同一条没有 platform关键字的规则会尝试用常规依赖项替换常规依赖项,这不是您想要的,因此,重要的是要了解替代规则适用于依赖项规范 :它将匹配的请求依赖项( substitute XXX )与替代品( using YYY ).

您可以在请求的依赖项替代项上都具有属性,并且替代项不仅限于platform :实际上,您可以使用variant符号来指定整个依赖项属性集. 以下规则与上述规则严格等效

例子9.使用变量符号用常规依赖替换平台依赖
build.gradle
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute variant(module('com.google.guava:guava:28.2-jre')) {
            attributes {
                attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.REGULAR_PLATFORM))
            }
        } using module('com.google.guava:guava:28.2-jre')
    }
}
build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(variant(module("com.google.guava:guava:28.2-jre")) {
            attributes {
                attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.REGULAR_PLATFORM))
            }
        }).using(module("com.google.guava:guava:28.2-jre"))
    }
}

请参阅替代DSL API文档 ,以获取变体替代API的完整参考.

复合构建中 ,将不应用您必须匹配所请求的确切依赖属性的规则:使用复合时,Gradle将自动匹配所请求的属性. 换句话说,如果您包含另一个构建,则意味着您要用包含的构建中的等效变体替换替换模块的所有变体 .

Substituting a dependency with a dependency with capabilities

属性替换类似,Gradle允许您将具有或不具有功能的依赖项替换为具有或不具有功能的另一个依赖项.

例如,假设您需要用其测试治具替代常规依赖项. 您可以使用以下依赖替换规则来实现:

例子10.用测试夹具代替一个依赖
build.gradle
configurations.testCompileClasspath {
    resolutionStrategy.dependencySubstitution {
        substitute(module('com.acme:lib:1.0'))
            .using variant(module('com.acme:lib:1.0')) {
            capabilities {
                requireCapability('com.acme:lib-test-fixtures')
            }
        }
    }
}
build.gradle.kts
configurations.testCompileClasspath {
    resolutionStrategy.dependencySubstitution {
        substitute(module("com.acme:lib:1.0")).using(variant(module("com.acme:lib:1.0")) {
            capabilities {
                requireCapability("com.acme:lib-test-fixtures")
            }
        })
    }
}

其在所请求的相关性的变换规则声明功能构成的依赖性匹配规范的一部分,因此不需要能力依赖将不匹配.

请参考Substitution DSL API文档 ,以获取变体替代API的完整参考.

组合构建中 ,不应用您必须匹配确切的请求依赖功能的规则:使用组合时,Gradle将自动匹配请求的功能. 换句话说,如果您包含另一个构建,则意味着要用包含的构建中的等效变体替换替换模块的所有variants_ .

Substituting a dependency with a classifier or artifact

虽然通常通过组/工件/版本坐标来​​访问外部模块,但通常会发布此类模块并附带其他工件,您可能希望使用这些附加工件来代替主工件. 对于分类的工件,通常是这种情况,但是您可能还需要选择具有不同文件类型或扩展名的工件. Gradle不鼓励在依赖项中使用分类器,并且更喜欢将工件作为变量进行建模,例如模块的其他变体 . 使用变体代替分类的工件有很多优点,包括但不仅限于这些工件的一组不同的依赖关系.

但是,为了帮助桥接两个模型,Gradle提供了在替换规则中更改或删除分类器的方法.

例子11.依赖关系会导致解析错误
build.gradle
dependencies {
    implementation 'com.google.guava:guava:28.2-jre'
    implementation 'co.paralleluniverse:quasar-core:0.8.0'
    implementation project(':lib')
}
build.gradle.kts
dependencies {
    implementation("com.google.guava:guava:28.2-jre")
    implementation("co.paralleluniverse:quasar-core:0.8.0")
    implementation(project(":lib"))
}

在上面的示例中,对quasar的第一级依赖使我们认为Gradle将解析quasar-core-0.8.0.jar但事实并非如此:构建将因以下消息而失败:

Execution failed for task ':resolve'.
> Could not resolve all files for configuration ':runtimeClasspath'.
   > Could not find quasar-core-0.8.0-jdk8.jar (co.paralleluniverse:quasar-core:0.8.0).
     Searched in the following locations:
         https://jcenter.bintray.com/co/paralleluniverse/quasar-core/0.8.0/quasar-core-0.8.0-jdk8.jar

那是因为存在对另一个项目lib的依赖,它本身依赖于quasar-core的不同版本:

例子12.一个"分类的"依赖
lib/build.gradle
dependencies {
    implementation "co.paralleluniverse:quasar-core:0.7.12_r3:jdk8"
}
lib/build.gradle.kts
dependencies {
    implementation("co.paralleluniverse:quasar-core:0.7.12_r3:jdk8")
}

What happens is that Gradle would perform conflict resolution between quasar-core 0.8.0 and quasar-core 0.7.12_r3. Because 0.8.0 is higher, we select this version, but the dependency in lib has a classifier, jdk8 and this classifier 不再存在 in release 0.8.0.

要解决此问题,您可以要求Gradle在没有分类器的情况下解决这两个依赖项:

例子13.禁用分类器选择的解析规则
build.gradle
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute module('co.paralleluniverse:quasar-core') using module('co.paralleluniverse:quasar-core:0.8.0') withoutClassifier()
    }
}
build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(module("co.paralleluniverse:quasar-core"))
            .using(module("co.paralleluniverse:quasar-core:0.8.0"))
            .withoutClassifier()
    }
}

该规则有效地用没有分类器的依赖关系替换了在图中找到的对quasar-core的依赖关系.

另外,也可以选择具有特定分类器的依赖项或者,对于更特定的用例,可以选择非常特定的工件(类型,扩展名和分类器)代替.

有关更多信息,请参考以下API文档:

Disabling transitive resolution

默认情况下,Gradle解析依赖项元数据指定的所有传递依赖项. 有时,例如,如果元数据不正确或定义了较大的传递依赖关系图,则此行为可能是不希望的. 您可以通过将ModuleDependency.setTransitive(boolean)设置为false来告诉Gradle为依赖项禁用传递依赖项管理. 结果,仅主工件将针对声明的依赖项进行解析.

例子14.为声明的依赖项禁用传递依赖项解析
build.gradle
dependencies {
    implementation('com.google.guava:guava:23.0') {
        transitive = false
    }
}
build.gradle.kts
dependencies {
    implementation("com.google.guava:guava:23.0") {
        isTransitive = false
    }
}

禁用传递依赖项解析可能需要您在构建脚本中声明必要的运行时依赖项,否则将自动解决. 不这样做可能会导致运行时类路径问题.

项目可以决定完全禁用传递依赖项解析. 您或者不想依赖发布到使用的存储库的元数据,或者想要完全控制图形中的依赖项. 有关更多信息,请参见Configuration.setTransitive(boolean) .

例子15.在配置级别禁用传递依赖项解析
build.gradle
configurations.all {
    transitive = false
}

dependencies {
    implementation 'com.google.guava:guava:23.0'
}
build.gradle.kts
configurations.all {
    isTransitive = false
}

dependencies {
    implementation("com.google.guava:guava:23.0")
}

Changing configuration dependencies prior to resolution

有时,插件可能要在解决配置之前修改其依赖关系. withDependencies方法允许以编程方式添加,删除或修改依赖项.

例子16.修改对配置的依赖
build.gradle
configurations {
    implementation {
        withDependencies { DependencySet dependencies ->
            ExternalModuleDependency dep = dependencies.find { it.name == 'to-modify' } as ExternalModuleDependency
            dep.version {
                strictly "1.2"
            }
        }
    }
}
build.gradle.kts
configurations {
    create("implementation") {
        withDependencies {
            val dep = this.find { it.name == "to-modify" } as ExternalModuleDependency
            dep.version {
                strictly("1.2")
            }
        }
    }
}

Setting default configuration dependencies

如果未为配置显式设置依赖项,则可以使用默认依赖项来配置配置. 此功能的主要用例是开发使用用户可能会覆盖的版本控制工具的插件. 通过指定默认依赖关系,只有在用户未指定要使用的特定版本时,插件才能使用该工具的默认版本.

示例17.指定配置的默认依赖项
build.gradle
configurations {
    pluginTool {
        defaultDependencies { dependencies ->
            dependencies.add(project.dependencies.create("org.gradle:my-util:1.0"))
        }
    }
}
build.gradle.kts
configurations {
    create("pluginTool") {
        defaultDependencies {
            add(project.dependencies.create("org.gradle:my-util:1.0"))
        }
    }
}

Excluding a dependency from a configuration completely

在依赖项声明中排除依赖项类似,您可以使用Configuration.exclude(java.util.Map)完全排除特定配置的传递性依赖项. 对于配置上声明的所有依赖关系,这将自动排除传递依赖关系.

例子18.排除特定配置的传递依赖
build.gradle
configurations {
    implementation {
        exclude group: 'commons-collections', module: 'commons-collections'
    }
}

dependencies {
    implementation 'commons-beanutils:commons-beanutils:1.9.4'
    implementation 'com.opencsv:opencsv:4.6'
}
build.gradle.kts
configurations {
    "implementation" {
        exclude(group = "commons-collections", module = "commons-collections")
    }
}

dependencies {
    implementation("commons-beanutils:commons-beanutils:1.9.4")
    implementation("com.opencsv:opencsv:4.6")
}

Matching dependencies to repositories

Gradle公开了一个API,以声明存储库可能包含或不包含的内容. 此功能提供了对哪个存储库提供哪些工件的精细控制,这可以是控制依赖项来源的一种方法.

请转至有关存储库内容过滤的部分,以了解有关此功能的更多信息.

Enabling Ivy dynamic resolve mode

Gradle的Ivy存储库实现支持等同于Ivy的动态解析模式. 通常,Gradle将对ivy.xml文件中包含的每个依赖项定义使用rev属性. 在动态解析模式下,对于给定的依赖项定义,Gradle会偏爱revConstraint属性而不是rev属性. 如果revConstraint属性不存在,则使用rev属性.

要启用动态解析模式,您需要在存储库定义中设置适当的选项. 下面显示了两个示例. 请注意,动态解析模式仅适用于Gradle的Ivy存储库. 它不适用于Maven存储库或自定义Ivy DependencyResolver实现.

例子19.启用动态解析模式
build.gradle
// Can enable dynamic resolve mode when you define the repository
repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
        resolve.dynamicMode = true
    }
}

// Can use a rule instead to enable (or disable) dynamic resolve mode for all repositories
repositories.withType(IvyArtifactRepository) {
    resolve.dynamicMode = true
}
build.gradle.kts
// Can enable dynamic resolve mode when you define the repository
repositories {
    ivy {
        url = uri("http://repo.mycompany.com/repo")
        resolve.isDynamicMode = true
    }
}

// Can use a rule instead to enable (or disable) dynamic resolve mode for all repositories
repositories.withType<IvyArtifactRepository> {
    resolve.isDynamicMode = true
}