Spoon系列-概要
- 概要
- 相关资源
- 主要功能
- 可构建 AST(Abstract Syntax Tree)
- 静态代码分析(Static Code Analysis)
- 代码重写转换(Transform)
概要
可对Java 源代码进行分析,重写,转换的开源工具。支持Java13。
相关资源
Source&Document
Example
学术文档
主要功能
可构建 AST(Abstract Syntax Tree)
例如对Helloworld.java
package spoon_explore_test;public class HelloWorld {public void helloWorld() {System.out.println("Hello World!");}public static void main(String[] args) {new HelloWorld().helloWorld();try {} catch (Throwable e) {}}}
当执行完如下命令后:
java -cp spoon -core -{{site.spoon_release}}-jar -with -dependencies.jar spoon.Launcher \
-i helloword.java --gui
可生成如下的AST:

静态代码分析(Static Code Analysis)
- 可通过Processor对代码进行静态扫描
- 可检出代码缺陷等
- 和JUnit联合使用,强制遵循命名规范等架构规范。
例如:下面的Processor将会对代码进行静态扫描,查找出空的try{}catch{}代码块的代码:
package spoon_explore;import org.apache.logging.log4j.Level;import spoon.processing.AbstractProcessor;
import spoon.reflect.code.CtCatch;/*** Reports warnings when empty catch blocks are found.*/
public class CatchProcessor extends AbstractProcessor<CtCatch> {public void process(CtCatch element) {
// we get all statements and if there isn't statement , it means the block catch is empty!if (element.getBody().getStatements().size() == 0) {System.out.println("empty catch clause:" + element);getFactory().getEnvironment().report(this, Level.WARN, element, "empty catch clause");}}
}
同样对于Helloworld.java
package spoon_explore_test;public class HelloWorld {public void helloWorld() {System.out.println("Hello World!");}public static void main(String[] args) {new HelloWorld().helloWorld();try {} catch (Throwable e) {}}}
执行:
$ java -classpath /path/to/processor.jar:spoon -core -{{site.spoon_release}}-jar -with -
dependencies.jar \
spoon.Launcher -i /path/to/src/of/your/project -p processors.CatchProcessor
返回如下结果:
empty catch clause:catch (java.lang.Throwable e) {
}
例如:下面的代码当public方法的Javadoc不完整的时候,Junit测试会无法通过。
package spoon_explore;import static org.junit.Assert.fail;import java.util.ArrayList;
import java.util.List;import org.apache.commons.lang3.StringUtils;
import org.junit.Test;import spoon.Launcher;
import spoon.SpoonAPI;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.visitor.filter.TypeFilter;public class JavaDocTest {@Testpublic void testDocumentation() throws Exception {SpoonAPI spoon = new Launcher();spoon.addInputResource("src/main/java/");spoon.buildModel();List<String> notDocumented = new ArrayList<>();for (CtMethod method : spoon.getModel().getElements(new TypeFilter<>(CtMethod.class))) {// now we see whether this should be documentedif (method.hasModifier(ModifierKind.PUBLIC) && method.getTopDefinitions().size() == 0) // optional: only the// top{// is it really well documented?if (method.getDocComment().length() < 20) { // at least 20 charactersnotDocumented.add(method.getParent(CtType.class).getQualifiedName() + "#" + method.getSignature());}}}if (notDocumented.size() > 0){fail(notDocumented.size() + " public methods should be documented with proper API documentation: \n"+ StringUtils.join(notDocumented, "\n"));}}
}
代码重写转换(Transform)
可通过Spoon提供的API来改写AST树,重写或者转换元代码。
例如:利用如下的Processor可在代码之前添加System.out.println打印函数的入口信息:
package spoon_explore;import spoon.Launcher;
import spoon.processing.AbstractProcessor;
import spoon.reflect.code.CtCodeSnippetStatement;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtExecutable;/*** Example of tracing** class A{ void m(Object o} { ...(method body) .... } }** is transformed into** void m(Object o} { System.out.println("enter in method m from class A"); //* rest of the method body } Use with $ java -jar spoon.jar -i src/main/java -o* spooned -p fr.inria.gforge.spoon.transformation.autologging.LogProcessor** Of with https://github.com/SpoonLabs/spoon-maven-plugin* */
public class LogProcessor extends AbstractProcessor<CtExecutable> {@Overridepublic void process(CtExecutable element) {CtCodeSnippetStatement snippet = getFactory().Core().createCodeSnippetStatement();// Snippet which contains the log.final String value = String.format("System.out.println(\"Enter in the method %s from class %s\");",element.getSimpleName(), element.getParent(CtClass.class).getSimpleName());snippet.setValue(value);// Inserts the snippet at the beginning of the method body.if (element.getBody() != null) {element.getBody().insertBegin(snippet);}}public static void main(String[] args) {Launcher spoon = new Launcher();spoon.addInputResource("C:/d/workspace_learn/spoon_learn/spoon_explore_test/src/main/java/");spoon.addProcessor(new LogProcessor());spoon.run();spoon.prettyprint();}
}
对于Helloworld.java被重写的代码如下:
package spoon_explore_test;
public class HelloWorld {public void helloWorld() {System.out.println("Enter in the method helloWorld from class HelloWorld");;java.lang.System.out.println("Hello World!");}public static void main(java.lang.String[] args) {System.out.println("Enter in the method main from class HelloWorld");;new spoon_explore_test.HelloWorld().helloWorld();try {} catch (java.lang.Throwable e) {}}
}















