(七)Ant扩展介绍

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 支持的转换规则如下:

  1. 最常见的 java.lang.String 类型;
  2. 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));
    }
  3. charjava.lang.Character,将传递指定值的第一个字符;
  4. 其他基本类型(int,short,long,float,double,byte);
  5. java.io.File,Ant首先会判断构建文件中给出的值是否代表绝对路径名。如果不是,Ant 会将该值解释为相对于项目的 basedir 的路径名;
  6. org.apache.tools.ant.types.Resource,Ant 会将字符串解析为上面的 java.io.File ,然后作为 org.apache.tools.ant.types.resources.FileResource 传入;
  7. org.apache.tools.ant.types.Path,Ant 将标记化构建文件中指定的值,接受 : 和 ;作为路径分隔符。相对路径名将被解释为相对于项目的 basedir;
  8. java.lang.Class ,Ant 会将构建文件中给出的值解释为 Java 类名,并从系统类加载器加载指定的类;
  9. org.apache.tools.ant.types.EnumeratedAttribute 的子类,会调用子类的 setValue 方法,设置的值必须是抽象方法 String[] getValues() 包含的值,通常用于指定固定选项。
  10. 枚举值,会和枚举的 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
{
/**
* The Pattern sets.
*/
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

对于每个嵌套元素,需要编写一个 createaddaddConfigured 方法。 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 单参数构造方法

4.3 addConfiguredXxx

这个方法和上一个类似,但是这里会传入一个配置好的 PatternSet,如果你需要在配置好的对象上进行后续处理,可以使用这个方法,如果想在配置前处理,可以选择前两种方式。

4.4 不要同时使用这3种方法

同时存在时不一定会调用哪一个方法,当你方法中存在逻辑时,就无法保证处理一致。不会像你所想的,先调用 create 创建对象,再通过 add 初始化值,最后调用 addConfigured 方法进行后续处理。

了解这些内容后就可以扩展 Ant 任务了,下一节开始针对 yGuard 进行扩展。


(七)Ant扩展介绍
https://blog.mybatis.io/post/aa60e3e9.html
作者
Liuzh
发布于
2024年6月22日
许可协议