ant 扩展官方文档:https://ant.apache.org/manual/develop.html
Writing Your Own Task
编写你自己的任务
1. 创建一个XXTask类
创建一个Java类继承org.apache.tools.ant.Task
,实际上不继承也可以,定义一个 execute()
方法就可以,例如下面的例子:
1 2 3 4 5
| public class MyTask { public void execute() { System.out.println("MyTask"); } }
|
配置到插件中测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <phase>package</phase> <goals> <goal>run</goal> </goals> <id>obfuscate</id> <configuration> <tasks> <property name="runtime_classpath" refid="maven.runtime.classpath"/> <taskdef name="my-task" classname="MyTask" classpath="${runtime_classpath}"/> <my-task/> </tasks> </configuration> </execution> </executions> </plugin>
|
后续示例只提供 <tasks>
中的内容。
可以在示例的 module-yguard 中的 pom.xml 中测试。
执行 package 打包,部分输出日志如下:
1 2 3 4 5 6 7
| [INFO] --- maven-antrun-plugin:1.8:run (obfuscate) @ module-yguard --- [WARNING] Parameter tasks is deprecated, use target instead [INFO] Executing tasks
main: MyTask [INFO] Executed tasks
|
接下来继承 org.apache.tools.ant.Task
试试:
1 2 3 4 5
| public class MyTask extends org.apache.tools.ant.Task { public void execute() { System.out.println("MyTask, name: " + getTaskName()); } }
|
执行输出的部分日志如下:
1 2 3 4 5 6 7
| [INFO] --- maven-antrun-plugin:1.8:run (obfuscate) @ module-yguard --- [WARNING] Parameter tasks is deprecated, use target instead [INFO] Executing tasks
main: MyTask, name: my-task [INFO] Executed tasks
|
警告中建议将 tasks
改为 target
,修改试试:
1 2 3 4 5
| <target> <property name="runtime_classpath" refid="maven.runtime.classpath"/> <taskdef name="my-task" classname="MyTask" classpath="${runtime_classpath}"/> <my-task/> </target>
|
再次运行就没有警告信息了。
2. 给 <my-task>
添加属性
给 <my-task>
添加属性,例如:
1
| <my-task path="${user.dir}"/>
|
为了能接收这个属性值,需要在 MyTask
添加对应的 setter
方法, setter
方法必须是采用单个参数的 public void
方法,修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.io.File;
public class MyTask extends org.apache.tools.ant.Task { private File path;
public void setPath(File path) { this.path = path; }
public void execute() { System.out.println("MyTask, name: " + getTaskName() + ", path: " + path); } }
|
执行测试输出内容如下:
1 2 3 4 5 6
| [INFO] --- maven-antrun-plugin:1.8:run (obfuscate) @ module-yguard --- [INFO] Executing tasks
main: MyTask, name: my-task, path: E:\GitHub\yguard-modules-parent [INFO] Executed tasks
|
可以看到 Ant 自动把参数转换为 File
类型了,Ant 支持的转换规则如下:
- 最常见的
java.lang.String
类型;
boolean
类型,支持设置为 true,yes,on
,其他值则为 false
。这部分的逻辑在 Project
类中,代码如下: 1 2 3 4 5
| public static boolean toBoolean(final String s) { return ("on".equalsIgnoreCase(s) || "true".equalsIgnoreCase(s) || "yes".equalsIgnoreCase(s)); }
|
char
或 java.lang.Character
,将传递指定值的第一个字符;
- 其他基本类型(
int,short,long,float,double,byte
);
java.io.File
,Ant首先会判断构建文件中给出的值是否代表绝对路径名。如果不是,Ant 会将该值解释为相对于项目的 basedir 的路径名;
org.apache.tools.ant.types.Resource
,Ant 会将字符串解析为上面的 java.io.File
,然后作为 org.apache.tools.ant.types.resources.FileResource
传入;
org.apache.tools.ant.types.Path
,Ant 将标记化构建文件中指定的值,接受 : 和 ;作为路径分隔符。相对路径名将被解释为相对于项目的 basedir;
java.lang.Class
,Ant 会将构建文件中给出的值解释为 Java 类名,并从系统类加载器加载指定的类;
org.apache.tools.ant.types.EnumeratedAttribute
的子类,会调用子类的 setValue
方法,设置的值必须是抽象方法 String[] getValues()
包含的值,通常用于指定固定选项。
- 枚举值,会和枚举的
name
进行匹配。
3. 给 <my-task>
添加文本数据
添加文本数据示例:
1
| <my-task path="${user.dir}" enable="hello">中间的文本</my-task>
|
很显然这种方式和在<my-task>
添加子元素是冲突的,这种情况下需要添加 public void addText(String)
方法,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import java.io.File;
public class MyTask extends org.apache.tools.ant.Task { private File path; private boolean enable; private String text;
public void setEnable(boolean enable) { this.enable = enable; }
public void setPath(File path) { this.path = path; }
public void addText(String text) { this.text = text; }
public void execute() { System.out.println("MyTask, name: " + getTaskName() + ", path: " + path + ", enable: " + enable + ", text: " + text); } }
|
测试输出结果:
1 2 3 4 5 6
| [INFO] --- maven-antrun-plugin:1.8:run (obfuscate) @ module-yguard --- [INFO] Executing tasks
main: MyTask, name: my-task, path: E:\GitHub\yguard-modules-parent, enable: false, text: 中间的文本 [INFO] Executed tasks
|
4. 给 <my-task>
添加嵌套元素
例如前面提到的 <patternset>
元素:
1 2 3 4 5 6
| <my-task> <patternset> <include name="org.example.c."/> <exclude name="org.example.c.util.FileUtil"/> </patternset> </my-task>
|
更新 MyTask
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import org.apache.tools.ant.types.PatternSet;
import java.util.ArrayList; import java.util.List;
public class MyTask extends org.apache.tools.ant.Task {
private List<PatternSet> patternSets = new ArrayList(5);
public void addConfiguredPatternSet(PatternSet ps) { patternSets.add(ps); }
public void execute() { patternSets.forEach(ps -> { String[] includes = ps.getIncludePatterns(getProject()); String[] excludes = ps.getExcludePatterns(getProject()); System.out.println("includes: " + String.join(", ", includes)); System.out.println("excludes: " + String.join(", ", excludes)); }); } }
|
执行代码输出结果:
1 2 3 4 5 6 7
| [INFO] --- maven-antrun-plugin:1.8:run (obfuscate) @ module-yguard --- [INFO] Executing tasks
main: includes: org.example.c. excludes: org.example.c.util.FileUtil [INFO] Executed tasks
|
对于每个嵌套元素,需要编写一个 create
、add
或 addConfigured
方法。 create
方法必须是不带参数并返回 Object 类型的 public 方法。 create
方法的名称必须以 create
开头,后跟元素名称。 add
(或 addConfigured
)方法必须是一个 public void
方法,该方法采用带有无参数构造函数的 Object 类型的单个参数。 add
(addConfigured
) 方法的名称必须以 add
( addConfigured
) 开头,后跟元素名称。
也就是上面示例中,支持下面3种写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public PatternSet createPatternSet() { PatternSet ps = new PatternSet(); patternSets.add(ps); return ps; }
public void addPatternSet(PatternSet ps) { patternSets.add(ps); }
public void addConfiguredPatternSet(PatternSet ps) { patternSets.add(ps); }
|
这3种写法不能同时存在,具体差异通过 debug 来查看。测试时注释另外两个方法。
4.1 createXxx 方法
这个方法会创建一个空的 PatternSet
对象,这个对象需要提前添加到 patternSets
,否则后续无法获取该值。这里返回该对象后,这个对象会被赋值,在后续执行 execute
时是有值的。
当你嵌套的对象需要复杂的创建方式时,可以采用这种方式,这种方式适合初始化对象。
4.2 addXxx 方法
可以看到Ant自己实例化了一个PatternSet
对象注入进来了,而且此时该对象是空的,还没有被赋值,这种情况下的参数(如这里的PatternSet
)需要提供 public 无参数构造方法或将 Project
类作为参数的 public 单参数构造方法。
这个方法和上一个类似,但是这里会传入一个配置好的 PatternSet
,如果你需要在配置好的对象上进行后续处理,可以使用这个方法,如果想在配置前处理,可以选择前两种方式。
4.4 不要同时使用这3种方法
同时存在时不一定会调用哪一个方法,当你方法中存在逻辑时,就无法保证处理一致。不会像你所想的,先调用 create
创建对象,再通过 add
初始化值,最后调用 addConfigured
方法进行后续处理。
了解这些内容后就可以扩展 Ant 任务了,下一节开始针对 yGuard 进行扩展。