Status
QDox 是一种高速、小内存占用的解析器,用于完全提取类/接口/方法定义(包括注解、参数、参数名)。它被设计为可供活跃的代码生成器或文档工具使用。
大概意思是一款完整的 java 类、接口、方法定义的提取器,包括了注释、参数及参数名称。其实核心功能就是你输入一个 java 类的源码,他可以把这个 java 类解析成一个对象,我们通过这个对象可以获取很方便的获取解析的类的不同组成,比如我可以获得这个类有哪些方法,这个方法的参数是什么,返回值又是什么,他们的类型又分别是什么?还有这个方法上有哪些注释、哪些 tag。也能获取类中有哪些的 field。。。总之把这个类庖丁解牛般解析好,使得调用者很方便的获取到自己感兴趣的信息。
虽然不再那么重要,但它也能处理 JavaDoc @标签。
In A Nutshell(简而言之)
使用JFlex和BYacc/J构建了一个定制的解析器。之所以选择这些库,是因为它们的性能已得到验证,并且在运行时不需要外部库。
解析器只略读源文件,查找感兴趣的内容,如类/接口定义、导入语句、JavaDoc和成员声明。解析器忽略诸如实际方法实现之类的内容以避免开销(而在方法块中,花括号计数就足够了)。
解析器的最终结果是一个非常简单的文档模型,其中包含足够有用的信息。
Frequently Asked Questions(常见问题)
General
接口的对象类型是什么?
JavaClass方法用于表示类和接口。isInterface()方法允许您区分这两者。
当使用一个类时,getSuperClass()返回被扩展的类。如果在输入源代码中没有定义,则返回java.lang.Object。当使用接口时,此方法总是返回null。
当使用一个类时,getImplements()返回一个由这个类实现的接口数组。如果没有实现,则返回一个空数组。当使用接口时,这将返回当前接口EXTENDS的接口数组。
我可以完全控制类加载器吗?
在某些情况下,QDox被用来为另一个具有自己依赖项的项目生成类。这可能导致类冲突。默认情况下,JavadocBuilder将包含当前项目的类加载器,但是通过定义您自己的classLibrary,您可以拥有所需的控制。
/* new ClassLibrary()将给你一个空的classLoader
* 很有可能你至少需要 system classloader(系统类加载器)。
*/
ClassLibraryBuilder libraryBuilder = new SortedClassLibraryBuilder(); //or OrderedClassLibraryBuilder()
libraryBuilder.addClassLoader( ClassLoader.getSystemClassLoader() );
JavaProjectBuilder builder = new JavaProjectBuilder( libraryBuilder );
下载
QDox可在Maven中心获得。要在您的pom中包含最新的QDox,请包含以下依赖项:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</dependency>
QDox 的使用
入口点
JavaProjectBuilder是QDox的入口点。它负责解析源代码、解析导入和存储数据。
要创建它,需要做的就是调用默认构造函数。
JavaProjectBuilder builder = new JavaProjectBuilder();
读取源文件
然后可以将Java源代码添加到JavaProjectBuilder中。源代码可以一次读取一个文件(使用java.io.Reader),也可以递归地添加整个源代码树。
// Reading a single source file.
builder.addSource(new FileReader("MyFile.java"));
// Reading from another kind of input stream.
builder.addSource(new StringReader("package test; public class Hello {}"));
// Adding all .java files in a source tree (recursively).
builder.addSourceTree(new File("mysrcdir"));
解析类名
为了解析使用通配符导入的类(例如import java.util.*;), ClassLibrary必须知道项目中使用的其他类。
ClassLibrary有4种方法来解析类:
- 通过查看已添加的其他源。
- 通过搜索提供的sourceFolders
- 通过查看当前类路径(包括标准JRE类)。
- 通过查看在运行时指定的其他classloader。
添加到JavaProjectBuilder中的所有源代码和源代码树都将被解析。这通常远远超出了要求。为了提高效率,使用ClassLibrary来添加源文件夹。将这些文件视为惰性解析源文件。
当前的类路径是由JavaProjectBuilder自动设置的。在大多数情况下,这应该是足够的,但是在某些情况下,您可能希望在外部库中解析完整的类。
// 获取 ClassLibrary
JavaProjectBuilder builder = new JavaProjectBuilder();
// 添加一个 sourcefolder;
builder.addSourceFolder( new File( "src/main/java" ) );
builder.addSourceFolder( new File( "target/generated-sources/foobar" ) );
// 添加一个自定义 ClassLoader
builder.addClassLoader( myCustomClassLoader );
// Ant example : add the <classpath> element's contents
builder.addClassLoader( new AntClassLoader( getProject(), classpath ) );
在解析任何源文件之前添加额外的classloader是很重要的。
导航模型
现在已经解析了文件,接下来进行模型导航。
模型
在源代码被解析之后,可以使用简单易用且直观的对象模型来导航文件的内容。
Java源代码
表示一个完整的.java文件。它包含一个类的集合。
输入例子
package com.blah.foo;
import java.awt.*;
import java.util.List;
public class Class1 {
...
}
class Class2 {
}
interface Interface1 {
}
Example Code
JavaProjectBuilder builder = new JavaProjectBuilder();
JavaSource src = builder.addSource(myReader);
JavaPackage pkg = src.getPackage();
List<String> imports = src.getImports(); // {"java.awt.*",
// "java.util.List"}
JavaClass class1 = src.getClasses().get(0);
JavaClass class2 = src.getClasses().get(1);
JavaClass interface1 = src.getClasses().get(2);
JavaPackage
表示类的包。
输入例子
package com.blah.foo;
public class BarClass {
...
}
示例代码
JavaProjectBuilder builder = new JavaProjectBuilder();
JavaSource src = builder.addSource(myReader);
JavaPackage pkg = src.getPackage();
Collection<JavaClass> classes = pkg.getClasses(); // BarClass
String name = pkg.getName(); // "com.blah.foo"
String toString = pkg.toString(); // "package com.blah.foo" conform javaAPI
JavaPackage parent = pkg.getParentPackage(); //
JavaClass
表示类或接口。它包含doclet标记、字段和方法。可以获得有关类定义的信息,例如扩展了哪些类,实现了哪些接口和修饰符。
Example Input
package com.blah.foo;
import java.io.*;
import com.custom.*;
import com.base.SubClass;
/**
* @author Joe
*/
public abstract class MyClass extends SubClass
implements Serializable, CustomInterface {
private String name;
public void doStuff() { ... }
private int getNumber() { ... }
}
示例代码
JavaProjectBuilder builder = new JavaProjectBuilder();
builder.addSource(myReader);
JavaClass cls = builder.getClassByName("com.blah.foo.MyClass");
String pkg = cls.getPackage(); // "com.blah.foo"
String name = cls.getName(); // "MyClass"
String fullName = cls.getCanonicalName(); // "com.blah.foo.MyClass";
String canonicalName = cls.getFullyQualifiedName(); // "com.blah.foo.MyClass";
boolean isInterface = cls.isInterface(); // false
boolean isPublic = cls.isPublic(); // true
boolean isAbstract = cls.isAbstract(); // true
boolean isFinal = cls.isFinal(); // false
JavaType superClass = cls.getSuperClass(); // "com.base.SubClass";
List<JavaType> imps = cls.getImplements(); // {"java.io.Serializable",
// "com.custom.CustomInterface"}
String author = cls.getTagsByName("author").getValue(); // "joe"
JavaField nameField = cls.getFields()[0];
JavaMethod doStuff = cls.getMethods()[0];
JavaMethod getNumber = cls.getMethods()[1];
JavaSource javaSource = cls.getParentSource();
JavaField
表示类中的字段。它包含doclet标记、名称和类型。
输入例子
import java.util.Date;
public class MyClass {
/**
* @magic
*/
private String email;
public static Date[][] dates;
}
示例代码
JavaField e = cls.getFields()[0];
JavaType eType = e.getType(); // "java.lang.String";
String eName = e.getName(); // "email";
DocletTag eTag = e.getTagsByName("magic"); // @magic
boolean eArray = e.getType().isArray(); // false;
JavaField d = cls.getFields()[1];
JavaType dType = d.getType(); // "java.util.Date";
String dName = d.getName(); // "dates";
DocletTag dTag = d.getTagsByName("magic"); // null
boolean dArray = d.getType().isArray(); // true;
int dDimensions= d.getType().getDimensions(); // 2;
boolean dStatic= d.isStatic(); // true;
JavaClass javaClass = d.getParentClass();
JavaMethod
表示类中的方法。它包含doclet标记、名称、返回类型、参数和异常。
输入例子
import java.util.Date;
import java.io.*;
public class MyClass {
/**
* @returns Lots of dates
*/
public static Date[] doStuff(int number,
String stuff)
throws RuntimeException, IOException {
...
}
}
示例代码
JavaMethod m = cls.getMethods()[0];
String mName = m.getName(); // "doStuff";
JavaType mReturns = m.getReturns(); // "java.util.Date";
boolean mArray = m.getReturns().isArray(); // true
boolean mStatic = m.isStatic(); // true
boolean mPublic = m.isPublic(); // true
String doc = m.getTagByName("returns").getValue();
// "Lots of dates"
List<JavaType> exceptions = m.getExceptions();
// {"java.lang.RuntimeException", "java.io.IOException"}
JavaParameter numberParam = m.getParameters()[0];
JavaParameter stuffParam = m.getParameters()[1];
JavaClass javaClass = m.getParentClass();
JavaParameter
表示传递给方法的参数。它有名称和类型。
Example Input
public class MyClass {
public void stuff(int n, Object[] objects) {
...
}
}
示例代码
JavaMethod m = cls.getMethods()[0];
JavaParameter n = m.getParameters()[0];
String nName = n.getName(); // "n"
JavaType nType = n.getType(); // "int";
JavaParameter o = m.getParameters()[1];
String oName = o.getName(); // "objects"
JavaType oType = o.getType(); // "java.lang.Object";
boolean oArray = o.getJavaClass().isArray(); // true
JavaMethod javaMethod = o.getParentMethod();
JavaType
表示被另一个类使用的类的特定实例(如返回值、超类等)。该值表示类的名称。数组尺寸也是可用的。从1.8开始,也可以获得Type的泛型值
输入例子
import java.util.*;
public class MyClass {
public void stuff(int n, Object[] objects,
Date[][] dates, List<String> stringList) {
...
}
}
示例代码
JavaMethod m = cls.getMethods()[0];
JavaType returns = m.getReturns();
returns.getValue(); // "void"
returns.isArray(); // false
returns.getDimensions(); // 0
JavaType n = m.getParameters()[0].getType();
n.getValue(); // "int"
n.isArray(); // false
n.getDimensions(); // 0
JavaType objects = m.getParameters()[1].getType();
objects.getValue(); // "java.lang.Object"
objects.isArray(); // true
objects.getDimensions(); // 1
JavaType dates = m.getParameters()[2].getType();
dates.getValue(); // "java.util.Date"
dates.isArray(); // true
dates.getDimensions(); // 2
JavaType stringList = m.getParameters()[3].getType();
stringList.getValue(); // "java.util.List"
stringList.getGenericValue(); // "java.util.List<java.lang.String>"
stringList.isArray(); // false
stringList.getDimensions(); // 0
DocletTag
表示JavaDoc标记。每个标记都有一个名称和一个值。可选地,可以将值分解为按索引或名称访问的令牌。
JavaClass、JavaField和JavaMethod类都支持注释和文档标签
返回的DocletTag带有名称、值和将值分解为特定参数的方法。
输入例子
/**
* This method does nothing at all.
*
* @returns A boolean of whether we care or not.
* @param email Someone's email address.
* @param dob Date of birth.
*
* @permission administrator full-access
* @webservice publish=true name=myservice type=rpc
*/
boolean doWeCare(String email, Date dob);
示例代码
JavaMethod mth = cls.getMethods()[0];
// Access the JavaDoc comment
String comment = mth.getComment();
// "This method does nothing at all."
// Access a single doclet tag
DocletTag returns = mth.getTagByName("returns");
returns.getName(); // "returns";
returns.getValue(); // "A boolean of whether we care or not."
// Access multiple doclet tags with the same name
DocletTag[] params = mth.getTagsByName("param");
params[0].getValue(); // "Someone's email address."
params[1].getValue(); // "Date of birth."
// Access specific parameters of a doclet tag by index
DocletTag permission = mth.getTagByName("permission");
permission.getParameter[0]; // "administrator"
permission.getParameter[1]; // "full-access"
// Access specific parameters of a doclet tag by name
DocletTag webservice = mth.getTagByName("webservice");
webservice.getNamedParameter("type"); // "rpc"
webservice.getNamedParameter("name"); // "myservice"