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 进行扩展。