自己在公司实习的时候是负责后台开发,自己在编写完接口之后还需要对外提供接口文档,接口文档的形式也在自己手里经历了以下几个阶段的演化。 第一阶段:使用的是excel,通过excel的链接将接口和参数描述链接在一起,这样可维护性极差:(1)一个方法要加一个参数,或者减一个参数,那个excel的参数页就整体上移或者下移,于是,就要把所有方法的链接都改一遍。(2)前端开发一不小心就点到其他方法的参数描述中,开发时不是频繁交流,就是靠猜。成本极大 第二阶段:后来摒弃这种低级的excel形式,以html形式展示给前端,一个方法一个页面,再用一个页面A将所有方法的页面以超链接的形式整合起来,这样点击页面A的每个方法名称,就能跳转到对应的方法描述中。这样前端确实省心了不少,但是给后端带来很大的工作量:(1)我写一个接口,我就要写一个html,尤其是当参数很多时,我就要不断的复制粘贴,既繁琐又无聊。(2)一个标签写错,整个页面就错了,还要花费很多时间去debug,时间成本太大。 第三阶段:这样持续一段时间后,自己实在受不了,就想着能不能通过代码来自动生成这些文档。想起来java帮助文档的生成形式,然后再网上查阅了一些资料,知道了javadoclet,自己尝试着写了一个自定义的doclet——doclet1,利用这个自定义的doclet中生成接口文档。(自己还顺便帮Android端生成访问url的类,Android开发的人应该很烦按照接口文档中的入参生成对应的类,然后进行url访问吧,毕竟一不小粘错,又要debug好久) 第四阶段:在最开始的doclet1中,自己将所有内容都写在了里面,包含输出地址,html内容,等等。耦合性太强:当需要修改页面样式或者布局的时候,就需要修改doclet1代码。后来自己将html模板和java模板从中抽出,利用freemarker定制自己的模板,做了自己的第二代doclet——doclet2。 第五阶段:依照之前的思路,自己想如何将那些自定义标签,输入输出路径等也从中抽出来。这些内容以什么形式来展示,如何与doclet,freemarker整合,参考着HtmlDoclet源码来设计。 接下来的内容,我将依照自己从开始都现在自己所接触到的内容依次进行阐述,大致内容为:java Doclet概述,javadoc命令简单说明,freemark概述,第五阶段doclet设计思路
Doclet 是用 JavaTM编程语言编写的程序,它用 doclet API 指定 Javadoc 工具的输出内容和格式。缺省情况下,由Standard类 来生成 HTML 形式的 API 文档(在Standard的start方法中调用了HtmlDoclet的start方法:return HtmlDoclet.start(var0))。用户可以利用 doclet API从头开始编写 自定义的doclet,也可以对标准 doclet 进行修改,以适合自己的需要。 下面是创建和使用自己的 doclet 的基本步骤: 1.编写用来构成 doclet 的 Java 程序。为使用 doclet API,该程序应导入 com.sun.javadoc.*。程序的入口是一个带有 public static boolean start 方法的类,它将 RootDoc 作为参数。 2.编译 doclet。 3.用 -doclet 选项运行 javadoc 工具,生成 doclet 指定的输出。如果运行 javadoc 时未使用 -doclet 命令行选项,则按缺省状态的标准 doclet 生成 HTML 格式的 API 文档。 值得注意的是:doclet API 类文件在 JDK 软件的 lib/tools.jar 文件中,所以当编译 doclet 和使用自定义 doclet 时,tools.jar 必须在类路径上。为此,可以对 javac 和 javadoc 使用 -classpath 选项。 (包 com.sun.javadoc 由定义 doclet API 的接口组成。JDKTM 软件的 lib/tools.jar 文件包含这些接口及实现这些接口的类的私有包。tools.jar 文件还包括实现标准 doclet 的类。)
举个简单的doclet例子,让我们可以对doclet的运行方式有所了解: inport com.sun.javadoc.*; public class listClass{ public static boolean start(RootDoc root){ ClassDoc[] classes = root.classes(); for(int i=0 ; i < classex.length ; i+=){ System.out.println(classes[i]); } return true; } }该 doclet 将取出 Javadoc 所操作的一些类,然后将它们的名称输到标准输出上。 就该 doclet 而言,首先应注意到: 1.为使用 doclet API,程序导入了 com.sun.javadoc.*。 2.对于所有 doclets,入口点均为 public static boolean start 方法。 3.start 方法将 RootDoc 作为参数使用。该参数携带运行 javadoc 时命令行指定的所有选项信息,以及 Javadoc 所操作的类和包的信息。 接下来,编译listClass doclet,正如前面所说,编译时需要加上tools.jar的位置,对本例如下:
javac -classpath D:\ProgramFileZhaoShiqiang\java\jdk1.7\lib\tools.jar listClass.java编译完成后,为了运行listClass doclet,必须用javadoc的doclet标记指向编译后的doclet,例如,要在testClass上运行doclet,需要使用命令:
javadoc -doclet listClass -classpath D:\ProgramFileZhaoShiqiang\java\jdk1.7\lib\tools.jar testClass.java输出结果将是字符串 “testClass”。注意:该命令也要求类路径中包含 tools.jar。 关于命令行选项的说明,如果运行javadoc -help,就会看到javadoc工具有两个命令行选项集。一个选项集是普通选项集,它适用于任何 doclet。另一个则专门用于标准 doclet。下面我将其中部分重要的命令行选项进行说明 (javadoc可以自定义标签和自定义命令行选项,这里就不展开讨论了,如果大家有兴趣可以参考http://dadi520.iteye.com/blog/545897,作为简单介绍doclet,这篇博客很不错,上面大部分内容也是摘自这篇博客。只不过自己写的doclet从start之后就完全重新设计了)
在命令行中运行javadoc -help命令,将显示javadoc命令行语法,如下: javadoc [ options ] [ packagenames ] [ sourcefiles ] [ @files ] 参数可以按照任意顺序排列,大多数参数都简单易懂,这节最后我会提几个参数的注意事项 Packagenames 包列表。这个选项可以是一系列的包名(用空格隔开),例如java.lang java.lang.reflect java.awt。不过,因为javadoc不递归作用于子包,不允许对包名使用通配符;所以你必须显示地列出希望建立文档的每一个包。 Sourcefiles 源文件列表。这个选项可以是一系列的源文件名(用空格隔开),可以使用通配符。javadoc允许四种源文件:类源代码文件、包描述文件、总体概述文件、其他杂文件。 @files 包含文件。为了简化javadoc命令,你可以把需要建立文档的文件名和包名放在一个或多个文本文件中。例如,为了简化下面命令: javadoc -d apidoc com.mypackage1 com.mypackage2 com.mypackage3 你可以建立一个名称为mypackage.txt的文件,其内容如下: com.mypackage1 com.mypackage2 com.mypackage3 然后执行下面命令即可: javadoc -d apidoc @mypackage.txt 注: 1.-sourcepath sourcepathlist 指定包的源文件搜索路径。但是必须注意,只有在javadoc命令中指定了包名的时候才可以使用-sourcepath选项。如果指定了包名,而省略了- sourcepath,那么javadoc使用类路径查找源文件。 举例说明:假定你打算为com.mypackage建立文档,其源文件的位置是C: usersrc。那么你可以使用下面的命令: javadoc -sourcepath c:usersrc com.mypackage 2. -classpath classpathlist 指定javadoc查找”引用类”的路径。引用类是指带文档的类加上它们引用的任何类。javadoc将搜索指定路径的所有子目录。 Classpathlist可以包含多个路径(使用;隔开)。如果省略-classpath,则javadoc使用-sourcepath查找源文件和类 文件。 举例说明:假定你打算为com.mypackage建立文档,其源文件的位置是C:usersrc,包依赖C:userlib中的库。那么你可以使 用下面的命令: javadoc -classpath c:userlib -sourcepath c:usersrc com.mypackage (更多详细命令,可以参考博客http://blog.chinaunix.net/uid-725717-id-2060139.html)
正如前面介绍的,自定义doclet,需要有public static boolean start(RootDoc root)是如果只有这个方法,那么我们只能使用javadoc规定的参数(即普通选项集中的参数),不能使用sun标准的doclet参数(即标准选项集中的参数),比较sun的HtmlDoclet源码,就会发现,只要增加下面2个方法就可以使用标准选项集中的参数:
public static int optionLength(String option) { // Construct temporary configuration for check return (ConfigurationImpl.getInstance()).optionLength(option); } public static boolean validOptions(String options[][], DocErrorReporter reporter) { // Construct temporary configuration for check return (ConfigurationImpl.getInstance()).validOptions(options, reporter); }看过第一节中提及的博客的人应该熟悉在两个函数,这两个函数就是为了扩展命令行选项的。
最后介绍一下自己自定义的标签: 类相关的注解有: 类的描述:description 类的访问路径:uri 方法相关的注解有 方法访问路径:uri 方法类别(读方法or写方法):type 方法描述:description UE中如何找到接口:path 支持格式(默认json):datatype 请求方式(默认post): 传入参数描述:param 注意事项(默认无):notice 返回的json格式:returnjson 输出参数描述:returnparam
类相关的注解使用如下:
/** * @uri 2.0/dynamic * @description 动态类 * @author zhaoshiqiang@jchvip.com */ @Controller @RequestMapping("/2.0/dynamic") public class DynamicController { }方法相关的注解使用如下:
/** * @uri publish * @description 发布动态 * @type write * @path 发布动态 * @param comment~true~string~动态内容 * @param userid~true~string~用户id * @param imgs~false~list(array)~发动态上传的图片时,不同图片用逗号分隔 * @returnparam id~int~发布动态的id * @returnjson { "status": 0, "data": { "id":1 } } */ @RequestMapping("publish") @ResponseBody public String publish(HttpServletRequest request, HttpServletResponse response,String comment,String userid,String imgs){ return null; }最后把自己写得第一代doclet1贴上,大家做个参考(有400+行代码都在一个文件中,维护起来很困难,这就是之后自己为什么要重构了,大家看大致框架就好):
import com.sun.javadoc.*; import com.sun.tools.doclets.formats.html.ConfigurationImpl; import java.io.*; import java.util.ArrayList; /** * @mytag class mytag */ public class doclet1{ //类相关的注解 private static String CLASS_DESCRIBE = "description"; //类的描述 private static String CLASS_URI = "uri"; //类的访问路径 //方法相关的注解 private static String METHOD_URI = "uri"; //方法访问路径 private static String METHOD_TYPE = "type"; //方法类别 private static String METHOD_DESCRIBE = "description"; //方法描述 private static String METHOD_PATH = "path"; //UE中如何找到接口 private static String METHOD_SUPPORT = "datatype"; //支持格式(json) private static String METHOD_REQUESTTYPE = "method"; //请求方式(get/post) private static String METHOD_PARAM = "param"; //传入参数描述,描述格式为:名称~必选~类型~说明(若为非必选,须说明什么情况下不选) private static String METHOD_NOTICE = "notice"; //注意事项 private static String METHOD_RETURNJSON = "returnjson"; //返回的json格式 private static String METHOD_RETURNPARAM = "returnparam"; //输出参数描述,描述格式为:名称~类型~字段说明 private static String HTML_CSS = "<style type=\"text/css\"> body{font: 12px/1.125 Arial, Helvetica, sans-serif; } .wiki_title{line-height: 37px; border-bottom: 1px " + "solid #e5e5e5; margin: 16px 0 8px 0; font-size: 20px; color: #333; font-family: \"Microsoft Yahei\"; font-weight: 300; } h1." + "wiki_title{font-size: 24px; } a{color: #3c7cb3; text-decoration: none; } table{border-collapse: collapse; border-spacing: 0;" + " } table.parameters{border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; -" + "webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; width: 100%; } th,td{text-align: center; font-" + "weight: bolder; border: 1px solid #cccccc; height: 20px; } .code_type{text-transform: uppercase; margin-bottom: 5px; display" + ": inline-block;* display: inline;* zoom: 1; background: #b4e3b4; border-radius: 2px; color: #008200; padding: 2px 8px; } a:" + "hover{text-decoration: underline; } </style> "; private static String methodUri; private static String methodType; private static String methodDescribe; private static String methodPath; private static String methodSupport; private static String methodRequesttype; private static ArrayList<String> methodParam; private static String methodNotice; private static String methodReturnJson; private static ArrayList<String> methodReturnParam; private static String CLASSITEM="<table class = \"custom\"><colgroup><col class = \"tbF1\"><col class = \"tbF2\"><col></colgroup><tr><th colspan=\"3\">CLASS_DESCRIBE</th></tr>"; private static String METHODITEMHEAD="<tr><td rowspan=\"METHOD_TYPE_NUM\">METHOD_TYPE</td><td><a href=\"CLASS_URI/METHOD_URI.html\">CLASS_URI/METHOD_URI</a></td>"+ "<td>METHOD_PATH</td></tr>"; private static String METHODITEM="<tr><td><a href=\"CLASS_URI/METHOD_URI.html\">CLASS_URI/METHOD_URI</a></td>"+ "<td>METHOD_PATH</td></tr>"; private static String DIRPATH="D:/api/"; private static String SRCNAME = "com.bluemobi.bluecollar.network"; private static String MAPOPERATION ="map.put(\"PARAM\", PARAM);"; private static String APIPATH = "private static final String APIPATH = \"URI\";"; private static String packagename="package SRCNAME.request;"; private static String importname = "import java.util.HashMap;\nimport java.util.Map;\nimport com.android.volley.Response.ErrorListener;\nimport com.android.volley.Response.Listener;\nimport SRCNAME.LlptHttpJsonRequest;\nimport SRCNAME.response.METHODNAMEResponse;"; private static String methodname = "public class METHODNAMERequest extends LlptHttpJsonRequest<METHODNAMEResponse> {"; private static String constructor1 = "public METHODNAMERequest(Listener<METHODNAMEResponse> listener, ErrorListener errorListener) {\n" + "\t\tsuper(Method.POST, APIPATH, listener, errorListener);\n" + "\t}"; private static String constructor2 = "public METHODNAMERequest(int method, String partUrl, Listener<METHODNAMEResponse> listener, ErrorListener errorListener) {\n" + "\t\tsuper(method, partUrl, listener, errorListener);\n" + "\t}"; private static String GetApiPath = "public String GetApiPath() {return APIPATH;}"; private static String getResponse = "public Class<METHODNAMEResponse> getResponseClass() {return METHODNAMEResponse.class;}"; private static String paramModel = "private String PARAM;\n" + "public String getPARAM() {return PARAM;}\n" + "public void setPARAM(String PARAM) {this.PARAM = PARAM;}"; private static String GetParameters ="public Map<String, String> GetParameters() {\n" + "\t\tMap<String, String> map = new HashMap<String, String>();\n" + "mapOperation"+ "\n\t\treturn map;\n" + "\t}"; /** * @mytag start mytag * @param root */ public static boolean start(RootDoc root) { try { doc(root.classes()); } catch (Exception e) { e.printStackTrace(); } return true; } private static String doc_class(ClassDoc classDoc, String TAG) { Tag[] Class_Describe = classDoc.tags(TAG); if (Class_Describe.length != 0) { return Class_Describe[0].text(); }else { return null; } } private static String docMethod(MethodDoc methodDoc, String TAG) { Tag[] mcts = methodDoc.tags(TAG); if (mcts.length != 0) return mcts[0].text(); else return null; } private static ArrayList<String> docMethodList(MethodDoc methodDoc, String TAG) { ArrayList<String> params = new ArrayList<String>(); Tag[] mcts = methodDoc.tags(TAG); for (int i=0; i < mcts.length ; i++) { params.add(mcts[i].text()); } return params; } /** * @mytag doc mytag * @param classDocs */ private static void doc(ClassDoc[] classDocs) throws Exception { BufferedWriter write=null; for (int i = 0; i < classDocs.length; i++) { String class_describe=doc_class(classDocs[i], CLASS_DESCRIBE); String class_uri=doc_class(classDocs[i], CLASS_URI); int writenum = 0; int readnum = 0; StringBuffer writediv = new StringBuffer(); //写入块的存储 StringBuffer readdiv = new StringBuffer(); //读取块的存储 String classitem=CLASSITEM.replaceAll("CLASS_DESCRIBE",class_describe); //替换该字符串中的方法描述 StringBuffer destdirname= new StringBuffer(); destdirname.append(DIRPATH).append(doc_class(classDocs[i], CLASS_URI)); File dir = new File(destdirname.toString()); if (dir.exists()) { System.out.println("创建目录" + destdirname.toString() + "失败,目标目录已经存在"); } //创建目录 if (dir.mkdirs()) { System.out.println("创建目录" + destdirname.toString() + "成功!"); } else { System.out.println("创建目录" + destdirname.toString() + "失败!"); } MethodDoc[] methods = classDocs[i].methods(); for(int j = 0; j< methods.length; j++){ StringBuffer HTMLString = new StringBuffer(); StringBuffer paramstring = new StringBuffer(); StringBuffer parammap = new StringBuffer(); String method_uri=docMethod(methods[j], METHOD_URI); String method_path=docMethod(methods[j],METHOD_PATH); String method_type=docMethod(methods[j], METHOD_TYPE); //获取uri后即可得函数名称,API路径,构造函数, String packagename1 = packagename.replaceAll("SRCNAME",SRCNAME); System.out.println(class_uri+"+"+method_uri); String importname1=importname.replaceAll("METHODNAME",method_uri); importname1=importname1.replaceAll("SRCNAME",SRCNAME); String methodname1 = methodname.replaceAll("METHODNAME",method_uri); String construct1 = constructor1.replaceAll("METHODNAME",method_uri); String construct2 = constructor2.replaceAll("METHODNAME",method_uri); String getresponsecalss1 = getResponse.replaceAll("METHODNAME",method_uri); String uri = "/"+class_uri+"/"+method_uri; String apipath = APIPATH.replaceAll("URI",uri); //首页数据的写入 if (method_type.equals("write")) { writenum++; if (writenum == 1) { String methoditemhead=METHODITEMHEAD.replaceAll("METHOD_TYPE_NUM","writenum"); methoditemhead=methoditemhead.replaceAll("CLASS_URI",class_uri); methoditemhead=methoditemhead.replaceAll("METHOD_URI",method_uri); methoditemhead=methoditemhead.replaceAll("METHOD_PATH",method_path); methoditemhead=methoditemhead.replaceAll("METHOD_TYPE", "写入接口"); writediv.append(methoditemhead); }else { String methoditem = METHODITEM.replaceAll("CLASS_URI",class_uri); methoditem = methoditem.replaceAll("METHOD_URI",method_uri); methoditem = methoditem.replaceAll("METHOD_PATH",method_path); writediv.append(methoditem); } } else if (method_type.equals("read")) { readnum++; if (readnum == 1) { String methoditemhead=METHODITEMHEAD.replaceAll("METHOD_TYPE_NUM","readnum"); methoditemhead=methoditemhead.replaceAll("CLASS_URI",class_uri); methoditemhead=methoditemhead.replaceAll("METHOD_URI",method_uri); methoditemhead=methoditemhead.replaceAll("METHOD_PATH",method_path); methoditemhead=methoditemhead.replaceAll("METHOD_TYPE", "读取接口"); readdiv.append(methoditemhead); }else { String methoditem = METHODITEM.replaceAll("CLASS_URI",class_uri); methoditem = methoditem.replaceAll("METHOD_URI",method_uri); methoditem = methoditem.replaceAll("METHOD_PATH",method_path); readdiv.append(methoditem); } } HTMLString.append("<!DOCTYPE html> <html> <meta charset = utf8 > <head> <title></title> </head>"); HTMLString.append(HTML_CSS); HTMLString.append("<body>"); HTMLString.append("<h1 class=\"wiki_title\"><span class=\"mw-headline\">"); HTMLString.append(class_uri+"/"); HTMLString.append(method_uri); HTMLString.append("</span></h1>"); HTMLString.append("<p>"); HTMLString.append(docMethod(methods[j], METHOD_DESCRIBE)); HTMLString.append("</p>"); HTMLString.append("<h2 class=\"wiki_title\"> <span class=\"mw-headline\">URL</span> </h2>"); HTMLString.append("<p> <span style=\"text-transform:lowercase;font-weight:600\"> <a rel=\"nofollow\" class=\"external free\" href=\"\">http://test.jchvip.net/"); HTMLString.append(class_uri+"/"); HTMLString.append(method_uri); HTMLString.append("</a> </span> </p>"); HTMLString.append("<h2 class=\"wiki_title\"> <span class=\"mw-headline\" >支持格式</span> </h2>"); HTMLString.append("<p> <span style=\"text-transform:uppercase;font-weight:600\">"); HTMLString.append(docMethod(methods[j], METHOD_SUPPORT)); HTMLString.append("</span> </p>"); HTMLString.append("<h2 class=\"wiki_title\"> <span class=\"mw-headline\" >HTTP请求方式</span> </h2> "); HTMLString.append("<p> <span style=\"text-transform:uppercase;font-weight:600\">"); HTMLString.append(docMethod(methods[j], METHOD_REQUESTTYPE)); HTMLString.append("</span> </p>"); HTMLString.append("<h2 class=\"wiki_title\"> <span class=\"mw-headline\" >请求参数</span> </h2>"); HTMLString.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" class=\"parameters\" style=\"border-color: #CCCCCC;\"><tbody>"); HTMLString.append("<tr> <th width=\"10%\" style=\"text-align:center;font-weight:bolder;border:1px solid #cccccc\">名称</th> <th width=\"5%\" style=\"text-align:center;font-weight:bolder;border:1px solid #cccccc\">必选</th> <th width=\"10%\" style=\"text-align:center;font-weight:bolder;border:1px solid #cccccc\">类型及范围</th> <th width=\"75%\" style=\"text-align:center;font-weight:bolder;border:1px solid #cccccc\">说明</th> </tr>"); ArrayList<String> params = docMethodList(methods[j], METHOD_PARAM);//list中每一项对应一个请求参数的数据 for (int k=0; k<params.size();k++) { String param = params.get(k); //获取每一个参数数据 String[] options = param.split("~"); //拆分需要的数据 HTMLString.append("<tr>"); HTMLString.append("<td style=\"text-align:center;font-weight:bolder;border:1px solid #cccccc\">"); HTMLString.append(options[0]); //名称 //每个参数即对应每个属性,对其进行操作 parammap.append(MAPOPERATION.replaceAll("PARAM",options[0])); //对getparameters中的map添加参数 paramstring.append(paramModel.replaceAll("PARAM",options[0])); //生成属性和其对应的get,set方法 HTMLString.append("</td>"); HTMLString.append("<td style=\"text-align:center;text-transform:lowercase;border:1px solid #cccccc\">"); HTMLString.append(options[1]); //必选 HTMLString.append("</td>"); HTMLString.append("<td style=\"text-align:left;padding-left:5px;border:1px solid #cccccc\">"); HTMLString.append(options[2]); //类型 HTMLString.append("</td>"); HTMLString.append("<td style=\"text-align:left;padding-left:5px;border:1px solid #cccccc\">"); HTMLString.append(options[3]); //说明 HTMLString.append("</td>"); HTMLString.append("</tr>"); } HTMLString.append("</tbody> </table>"); HTMLString.append("<h2 class=\"wiki_title\"> <span class=\"mw-headline\">返回结果</span> </h2>"); HTMLString.append("<div class=\"code_type\" style=\"text-transform:uppercase;margin-bottom:5px;\">JSON示例</div>"); HTMLString.append("<pre>" + docMethod(methods[j], METHOD_RETURNJSON) + "</pre>"); HTMLString.append("<h2 class=\"wiki_title\"> <span class=\"mw-headline\">返回字段说明</span> </h2>"); HTMLString.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" class=\"parameters\" style=\"border-color: #CCCCCC;\"> <tbody>"); HTMLString.append("<tr> <th width=\"25%\" style=\"text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc\">返回值字段</th> <th width=\"15%\" style=\"text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc\">字段类型</th> <th width=\"60%\" style=\"text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc\">字段说明</th> </tr>"); ArrayList<String> returnparams = docMethodList(methods[j], METHOD_RETURNPARAM);//list中每一项对应一个返回参数的数据 for (int k=0; k<returnparams.size();k++) { String returnparam = returnparams.get(k); //获取每一个参数数据 System.out.println(returnparam); String[] options = returnparam.split("~"); //拆分需要的数据 HTMLString.append("<tr>"); HTMLString.append("<td style=\"text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc\">"); HTMLString.append(options[0]); //名称 HTMLString.append("</td>"); HTMLString.append("<td style=\"text-align:left;padding-left:5px;text-transform:lowercase;border:1px solid #cccccc\">"); HTMLString.append(options[1]); //类型 HTMLString.append("</td>"); HTMLString.append("<td style=\"text-align:left;padding-left:5px;border:1px solid #cccccc\">"); HTMLString.append(options[2]); //说明 HTMLString.append("</td>"); HTMLString.append("</tr>"); } HTMLString.append("</tbody> </table>"); HTMLString.append("<h2 class=\"wiki_title\"> <span class=\"mw-headline\" >注意事项</span> </h2>"); String notice = docMethod(methods[j], METHOD_NOTICE); if (notice != null && "".equals(notice)) { HTMLString.append("<p>" + notice + "</p>"); }else { HTMLString.append("<p>无</p>"); } HTMLString.append("</body> </html>"); // System.out.println(HTMLString.toString()); //每个方法对应的html文件 StringBuffer filenamepath = new StringBuffer(); filenamepath.append(destdirname.toString()); filenamepath.append("/"); filenamepath.append(methods[j].name()); filenamepath.append(".html"); //每个方法对应的java文件 StringBuffer javapath = new StringBuffer(); javapath.append(destdirname.toString()); javapath.append("/"); javapath.append(methods[j].name()); javapath.append(".java"); // System.out.println(filenamepath.toString()); StringBuffer javacode = new StringBuffer(); javacode.append(packagename1); javacode.append("\n"); javacode.append(importname1); javacode.append("\n"); javacode.append(methodname1); javacode.append("\n"); javacode.append(apipath); javacode.append("\n"); javacode.append(paramstring.toString()); javacode.append("\n"); javacode.append(construct1); javacode.append("\n"); javacode.append(construct2); javacode.append("\n"); javacode.append(getresponsecalss1); javacode.append("\n"); javacode.append(GetApiPath); javacode.append("\n"); String getparameters1 = GetParameters.replaceAll("mapOperation",parammap.toString()); javacode.append(getparameters1); javacode.append("\n"); javacode.append("}"); //输出html文件 // System.out.println(filenamepath.toString()); File writefile = new File(filenamepath.toString()); write = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(writefile),"UTF-8")); write.write(HTMLString.toString()); write.flush(); //输出java文件 File javafile = new File(javapath.toString()); write = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(javafile),"UTF-8")); write.write(javacode.toString()); write.flush(); } String readstring = readdiv.toString().replaceAll("readnum",String.valueOf(readnum)); String writestring = writediv.toString().replaceAll("writenum",String.valueOf(writenum)); StringBuffer indexnamepath = new StringBuffer(); indexnamepath.append(DIRPATH); indexnamepath.append(class_uri); indexnamepath.append(".html"); File indexfile = new File(indexnamepath.toString()); write = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexfile),"UTF-8")); // System.out.println(classitem+readstring + writestring+"</table>"); write.write(classitem+readstring + writestring+"</table>"); write.flush(); } if (write != null) write.close(); } public static int optionLength(String option) { // Construct temporary configuration for check return (ConfigurationImpl.getInstance()).optionLength(option); } public static boolean validOptions(String options[][], DocErrorReporter reporter) { // Construct temporary configuration for check return (ConfigurationImpl.getInstance()).validOptions(options, reporter); } }1.http://dadi520.iteye.com/blog/545897 2.http://blog.chinaunix.net/uid-725717-id-2060139.html