1.gradle乱码问题
在gradle中定义了一个自定义的task,该task用于生成JavaDoc
task generatePublicAPIDocs(type: JavaDoc) {
List<File> list = new ArrayList()
list.add(file("src/main/java/com/fr/doclet"))
options.setDocletPath(list)
options.setDoclet("com.fr.doclet.JavadocFilter")
source = sourceSets.main.allJava
destinationDir = file("${buildDir}/doclet/doc")
}
然后运行该任务,有报错,看了堆栈是执行JavaDoc.exe时返回值为1。接下来尝试修改source和doclet。发现这两个值其中一个不设置就能执行成功,由于报错是乱码所以没能确定原因。而后又发现工程已经使用了aggregate-JavaDoc插件,其中也能设定上面的属性,以为有所冲突,想着在此寻求突破,结果又白忙活。最终决定先解决乱码的问题,看看报错的信息到底是什么。
百度试了几乎所有的方法都没有用,之后偶然看到说把settings中maven的runner的VM OPtions设置为 -Dfile.encoding=GBK。尝试了之后没有用。那就给gradle 的runner也设置一下,但是idea的settings中的gradle的runner没有该设置项,直接在gradle中设置,解决了乱码问题。终于知道错误的原因是找不到doclet类 com.fr.doclet.JavadocFilter。
2.自定义配置doclet
接下来就只要解决类找不到的问题。尝试了多种src目录和类名的组合都不行,之后在一篇博客上看到docletPath设置的路径带有build。于是build了项目,把路径设置为class文件的路径,终于在某一次组合中报错noclassdefinefound。所以路径应该就是build的路径,也就是设置的应该是class文件的路径而不是源文件的路径。最后的配置如下,任务执行成功:
//设置JavaDoc生成的路径 destinationDir = file("${buildDir}/doclet/doc") //设置查找自定义doclet的路径 List<File> fs = new ArrayList<>() fs.add(file("build/classes/java/main")) options.setDocletpath(fs) //设置自定义doclet options.setDoclet("包名.JavadocFilter")
3.配置多个子模块都能使用
首先直接设置source = sourceSets.main.allJava只包含父模块的类。所以要加上所有子模块的类即 source = allprojects.sourceSets.main.allJava。这样设置之后运行task会报错找不到类和包等等。通过搜索配置项发现:
-sourcepath<路径列表> 指定查找源文件的位置
-classpath<路径列表> 指定查找用户类文件的位置
所以还需要设定classpath路径,即classpath += files(allprojects.sourceSets.main.output.classDirs.files)。接着对某一模块测试成功生成了所有JavaDoc,但是对父工程生成JavaDoc的时候又报了找不到的错误,看报错信息这些类是在模块的lib里的jar包中的,所以还要给classpath加上模块的lib的路径。对于这部分的实现可以在gradle中使用配置项的形式,父工程定义了一个配置hasLibDependencies,默认为false。如果子模块有lib依赖则复写为true。在对应的gradle的task中对于该配置项进行判断来向classpath加lib路径。
在加了lib路径到classpath后还是会报找不到的错误,这是由于部分模块依赖了第三方库,这些jar在本地gradle目录下。尝试使用gradleuserhome来把路径加入,但是失败了,只好退而求其次把jar包复制一份放在lib文件夹中,为了生成JavaDoc使用。
4.自定义实现
接下来就可以使用自定义的doclet来进行JavaDoc生成的控制了。自定义的doclet类不需要继承任何类或者实现任何接口,其中只需要有一个方法即可:public static boolean start (RootDoc root) {}。该方法的意思也比较明确,从根Doc开始进行操作。只有这个方法想要使用sun标准的参数是不行的,即使用不了除去标准doclet参数之外的,为了实现目的需要加两个方法:
public static boolean start(RootDoc root) { return Standard.start((RootDoc)process(root, RootDoc.class)) } public static boolean validOptions(String[][] options, DocErrorReporter reporter) { return Standard.validOptions(options, reporter); } public static int optionLength(String option) { return Standard.optionLength(option); }
在实际判断的时候是通过一个递归方法来返回所需要的doc。在该方法中实现具体逻辑,来判断哪些是包含特殊的tag的,这些doc需要生成JavaDoc,tags(String)方法可以获取到对应所有tag。拿到的doc类型有ClassDoc,FieldDoc,MethodDoc等。其中MemberDoc可以代表字段,方法构造器这一类的doc。接着ClassDoc里有获取其中字段,方法,构造器,内部类的doc的方法。合理使用这些类和方法来实现目标逻辑。
5.使用
首先如果子模块有lib依赖要在build.gradle文件中配置hasLibDependecies为true。其次如果依赖了第三方的库请把对应的jar包放在父工程的lib文件夹下。把自定义的doclet类放在父工程的java source文件夹下。
在JavaDoc中使用@IncludeIntoJavadoc这个tag来决定哪些类或者方法、字段等的JavaDoc生成。并且如果在类层次使用了,请不要在类中的JavaDoc中使用。或者直接在类中使用而不在类上使用。
PS:有需要自定义docletFileter类的可以留言邮箱。之后有时间会上传至GitHub