创建一个领域配置对象(DSL configuration container)
我们在 Android 的主工程 build.gradle 中一定见过以下代码:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
...
}
}
上面的 release
,debug
都是我们自己取的名字,我们可以写任意多个、取任意名字,但是 release
或 debug
却是内置、有限的。这是怎么实现的呢?
其实是使用了 NamedDomainObjectContainer
。
NamedDomainObjectContainer
下面我们来实现如下效果:
apply plugin: ServerEnvironmentPlugin
// environment 后面闭包里的内容其实是一个 container
environments {
// 每一组都是一个 ServerEnvironment 对象
dev {
url = 'http://localhost:8080'
}
staging {
url = 'http://staging.enterprise.com'
}
production {
url = 'http://prod.enterprise.com'
}
...
}
environment
是我们自定义的一个扩展名,它对应的是一个 NamedDomainObjectContainer
,也就是说,在 ExtensionContainer
中,key 是 environment
,而 value 是 NamedDomainObjectContainer
的一个实例。既然是个 Container 那么它就是一个集合,而这个集合中的元素,就是用户需要添加的。
我们先写这个集合元素类 ServerEnvironment
:
public class ServerEnvironment {
private final String name;
private Property<String> url;
@Inject
public ServerEnvironment(String name, ObjectFactory objectFactory) {
this.name = name;
this.url = objectFactory.property(String.class);
}
public void setUrl(String url) {
this.url.set(url);
}
public String getName() {
return name;
}
public Property<String> getUrl() {
return url;
}
}
上面的写法中,注意构造函数需要使用 @Inject
来注解,只有这样 ObjectFactory
才能被注入进来。上面例子中还是用了 Property
属性来进行延迟加载。
类 ServerEnvironment
就代表我们使用时的:
dev {
url = 'http://localhost:8080'
}
下面我们来写 Plugin:
public class ServerEnvironmentPlugin implements Plugin<Project> {
@Override
public void apply(Project target) {
NamedDomainObjectContainer<ServerEnvironment> serverEnvironmentsContainer = target.container(ServerEnvironment.class,
name -> target.getObjects().newInstance(ServerEnvironment.class, name, target.getObjects()));
target.getExtensions().add("environments", serverEnvironmentsContainer);
serverEnvironmentsContainer.all(serverEnvironment -> {
String env = serverEnvironment.getName();
String capitalizedServerEnv = env.substring(0, 1).toUpperCase() + env.substring(1);
String taskName = "deployTo" + capitalizedServerEnv;
target.getTasks().register(taskName, Deploy.class, deploy -> deploy.getUrl().set(serverEnvironment.getUrl()));
});
}
}
- 首先通过方法
Project#container(Class<T> type, NamedDomainObjectFactory<T> factory)
创建一个NamedDomainObjectContainer
对象。第二个参数是NamedDomainObjectFactory
它只有一个方法,接收一个String
类型的name
。这个name
就是我们写代码时那个自定义的名字,也就是上面例子中的dev
,staging
,production
。然后通过Project#getObjects()#newInstance()
来实例化一个ServerEnvironment
对象。注意,newInstance()
方法第一个是我们要实例化的类型Class,后面的参数就是ServerEnvironment
构造方法接收的参数列表。 - 我们向 Project 的
ExtensionContainer
中添加一个元素,它的key 为environments
,value 为上面实例化出来的serverEnvironemtContainer
对象。这样我们就可以在代码中使用environments
- 遍历
serverEnvironmentContainer
,根据ServerEnvironment
中的内容动态注册 Task。
我们此时再执行 gradle tasks --all
,就能看到我们动态注册的 Task 了。

我们执行 deployToDev
就可以得到我们想要的结果。

网友评论