NamedDomainObjectContainer 的使用

2019-02-20  本文已影响0人  fan_xing

前提:对gradle的DSL对象创建有一定的了解

引言

gradle默认情况下对只包含原生数据类型的对象可以自动进行DSL匹配,如string、int、List等,通过project.extensions.create即可在extensions中创建该对象。当集合中(如List)包含自定义对象时,gradle提供了NamedDomainObjectContainer 来实现对象集合的DSL映射。

为了配合NamedDomainObjectContainer 的使用,

  1. 对象必须提供一个含有String的构造方法和一个name属性去唯一标识实例对象。
  2. 通过project.container去创建NamedDomainObjectContainer,
  3. 并由project.extensions.add来添加该NamedDomainObjectContainer实例。

快速上手

// File: build.gradle
apply plugin: ProductsPlugin
 
// DSL to define new objects of type Product.
products {
    // Create Product with name pencil.
    pencil {
        price = 0.05
    }
    // Create Product with name crayon.
    crayon {
        price = 0.18
    }
}
 
 
class ProductsPlugin implements Plugin<Project> {
    void apply(final Project project) {
        // Create NamedDomainObjectContainer instance for
        // a collection of Product objects
        NamedDomainObjectContainer<Product> productContainer =
            project.container(Product)
 
        // Add the container instance to our project
        // with the name products.
        project.extensions.add('products', productContainer)
 
        // Simple task to show the Product objects
        // that are created by Gradle using
        // the DSL syntax in our build file.
        project.task('reportProducts') << {
            def products = project.extensions.getByName('products')
 
            products.all {
                // A Product instance is the delegate
                // for this closure.
                println "$name costs $price"
            }
        }
    }
}
 
class Product {
    // We need a name property
    // so the object can be created
    // by Gradle using a DSL.
    String name
 
    BigDecimal price
 
    Product(final String name) {
        this.name = name
    }
}

运行结果

$ gradle -q reportProducts
crayon costs 0.18
pencil costs 0.05
$

进阶

嵌套情况下的使用

// File: build.gradle
apply plugin: com.mrhaki.gradle.DeploymentPlugin
 
deployments {
    aws {
        url = 'http://aws.address'
 
        nodes {
            node1 {
                port = 9000
            }
            node2 {
                port = 80
            }
        }
    }
 
    cf {
        url = 'http://cf.address'
 
        nodes {
            test {
                port = 10001
            }
            acceptanceTest {
                port = 10002
            }
        }
    }
}

Node类

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/Node.groovy
package com.mrhaki.gradle
 
class Node {
 
    String name
 
    Integer port
 
    /**
      * We need this constructor so Gradle can create an instance
      * from the DSL.
      */
    Node(String name) {
        this.name = name
    }
}

Server类

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/Server.groovy
package com.mrhaki.gradle
 
import org.gradle.api.NamedDomainObjectContainer
 
class Server {
 
    /**
     * An instance is created in the plugin class, because
     * there we have access to the container() method
     * of the Project object.
     */
    NamedDomainObjectContainer<Node> nodes
 
    String url
 
    String name
 
    /**
     * We need this constructor so Gradle can create an instance
     * from the DSL.
     */
    Server(String name) {
        this.name = name
    }
 
    /**
     * Inside the DSL this method is invoked. We use
     * the configure method of the NamedDomainObjectContainer to
     * automatically create Node instances.
     * Notice this is a method not a property assignment.
     * <pre>
     * server1 {
     *     url = 'http://server1'
     *     nodes { // This is the nodes() method we define here.
     *         port = 9000
     *     }
     * }
     * </pre>
     */
    def nodes(final Closure configureClosure) {
        nodes.configure(configureClosure)
    }
 
}

我们需要这样定义插件

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/DeploymentPlugin.groovy
package com.mrhaki.gradle
 
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.NamedDomainObjectContainer
 
class DeploymentPlugin implements Plugin<Project> {
 
    public static final String EXTENSION_NAME = 'deployments'
 
    private static final String DEPLOY_TASK_PATTERN = 'deployOn%sTo%s'
 
    private static final String REPORTING_TASK_NAME = 'reportDeployments'
 
    private static final String TASK_GROUP_NAME = 'Deployment'
 
    void apply(final Project project) {
        setupExtension(project)
        createDeploymentTasks(project)
        createReportTask(project)
    }
 
    /**
     * Create extension on the project for handling the deployments
     * definition DSL with servers and nodes. This allows the following DSL
     * in our build script:
     * <pre>
     * deployments {
     *     server1 {
     *         url = 'http://server'
     *         nodes {
     *             node1 {
     *                 port = 9000
     *             }
     *         }
     *     }
     * }
     * </pre>
     */
    private void setupExtension(final Project project) {
 
        // Create NamedDomainObjectContainer for Server objects.
        // We must use the container() method of the Project class
        // to create an instance. New Server instances are
        // automatically created, because we have String argument
        // constructor that will get the name we use in the DSL.
        final NamedDomainObjectContainer<Server> servers =
            project.container(Server)
 
        servers.all {
            // Here we have access to the project object, so we
            // can use the container() method to create a
            // NamedDomainObjectContainer for Node objects.
            nodes = project.container(Node)
        }
 
        // Use deployments as name in the build script to define
        // servers and nodes.
        project.extensions.add(EXTENSION_NAME, servers)
    }
 
    /**
     * Create a new deployment task for each node.
     */
    private void createDeploymentTasks(final Project project) {
        def servers = project.extensions.getByName(EXTENSION_NAME)
 
        servers.all {
            // Actual Server instance is the delegate
            // of this closure. We assign it to a variable
            // so we can use it again inside the
            // closure for nodes.all() method.
            def serverInfo = delegate
 
            nodes.all {
                // Assign this closure's delegate to
                // variable so we can use it in the task
                // configuration closure.
                def nodeInfo = delegate
 
                // Make node and server names pretty
                // for use in task name.
                def taskName =
                    String.format(
                        DEPLOY_TASK_PATTERN,
                        name.capitalize(),
                        serverInfo.name.capitalize())
 
                // Create new task for this node.
                project.task(taskName, type: DeploymentTask) {
                    description = "Deploy to '${nodeInfo.name}' on '${serverInfo.name}'"
                    group = TASK_GROUP_NAME
 
                    server = serverInfo
                    node = nodeInfo
                }
            }
        }
    }
 
    /**
     * Add reporting task to project.
     */
    private void createReportTask(final Project project) {
        project.task(REPORTING_TASK_NAME, type: DeploymentReportTask) {
            description = 'Show configuration of servers and nodes'
            group = TASK_GROUP_NAME
        }
    }
}

DeploymentTask和DeploymentReportTask只是配合测试

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/DeploymentTask.groovy
package com.mrhaki.gradle
 
import org.gradle.api.tasks.TaskAction
import org.gradle.api.DefaultTask
 
class DeploymentTask extends DefaultTask {
 
    Server server
 
    Node node
 
    /**
     * Simple implementation to show we can
     * access the Server and Node instances created
     * from the DSL.
     */
    @TaskAction
    void deploy() {
        println "Deploying to ${server.url}:${node.port}"
    }
 
}
// File: buildSrc/src/main/groovy/com/mrhaki/gradle/DeploymentReportTask.groovy
package com.mrhaki.gradle
 
import org.gradle.api.tasks.TaskAction
import org.gradle.api.DefaultTask
 
class DeploymentReportTask extends DefaultTask {
 
    /**
     * Simple task to show we can access the
     * Server and Node instances also via the
     * project extension.
     */
    @TaskAction
    void report() {
        def servers = project.extensions.getByName(DeploymentPlugin.EXTENSION_NAME)
 
        servers.all {
            println "Server '${name}' with url '${url}':"
 
            nodes.all {
                println "\tNode '${name}' using port ${port}"
            }
        }
    }
 
}

运行结果

$ gradle -q tasks
...
Deployment tasks
----------------
deployOnAcceptanceTestToCf - Deploy to 'acceptanceTest' on 'cf'
deployOnNode1ToAws - Deploy to 'node1' on 'aws'
deployOnNode2ToAws - Deploy to 'node2' on 'aws'
deployOnTestToCf - Deploy to 'test' on 'cf'
reportDeployments - Show configuration of servers and nodes
...
$ gradle -q deployOnNode2ToAws
 
Deploying to http://aws.address:80
$ gradle -q reportDeployments
 
Server 'aws' with url 'http://aws.address':
 Node 'node1' using port 9000
 Node 'node2' using port 80
Server 'cf' with url 'http://cf.address':
 Node 'acceptanceTest' using port 10002
 Node 'test' using port 10001
$

参见英文原版

上一篇 下一篇

猜你喜欢

热点阅读