上一篇我们对freemarker及其使用方式做了简单的介绍,最后展示了自己是如何将模板的生成从第一代doclet中抽出来。在最后展示的doclet2中我们可以看到有以下缺陷:
这一篇将针对上面所列的问题,介绍自己是如何将注解名称从代码中抽出,如何设计model层的数据结构,如何将数据及文件的生成从doclet中抽出来,及针对freemarker模板针对这些数据结构所做的修改。于是有了第三代doclet——doclet3。
这个doclet3是可以用的,之后再这个基础上加上异常提示之后会继续更新。
这些注解名称需要用户自己去定义,并按照一定的格式要求写在一个外部文件中,在doclet的入口处即start中读进来并解析。那么,这些注解名称该在一个什么样的形式展示出来呢?自己最开始设想有三种:
最后选择了以xml的形式来展示,理由如下:
确定以xml方式展示后,紧接着面临着下一个问题:xml内的结构和内容怎么设计?
要解决这个问题,我们得先确定doclet需要从这个xml中知道注解的什么信息:
确定这些后,再来思考如何展示这些信息:
1. 我们需要告诉doclet无非是三类信息:包的相关信息,类的相关信息,方法的相关信息。除此之外,我们需要注意的是:doclet是以包为单位读入注解的(即使设置了多个包名,也是一个包一个包的读入注解),而在一个包中能写注解的地方只有在类和方法上。于是,设计doclat标签作为根标签,在doclet下,设计三个标签:package,class,method分别展示包、类、方法的相关信息,在class和method标签下,设计tag标签来展示一个注解的相关信息,tag标签可以有多个,这样我们可以确定一个大致的xml框架如下:
<doclet>
<package>
package>
<class>
<tag>tag>
<tag>tag>
class>
<method>
<tag>tag>
<tag>tag>
method>
doclet>
2.在每个tag标签下,设计name标签来告诉doclet有哪些注解,设计type标签来标识注解的格式:string表示是字符串,split表示需要拆分。设计symbo标签来展示拆分的符号,设计item标签来展示拆分后每一项对应的名称(item项要按照顺序写出来,如下面xml展示的param,表示有一个方法注解,名称为param,它是以name~select~type~explain顺序来展示的,如在某个方法上写@param userid~true~string~用户id),值得注意的是:name标签,item标签中的内容将作为map的key存放在map中,再讲其传给freemarker模板,所以name标签和item标签的值要与模板中相应值对应。这样设计的xml如下:
<doclet>
<package>
package>
<class>
<tag>
<name>uriname>
<type>stringtype>
tag>
class>
<method>
<tag>
<name>paramname>
<type>splittype>
<symbol>~symbol>
<item>nameitem>
<item>selectitem>
<item>typeitem>
<item>explainitem>
tag>
<tag>
<name>uriname>
<type>stringtype>
tag>
method>
doclet>
3.参考html表单提交时,所有的标签都是以list形式提交,相对应的,我把所有的注解都当成多个注解来处理,在代码中对应的为一个list,单个注解就是一个长度为1的list。这个在设计freemarker时一定要注意
4.参考web.xml中servlet的展示,相对应的,在package,class,method标签下,设计viewpath标签来表示模板的信息,在每个viewpath下,设计name标签标识模板的名称(在model设计中其作为map的key存放),value标签标识模板的路径。viewpath标签可以有多个,也可以没有,若有多个,则多个viewpath的name不能相同。
模板的路径有了,那么模板的输出又怎么来获取呢?在package标签中,设计basepath标签来标识这个包下所有文件路径(如果有方法或者类的输出路径,也包括这些路径)的基本路径,所有输出路径都是相对于这个路径而已的(这个标签是必须的),设计outpath标签来标识模板的输出信息,设计name标签与viewpath下的name标签相同,设计value标签来标识相对于basepaht路径的文件路径,在method和class下,由于class和method对应的模板输出路径要在注解中指定,所有输出路径是写在tag标签中的,name标签要和对应的viewpath标签的那么相同,type标签是固定值:string。设计的注解xml如下:
这里写代码片<doclet>
<package>
<outpath>
<name>packagehtmlname>
<value>2.0.htmlvalue>
outpath>
<viewpath>
<name>packagehtmlname>
<value>D:/model/index.ftlvalue>
viewpath>
<basepath>d:/api2basepath>
package>
<class>
<tag>
<name>uriname>
<type>stringtype>
tag>
class>
<method>
<tag>
<name>paramname>
<type>splittype>
<symbol>~symbol>
<item>nameitem>
<item>selectitem>
<item>typeitem>
<item>explainitem>
tag>
<tag>
<name>uriname>
<type>stringtype>
tag>
<tag>
<name>htmlname>
<type>stringtype>
tag>
<tag>
<name>javaname>
<type>stringtype>
tag>
<viewpath>
<name>htmlname>
<value>D:/model/Method.ftlvalue>
viewpath>
<viewpath>
<name>javaname>
<value>D:/model/javaTemplate.ftlvalue>
viewpath>
method>
doclet>
如上所示,每个方法有两个模板,其位置分别为D:/model/Method.ftl和D:/model/javaTemplate.ftl,对应的输出文件是在方法上@java和@html这两个注解标识的,包对应的有一个模板,其位置为D:/model/index.ftl,模板的输出文件位置为d:/api2/2.0.html
在标识整个注解信息的xml设计完成之后,doclet该如何知道这个xml的位置呢?
考虑到这个xml需要在程序最开始的时候读入,并且其位置信息还需要用户输入。于是很自然的想到了doclet的自定义标签(关于如何自定义doclet标签,可以参考博客《java doclet概述》)。这里我自定义了-xmlpath标签来标识这个xml的位置。这个xml的名称为xmltest.xml,位置为d:\,内容如下:
<doclet>
<package>
<outpath>
<name>packagehtmlname>
<value>2.0.htmlvalue>
outpath>
<viewpath>
<name>packagehtmlname>
<value>D:/model/index.ftlvalue>
viewpath>
<basepath>d:/api2basepath>
package>
<class>
<tag>
<name>uriname>
<type>stringtype>
tag>
<tag>
<name>descriptionname>
<type>stringtype>
tag>
class>
<method>
<tag>
<name>paramname>
<type>splittype>
<symbol>~symbol>
<item>nameitem>
<item>selectitem>
<item>typeitem>
<item>explainitem>
tag>
<tag>
<name>returnparamname>
<type>splittype>
<symbol>~symbol>
<item>nameitem>
<item>typeitem>
<item>explainitem>
tag>
<tag>
<name>uriname>
<type>stringtype>
tag>
<tag>
<name>descriptionname>
<type>stringtype>
tag>
<tag>
<name>typename>
<type>stringtype>
tag>
<tag>
<name>returnjsonname>
<type>stringtype>
tag>
<tag>
<name>pathname>
<type>stringtype>
tag>
<tag>
<name>methodnamename>
<type>stringtype>
tag>
<tag>
<name>htmlname>
<type>stringtype>
tag>
<tag>
<name>javaname>
<type>stringtype>
tag>
<viewpath>
<name>htmlname>
<value>D:/model/Method.ftlvalue>
viewpath>
<viewpath>
<name>javaname>
<value>D:/model/javaTemplate.ftlvalue>
viewpath>
method>
doclet>
模板的内容我会在第四节来阐述,需要生成文档的目标java文件为text.java,位置为d:\,内容如下:
package com.jchvip.jch2.appInterface;
import com.bluemobi.common.util.AppResult;
import com.bluemobi.common.util.ValidateUtil;
import com.jchvip.jch2.service.MessageManagerServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* @description 消息类
* @uri 2.0/message
*/
@Controller
@RequestMapping(value = "2.0/message", method= {RequestMethod.POST,RequestMethod.GET} ,produces="text/html;charset=UTF-8")
public class MessageController {
/**
* @uri count/userandmax
* @type read
* @path 根据用户id和消息最大id获取各种类型消息数目
* @html MessageController/userandmax.html
* @java MessageController/userandmax.java
* @description 根据用户id和消息最大id获取各种类型消息数目
* @methodname userandmax
* @param userid~true~string~用户id
* @param maxid~true~int~最大id
* @returnparam data中的key~int~消息类型,other 其他;like 点赞;comment 评论
* @returnparam data中的value~int~多少条未读消息
* @returnjson
*{
{
"status": 0,
"serverTime": "160819101001",
"data": {
"other": 21,
"comment": 1,
"like": 1
}
}
*/
@RequestMapping("/count/userandmax")
@ResponseBody
public String messagecountuserandmax(HttpServletRequest request,HttpServletResponse response,String userid,Integer maxid){
return null;
}
/**
* @uri userandtypeandmax
* @type read
* @path 根据用户id和消息最大id和消息类型获取用户消息
* @html MessageController/userandtypeandmax.html
* @java MessageController/userandtypeandmax.java
* @description 根据用户id和消息最大id和消息类型获取用户消息
* @methodname userandtypeandmax
* @param userid~trur~string~用户id
* @param maxid~true~int~消息最大id
* @param classes~true~int~前端分类,1 点赞,2 评论,3 其他所有分类
* @returnparam content~string~消息内容
* @returnparam createtime~string~消息产生时间
* @returnparam id~int~消息id
* @returnparam title~string~消息标题
* @returnparam status~int~消息状态,2 已读,其他值 未读
* @returnparam type~int~消息类型,1 实名认证通过,2 加好友,3 同意好友,4 点赞, 5 评论,6 实名不通过,7 签约,8 报名,10 活源
* @returnparam classes~int~前端分类,1 点赞,2 评论,3 其他所有分类
* @returnjson
* {
"status": 0,
"serverTime": "160819100844",
"data": [
{
"content": "恭喜您,实名认证通过!",
"createtime": 1471489002000,
"id": 1,
"title": "实名认证通过",
"status": 0,
"type": 1,
"classes":3
}
]
}
*/
@RequestMapping("/userandtypeandmax")
@ResponseBody
public String messageuserandtypeandmax(HttpServletRequest request,HttpServletResponse response,String userid,Integer maxid,Integer classes){
return null;
}
/**
* @uri updatemessagestatus
* @type write
* @path 标识消息为已读
* @html MessageController/updatemessagestatus.html
* @java MessageController/updatemessagestatus.java
* @methodname updatemessagestatus
* @description 标识消息为已读
* @param messageid~true~int~要修改的消息id
* @returnjson
{
"status": 0,
"serverTime": "160819100844",
}
*/
@RequestMapping("/updatemessagestatus")
@ResponseBody
public String updatemessagestatus(HttpServletRequest request,HttpServletResponse response,Integer messageid){
return null;
}
/**
* @uri deletemessagebyid
* @type write
* @html MessageController/deletemessagebyid.html
* @java MessageController/deletemessagebyid.java
* @methodname deletemessagebyid
* @description 删除消息
* @path 删除消息
* @param messageid~true~int~要删除的消息id
* @returnjson
{
"status": 0,
"serverTime": "160819100844",
}
*/
@RequestMapping("/deletemessagebyid")
@ResponseBody
public String deletemessagebyid(HttpServletRequest request,HttpServletResponse response,Integer messageid){
return null;
}
}
于是,整个doclet命令为:
javadoc -doclet doclet3 -docletpath D:\doclet\out\doclet.jar -encoding utf-8 -charset utf-8 -xmlpath d:\xmltest.xml d:\text.java
在dos命令行直接输入上述命令就会在指定位置生成文件。
不难发现,整个model层对应结构大致为一个树型结构:一个包包含多个类,一个类包含多个方法和多个注解,一个方法包含多个注解。我先展示整个model的uml图,让大家有个整体的认识,然后再依次来分析。整个model层uml图如下:
设计tag来标识注解的种类(注意这里抽象的是注解的种类,而不是个数),其结构如下:
public List transformTagvalue(){
if (type == 1){
//string
return itemvalues;
}else if (type == 2){
//split
List<Map> tagvalues = new ArrayList<Map>();
for (String itemvalue : itemvalues){
Map<String,String> itemmap = new HashMap<String,String>();
String[] values = itemvalue.split(symbol);
for (int i =0; i[i]);
}
tagvalues.add(itemmap);
}
return tagvalues;
}else {
return null;
}
}
MethodType类需要实现Serializable接口,这是为了后面的深拷贝。
public Map<String,List> transformTagvalue(){
Map<String,List> map = new HashMap<>();
for (Tag tag : tagList){
map.put(tag.getName(),tag.transformTagvalue());
}
return map;
}
ClassType类需要实现Serializable接口,这是为了后面的深拷贝。
public Map<String,List> transformTagvalue(){
Map<String,List> map = new HashMap<>();
//转换class注解上的值
for (Tag tag : tagList){
map.put(tag.getName(),tag.transformTagvalue());
}
//转换class中method中的值
List methodvalues = new ArrayList();
for (MethodType methodType : methodTypeList){
methodvalues.add(methodType.transformTagvalue());
}
map.put("methods",methodvalues);
return map;
}
public Map<String,List> transformTagvalue(){
Map<String,List> map = new HashMap<>();
List classvalues = new ArrayList();
for (ClassType classType : classTypes){
classvalues.add(classType.transformTagvalue());
}
map.put("classes",classvalues);
return map;
}
在整个model层上,我又添加了三个类:HandleXml,TypeFactory,HandleView。主要是用来处理xml信息,model层下各个类的工厂,输出模板对应的文件。他们与model层的类的关系如下uml图所示:
这个类的作用是用来处理读入的xml信息,然后将对应的信息配置在TypeFactory中,使TypeFactory生成需要的类。
这个类中最重要的方法就是handlexml,在start方法(整个程序的入口)一开始便调用这个handlexml方法,入参是注解xml的地址。其代码如下:
public static void handlexml(String xmlAddress) throws DocumentException {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File(xmlAddress));
Element root = document.getRootElement();
//处理包的标签
Element packages = root.element("package");
//设置包模板路径
List packageviewpathes = packages.elements("viewpath");
TypeFactory.getPACKAGETYPE().setTemplate(handlePackagePath(packageviewpathes));
List packageOutpathes = packages.elements("outpath");
TypeFactory.getPACKAGETYPE().setOutpath(handlePackagePath(packageOutpathes));
Element packageBasepath = packages.element("basepath");
if (packageBasepath.getText().endsWith("/")){
TypeFactory.getPACKAGETYPE().setBasepath(packageBasepath.getText());
}else {
TypeFactory.getPACKAGETYPE().setBasepath(packageBasepath.getText()+"/");
}
//处理类的标签
Element classtags = root.element("class");
//设置类模板路径
List classPathes = classtags.elements("viewpath");
List
上述代码很简单,就是在读入xml后,分别读取包,类,方法的对应路径,然后将这个xml的信息配置在TypeFactory的三个静态属性上(TypeFactory下面会详细介绍)。
至于handlePackagePath,handleMethodOrClassPath,handleTags这三个私有方法,主要是将xml中的信息转换成TypeFactory中静态属性需要的格式。大家可以在最下面下载源码看看。
这个类是ClassType,MethodType,PackageType的工厂,doclet3中ClassType,MethodType,PackageType的获取都是通过这个工厂对应的三个get方法。
deepCopy这个方法,是用于对象的深度复制(此处我用的是序列化的方式:将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝)。
那么,为什么要用深度复制这样耗性能的方式给doclet3对象呢?要弄清这个问题,我们需要来看一下经过HandleXml的init方法之后,TypeFactory的三个静态属性会分别携带哪些信息:
PackageType类型的PACKAGETYPE变量:静态变量basepath会确定;静态变量template会确定;静态变量outpath会确定;变量classTypes待定。这样,PackageType类型的静态变量都确定,动态静态变量都不定,所以考虑到性能TypeFactory的getPackageType方法就直接new一个PackageType对象作为出参
MethodType类型的METHODTYPE变量:静态变量template会确定;变量outpath的大小和key值会确定,value需要从注解中读入;变量tagList的大小和内容会确定,只是它的每一项Tag类中的itemvalues需要从注解中读入。这样静态变量确定,动态变量的结构和内容大多都确定,只剩下在doclet3中读入注解内容将其填入即可,所以需要以METHODTYPE为原型,用深度复制的方式创建对象。(如果不用复制对象的方式,而是每次在doclet3中创建一个对象,然后读一遍xml将方法注解名称信息填入,这样一来更耗性能,二来耦合性太强,极易出错)
ClassType类型的CLASSTYPE变量:它的情况和METHODTYPE类似,不过methodTypeList值为null,需要在doclet3中填入。也是采用深度复制的方式创建对象。
这个类的init方法主要是依照包,类,方法中的模板路径读入模板,生成Template对象,保存在属性template中,这个map属性的key就是注解xml中viewpath标签下的name值,这也就是为什么所有的viewpath标签不能相同。程序中是在调用HandleXml的handlexml方法之后紧接着调用这个方法
这个类的createfile方法主要是在指定位置生成对应文件。入参data指定模板中填充的数据,入参outpath的key是viewpath标签的name,value是输出路径(是相对于basepath的路径),这样通过这个key就将模板和输出路径对应起来,之后填入数据输出即可。
doclet3的start方法代码如下:
public static boolean start(RootDoc root) {
try {
HandleXml.handlexml(xmlpath);
HandleView.init();
} catch (Exception e) {
e.printStackTrace();
}
PackageType packageType = TypeFactory.getPackageType();
try {
doc(root.classes(), packageType);
//为包生成对应的文档
try {
HandleView.createfile(packageType.transformTagvalue(),PackageType.getOutpath());
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
经过上面的介绍,这个代码应该不难理解。我们重点来介绍doc(root.classes(), packageType);这个方法,看它干了什么,其代码如下:
private static void doc(ClassDoc[] classDocs,PackageType packageType) throws Exception {
List classTypeList = new ArrayList();
packageType.setClassTypes(classTypeList);
//处理各个类
for (int i = 0; i < classDocs.length; i++) {
ClassType classType = TypeFactory.getClassType();
classTypeList.add(classType);
//处理类注解
ClassDoc classDoc = classDocs[i];
List.Tag> classtags = classType.getTagList();
for (tag.Tag classtag : classtags){
classtag.setItemvalues(docClassList(classDoc,classtag.getName()));
}
for (Map.Entry outpath : classType.getOutpath().entrySet()){
outpath.setValue(docClassList(classDoc,outpath.getKey()).get(0));
}
//处理类中各个方法的注解
List methodTypeList = new ArrayList();
classType.setMethodTypeList(methodTypeList);
MethodDoc[] methodDocs = classDoc.methods();
for (int j = 0 ; j < methodDocs.length ; j ++){
MethodType methodType = TypeFactory.getMethodType();
methodTypeList.add(methodType);
//处理方法注解
MethodDoc methodDoc = methodDocs[j];
List.Tag> methodtags = methodType.getTagList();
for (tag.Tag methodtag : methodtags){
methodtag.setItemvalues(docMethodList(methodDoc,methodtag.getName()));
}
//设置每个方法的输出路径
for (Map.Entry outpath : methodType.getOutpath().entrySet()){
outpath.setValue(docMethodList(methodDoc,outpath.getKey()).get(0));
}
//为每个方法生成对应的文件
HandleView.createfile(methodType.transformTagvalue(),methodType.getOutpath());
}
//设置每个类的输出路径
for (Map.Entry outpath : classType.getOutpath().entrySet()){
outpath.setValue(docClassList(classDoc, outpath.getKey()).get(0));
}
//为每个类生成对应的文件
HandleView.createfile(classType.transformTagvalue(),classType.getOutpath());
}
}
看了上面的注解,相信大家也有对其有个大概的了解,这个方法就做两件事:
由于将所有的注解都当成多个注解来处理,这样传入freemarker模板中的数据便都变为list,所以需要修改原来模板中${uri}这样的取单个注解的取值方式。(像之前param这样本来就是多个注解的不变)。
下面是每个方法对应的html模板。(注意uri,returnjson,description的取值方式变)
<html>
<meta charset=utf8>
<head>
<title>title>
head>
<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>
<body>
<h1 class="wiki_title">
<span class="mw-headline"><#list uri as a>${a}#list>span>
h1>
<p><#list description as a>${a}#list>p>
<h2 class="wiki_title">
<span class="mw-headline">URLspan>
h2>
<p>
<span style="font-weight:600">
<a rel="nofollow" class="external free" href="">http://test.jchvip.net/<#list uri as a>${a}#list>a>
span>
p>
<h2 class="wiki_title">
<span class="mw-headline" >支持格式span>
h2>
<p>
<span style="text-transform:uppercase;font-weight:600">JSONspan>
p>
<h2 class="wiki_title">
<span class="mw-headline" >HTTP请求方式span>
h2>
<p>
<span style="text-transform:uppercase;font-weight:600">POSTspan>
p>
<h2 class="wiki_title">
<span class="mw-headline" >请求参数span>
h2>
<table border="1" cellspacing="0" cellpadding="0" width="100%" class="parameters" style="border-color: #CCCCCC;">
<tbody>
<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>
<#list param as item>
<tr>
<td style="text-align:center;font-weight:bolder;border:1px solid #cccccc">${item.name}td>
<td style="text-align:center;border:1px solid #cccccc">${item.select}td>
<td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.type}td>
<td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.explain}td>
tr>
#list>
tbody>
table>
<h2 class="wiki_title">
<span class="mw-headline">返回结果span>
h2>
<div class="code_type" style="text-transform:uppercase;margin-bottom:5px;">JSON示例div>
<pre>
<#list returnjson as a>${a}#list>
pre>
<h2 class="wiki_title">
<span class="mw-headline">返回字段说明span>
h2>
<table border="1" cellspacing="0" cellpadding="0" width="100%" class="parameters" style="border-color: #CCCCCC;">
<tbody>
<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>
<#list returnparam as item>
<tr>
<td style="text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc">${item.name}td>
<td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.type}td>
<td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.explain}td>
tr>
#list>
tbody>
table>
<h2 class="wiki_title">
<span class="mw-headline" >注意事项span>
h2>
<p>无p>
body>
html>
下面是每个方法对应的java模板(注意methodname,uri取值方式的变化):
package com.bluemobi.bluecollar.network.request;
import java.util.HashMap;
import java.util.Map;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.bluemobi.bluecollar.network.LlptHttpJsonRequest;
import com.bluemobi.bluecollar.network.response.getTagListResponse;
public class <#list methodname as a>${a}</#list>Request extends LlptHttpJsonRequest<<#list methodname as a>${a}</#list>Response> {
private static final String APIPATH = "<#list uri as a>${a}#list>";
<#list param as a>
private String ${a.name};
public String get${a.name?cap_first}() {return ${a.name};}
public void set${a.name?cap_first}(String ${a.name}) {this.${a.name} = ${a.name};}
</#list>
public <#list methodname as a>${a}</#list>Request(Listener<<#list methodname as a>${a}</#list>Response> listener, ErrorListener errorListener) {
super(Method.POST, APIPATH, listener, errorListener);
}
public <#list methodname as a>${a}</#list>Request(int method, String partUrl, Listener<<#list methodname as a>${a}</#list>Response> listener, ErrorListener errorListener) {
super(method, partUrl, listener, errorListener);
}
public Class<<#list methodname as a>${a}</#list>Response> getResponseClass() {return <#list methodname as a>${a}</#list>Response.class;}
public String GetApiPath() {return APIPATH;}
public Map<String, String> GetParameters() {
Map<String, String> map = new HashMap<String, String>();
<#list param as a>
map.put("${a.name}",${a.name});
</#list>
return map;
}
}
下面是包对应的html模板(是可以在里面写js代码的):
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<html>
<title>title>
<style type="text/css">
body{font: 12px/1.125 Arial, Helvetica, sans-serif;}
.custom{
border-collapse:collapse;
border:gray;
width: 100%;
display: table;
text-align: left;
margin-bottom: 20px;
}
.custom .tbF1{width: 116px;}
.custom .tbF2{width: 218px;}
.custom th{
padding: 12px 0;
background: #e9e9e9;
border: 1px solid #e8eaec;
padding-left: 10px;
font-weight: 700;
font-size: 14px;
text-align: left;
color: #000;
}
.custom td{
background: #fff;
padding-left: 10px;
padding: 7px 10px;
line-height: 20px;
border: 1px solid #e8eaec;
}
.custom pre{overflow: hidden;}
a{
text-decoration: none;
color: #3c7cb3;
}
a:hover{text-decoration: underline;}
style>
<#list classes as classitem>
<div>
<table class = "custom">
<colgroup><col class = "tbF1"><col class = "tbF2"><col>colgroup>
<tr>
<th colspan="3"><#list classitem.description as a>${a}#list>th>
tr>
<#assign classuri><#list classitem.uri as a>${a}#list>#assign>
<#assign readnum = 0/>
<#assign writenum = 0/>
<#list classitem.methods as method>
<#assign type><#list method.type as a>${a}#list>#assign>
<#if type == "read">
<#assign readnum = readnum+1>
<#if readnum == 1>
<tr>
<td id="read${classitem_index}">读取接口td>
<td><a href="<#list method.html as a>${a}#list>"><#list method.uri as a>${classuri+"/"+a}#list>a>td>
<td><#list method.path as a>${a}#list>td>
tr>
<#else>
<tr>
<td><a href="<#list method.html as a>${a}#list>"><#list method.uri as a>${classuri+"/"+a}#list>a>td>
<td><#list method.path as a>${a}#list>td>
tr>
#if>
#if>
#list>
<#list classitem.methods as method>
<#assign type><#list method.type as a>${a}#list>#assign>
<#if type == "write">
<#assign writenum = writenum+1>
<#if writenum == 1>
<tr>
<td id="write${classitem_index}">写入接口td>
<td><a href="<#list method.html as a>${a}#list>"><#list method.uri as a>${classuri+"/"+a}#list>a>td>
<td><#list method.path as a>${a}#list>td>
tr>
<#else>
<tr>
<td><a href="<#list method.html as a>${a}#list>"><#list method.uri as a>${classuri+"/"+a}#list>a>td>
<td><#list method.path as a>${a}#list>td>
tr>
#if>
#if>
#list>
<input id="readnum${classitem_index}" hidden="hidden" value="${readnum}">
<input id="writenum${classitem_index}" hidden="hidden" value="${writenum}">
table>
div>
#list>
<script>
for(var i=0 ; i < ${classes?size} ; i++ )
{
var read = document.getElementById("read"+i);
if(read){
var readnum = document.getElementById("readnum"+i);
read.setAttribute("rowspan",readnum.value);
}
var write = document.getElementById("write"+i);
if(write){
var writenum = document.getElementById("writenum"+i);
write.setAttribute("rowspan",writenum.value);
}
}
var alist = document.getElementsByTagName("a");
var date = new Date();
for(var j=0 ; j < alist.length ; j++ ){
alist[j].setAttribute("href",alist[j].getAttribute("href")+ "?" + date.getTime());
}
script>
html>
代码下载:https://github.com/zhaoshiqiang/doclet