SpringMvc实战--在一个页面显示Controller中所有的方法信息

0.说明

本博客记录的是如何显示SpringMVC框架中所有使用@RequestMapping注解标注的方法.

由于项目需要,web框架使用SpringMVC.前端\客户端\后端是分开的不同组的人,所以不可避免的要编写\更新大量的接口说明文档.这大大降低了效率,因此实现了显示SpringMVC中所有接受请求的方法的信息的功能.

总体思想:从Spring容器中找到所有加了RequestMapping注解的方法,并且集中显示.

1.用处

显示SpringMVC中所有加了@RequestMapping注解的方法信息

2.实现

2.0.环境说明

使用maven开发,别的不多说了.直接上pom

复制代码
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${org.springframework.version}</version>
            <scope>test</scope>
        </dependency>
复制代码

其中

<org.springframework.version>3.2.13.RELEASE</org.springframework.version>

底层的spring core之类的也是3.2.13.RELEASE

2.1.显示所有请求信息

细心的同学可能发现了.在springMVC项目启动的时候,会出现很多的信息.

其中,和咱们这个文章相关的就是这个类输出的信息了:

复制代码
2015-05-18 12:18:47,253 INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/guide/display/search],methods=[GET],params=[],headers=[],consumes=[],produces=[application/json;charset=UTF-8],custom=[]}" onto public java.lang.String com.renren.toro.waltz.web.controller.guide.DisplayController.search()

2015-05-18 12:18:47,253 INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/guide/display],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView com.renren.toro.waltz.web.controller.guide.DisplayController.index()

2015-05-18 12:18:47,253 INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/guide/display/detail],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView com.renren.toro.waltz.web.controller.guide.DisplayController.detail(int)

2015-05-18 12:18:47,255 INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/logout],methods=[GET],params=[],headers=[],consumes=[],produces=[application/json;charset=UTF-8],custom=[]}" onto public java.lang.String com.renren.toro.waltz.web.controller.LogoutController.logout(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
复制代码

还有很多,只是截取了一段.

这个RequestMappingHandlerMapping 类会输出所有的Map信息.

下面介绍信息中的数据

这里以这一条输出作为实例:

2015-05-18 12:18:47,255 INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/logout],methods=[GET],params=[],headers=[],consumes=[],produces=[application/json;charset=UTF-8],custom=[]}" onto public java.lang.String com.renren.toro.waltz.web.controller.LogoutController.logout(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
序号 名称 说明
1
/logout
对应的接受请求的url
2
methods
接受请求的方法
3
params
请求的参数
4
headers
请求的header信息
5
consumes
接受请求的类型
6
produces
返回的数据类型
     

后边会输出对应的类和方法信息:

onto public java.lang.String com.renren.toro.waltz.web.controller.LogoutController.logout(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)


 因此从这里入手.

我这里使用了annotation-driven,所以可以直接

@Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

这样就可以在一个handler方法中获取所有的添加了@RequestMapping的方法了.

具体如下:

复制代码
 1         // request methods
 2         Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
 3         Set<RequestMappingInfo> keySet = handlerMethods.keySet();
 4 
 5         for (RequestMappingInfo requestMappingInfo : keySet) {
 6             // 请求路径
 7             String path = requestMappingInfo.getPatternsCondition().toString();
 8 
 9             // 请求方法
10             String requestMethod = requestMappingInfo.getMethodsCondition().toString();
11      
18             // 返回header类型
19             String responseType = requestMappingInfo.getProducesCondition().toString();
20 
21             RequestMethodItem item = new RequestMethodItem();
22             
23             item.setPath(path);26             item.setMethod(handlerMethod.toString().replace(" ", "<br>"));
27             item.setResponseType(responseType);
28             
29             items.add(item);
30         }
复制代码

这里需要写一个Item对象来存储这些信息,方便一起"打包"传给jsp页面,方便显示,由于只是一个POJO,就不帖代码了.

2.2.显示请求所对应的handler方法信息

可以使用requestMappingInfo中的getMethodsCondition()方法获取Controller类

从Map<RequestMappingInfo, HandlerMethod>这个Map中,使用对应的RequestMappingInfo作为key到这个map中查找对应的HandlerMethod

我们来具体看一下这个类:

HandlerMethod

复制代码
/**
 * Encapsulates information about a handler method consisting of a {@linkplain #getMethod() method}
 * and a {@linkplain #getBean() bean}. Provides convenient access to method parameters,
 * method return value, method annotations.
 *
 * <p>The class may be created with a bean instance or with a bean name (e.g. lazy-init bean,
 * prototype bean). Use {@link #createWithResolvedBean()} to obtain a {@link HandlerMethod}
 * instance with a bean instance resolved through the associated {@link BeanFactory}.
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @since 3.1
 */
复制代码

一个包含了getMethod和getBean的类.

里边有用的方法:

getMethodParameters() 获得参数列表

通过这个方法,可以获得这个方法所对应的参数信息.该方法返回的是:MethodParameter[]

2.3.显示handler方法的参数信息

遍历2.2中说到的MethodParameter[] 可以获得具体的每一个参数的信息

使用其中的方法:

getParameterName(); 参数名

getParameterType(); 参数类型

getParameterAnnotations(); 参数注解

 

一切似乎就这样美妙的完成了.但是,学习过java class 规范的同学应该记得,javac在编译的时候,会抹去参数名称信息;

那么,getParameterName()获取到的会是什么呢?

看注释,果然如我所料:

注意returns描述的括号中的内容.

有可能是null,如果参数名称没有在class文件中,或者在开始时没有设置ParameterNameDiscoverer.

查看javac -help

输出如下:

果断加上-g参数再进行一次编译

在maven中这个参数应该加在org.apache.maven.plugins插件配置中

复制代码
          <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <compilerArguments>
                        <!-- show all debug info -->
                        <g />
                    </compilerArguments>
                </configuration>
            </plugin>    
复制代码

在complierArguments中添加一个<g/>

那么,第一个问题解决了,参数名称信息已经包含在了class文件中了.

第二个问题,怎么在开始的时候指定ParameterNameDiscoverer?

查遍google stackoverflow无果...

spring doc也翻了翻,没找到,无奈,只能debug.查调用方.

思路如下,在接受参数的时候,如果不加@RequestParam注解,spring也能实现参数注入.一顿折腾.发现spring直接在spring-core中使用了asm的代码来操作字节码.最终完成了读取class文件中对应方法的的参数名

而初始化ParameterNameDiscoverer的代码在org.springframework.web.method.support.InvocableHandlerMethod类中找到了使用的方式.

直接声明了一个本地变量,使用LocalVariableTableParameterNameDiscoverer,实现类;然后调用initParameterNameDiscovery方法完成了初始化.

 

仔细看过LocalVariableTableParameterNameDiscoverer里边有很多虚拟机内缓存的实践,有大量的map结构用于存储数据,进而减少字节码操作.

鉴于这里只要springmvc容器启动,就不会出现class文件变动的情况,所以在本地变量中加上了static.

 

至此,就完成了显示所有@RequestMapping的信息的工作.

2.4.扩展功能

但是,由于只知道了对应的handlerMethod的参数,类,请求方式等信息,还是会出现可能的歧义,不够明确的指出接口的作用和意义.

所以定义了一个注解

复制代码
@Inherited
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WaltzDocument {
    /**
     * 注释内容,推荐使用html标签进行编辑<br>
     * 2015年5月7日:下午4:27:30<br>
     * <br>
     * 
     * @return
     */
    String info();

    /**
     * 返回给页面的参数,推荐使用html标签进行编辑 <br>
     * 2015年5月16日:下午4:05:32<br>
     * <br>
     * 
     * @return
     */
    String[] params() default "";

    /**
     * 接口提供者 <br>
     * 2015年5月7日:下午4:27:43<br>
     * <br>
     * 
     * @return
     */
    String author();

    /**
     * 接口状态 <br>
     * 2015年5月7日:下午4:34:06<br>
     * <br>
     * 
     * @return
     * <pre>
     * </pre>
     */
    Status status();

    public static enum Status {
        /**
         * 假接口
         */
        fake,
        /**
         * 开发
         */
        developing,
        /**
         * 开发完成
         */
        done,

        ;
    }
}
复制代码

在扫描对应的handlerMethod的同时,获取对应的注解:

handlerMethod.getMethodAnnotation(WaltzDocument.class);

之后把这个注解中对应的参数一同设置到Item中.方便显示.

同时还结合了javadoc的功能,可以直接跳转到具体方法的javadoc页面查看具体说明文档.

页面上,结合一下Bootstrap和datatables就可以完成分页\筛选\搜索\排序的功能,进一步方便使用.

最终效果:

3.思考

3.1.spring中"虚拟机内"缓存的使用

在spring中,有很多的"虚拟机内换存",所谓虚拟机内缓存,利用虚拟机内的内存空间来实现一些变量的存储,当调用查询方法的时候,先使用查询的key在对应的缓存map中进行查找,这样可以大大降低底层IO操作的次数和频度.像本文中提到的LocalVariableTableParameterNameDiscoverer类,下层的获取方法参数名的方式是使用asm处理字节码.使用ClassReader和LocalVariableTableParameterNameDiscoverer提供的内部类ParameterNameDiscoveringVisitor来实现字节码操作.

数据要求不可变:这种虚拟机内缓存,对数据也是有一定的要求的.要数据的key不可变.试想,调用put方法是的hash值是A0,而想要get时hash值却变成了A1,这样就不能正确的获取数据,反而会造成缓存map越来越大,map的命中率越来越低和不断的扩容.最终导致的是大量无用内存占用和性能下降,最差情况会到导致OOM.

缓存对象大小要求有限:这种虚拟机内缓存只是用于数量有限(放置于map中的对象小于内存大小要求)的情况,试想,如果是一个无限大小的数据集合要做缓存,那最终的结构就是OOM了.否则要考虑使用一些软连接,虚连接形式的对象声明来避免这个问题了.

3.2.java参数抹去

jvm在执行方法的时候,实际上是不需要知道参数的名称的,jvm关心的只是参数的类型.所以,javac抹去了所有的参数信息,替换成paramOfString0....

测试代码如下:

复制代码
public class Test {
    public static void main(
                    String[] mainArgs) {
        System.out.println("beenoisy");
    }

    public static void test(
                    int thisIsAIntArg,
                    String thisIsAObjectArg) {
        System.out.println("Test.test()");
    }
}
复制代码

javac Test.java编译出来的字节码:

而同样的代码,添加了-g参数,就会带上参数名称信息,相信这是对class文件压缩的一种体现,但是却某种程度上的削弱了jvm运行时的一些功能.

同样,java对集合类型的泛型也是采取抹去处理的.在thinking in java中也说过,java的泛型不是真正的泛型.估计也是类似的考量.

3.3.怎样提高前后端开发效率

尽量减少无用功.

代码是最好的文档,然文档跟着代码改变.

一体化的文档体系\bug追踪体系\需求分派体系对于效率提升十分重要.

当然,这个小工具还是开发开始阶段紧急弄出来的,很多地方没有很好的遵循规范,后期如果有时间,考虑做成一个通用工具来集成到spring中.

最后,附上完整代码:

Controller:

复制代码
  1 import java.util.Arrays;
  2 import java.util.Collections;
  3 import java.util.List;
  4 import java.util.Map;
  5 import java.util.Set;
  6 
  7 import org.springframework.beans.factory.annotation.Autowired;
  8 import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
  9 import org.springframework.core.MethodParameter;
 10 import org.springframework.stereotype.Controller;
 11 import org.springframework.web.bind.annotation.RequestMapping;
 12 import org.springframework.web.method.HandlerMethod;
 13 import org.springframework.web.servlet.ModelAndView;
 14 import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
 15 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 16 
 17 import com.google.common.collect.Lists;
 18 import com.renren.toro.waltz.common.annotation.WaltzDocument;
 19 import com.renren.toro.waltz.common.annotation.WaltzDocument.Status;
 20 import com.renren.toro.waltz.common.interceptor.performance.CountTime;
 21 import com.renren.toro.waltz.dev.controller.support.RequestMethodItem;
 22 import com.renren.toro.waltz.dev.controller.support.RequestMethodParameter;
 23 
 24 /**
 25  * 显示所有请求 <br>
 26  * 2015年5月6日:下午5:03:51
 27  * 
 28  * @author Keen <br>
 29  */
 30 @Controller
 31 @RequestMapping("dev/param")
 32 public class ParamController {
 33     @Autowired
 34     private RequestMappingHandlerMapping requestMappingHandlerMapping;
 35     
 36     private static LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
 37 
 38     @RequestMapping("")
 39     @WaltzDocument(info = "参数显示页面", author = "ziyi.wang", status = Status.done)
 40     @CountTime
 41     public ModelAndView exe() {
 42         // items
 43         List<RequestMethodItem> items = Lists.newArrayList();
 44 
 45         // request methods
 46         Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
 47         Set<RequestMappingInfo> keySet = handlerMethods.keySet();
 48 
 49         for (RequestMappingInfo requestMappingInfo : keySet) {
 50             // 请求路径
 51             String path = requestMappingInfo.getPatternsCondition().toString();
 52 
 53             // 请求方法
 54             String requestMethod = requestMappingInfo.getMethodsCondition().toString();
 55 
 56             // Controller的处理方法
 57             HandlerMethod handlerMethod = handlerMethods.get(requestMappingInfo);
 58 
 59             // 参数
 60             MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
 61             
 62             // 返回header类型
 63             String responseType = requestMappingInfo.getProducesCondition().toString();
 64 
 65             List<RequestMethodParameter> parameters = Lists.newArrayListWithExpectedSize(methodParameters.length);
 66             for (MethodParameter methodParameter : methodParameters) {
 67                 // 参数名称
 68                 // 如果没有discover参数会是null.参考LocalVariableTableParameterNameDiscoverer
 69                 methodParameter.initParameterNameDiscovery(discoverer);
 70                 String parameterName = methodParameter.getParameterName();
 71 
 72                 // 参数类型
 73                 Class<?> parameterType = methodParameter.getParameterType();
 74 
 75                 // 参数注解
 76                 Object[] parameterAnnotations = methodParameter.getParameterAnnotations();
 77 
 78                 // 注解
 79                 String annoation = Arrays.toString(parameterAnnotations);
 80 
 81                 RequestMethodParameter parameter = new RequestMethodParameter();
 82                 parameter.setAnnoation(annoation);
 83                 parameter.setName(parameterName);
 84                 parameter.setType(parameterType.toString());
 85                 parameters.add(parameter);
 86             }
 87 
 88             WaltzDocument documentAnnotation = handlerMethod.getMethodAnnotation(WaltzDocument.class);
 89 
 90             RequestMethodItem item = new RequestMethodItem();
 91             
 92             item.setPath(path);
 93             item.setRequestMethod(requestMethod);
 94             item.setParameters(parameters);
 95             item.setMethod(handlerMethod.toString().replace(" ", "<br>"));
 96             item.setDocument(documentAnnotation);
 97             item.setResponseType(responseType);
 98             
 99             items.add(item);
100         }
101 
102         Collections.sort(items);
103 
104         ModelAndView mav = new ModelAndView("dev/param");
105         mav.addObject("items", items);
106         return mav;
107     }
108 }
复制代码

POJO:

复制代码
  1 import java.util.List;
  2 
  3 import com.renren.toro.waltz.common.annotation.WaltzDocument;
  4 
  5 /**
  6  * 条目 <br>
  7  * 2015年5月7日:下午3:26:15
  8  * 
  9  * @author Keen <br>
 10  */
 11 public class RequestMethodItem implements Comparable<RequestMethodItem> {
 12     private String path;
 13     private String requestMethod;
 14     private String method;
 15     private String responseType;
 16     private List<RequestMethodParameter> parameters;
 17     private WaltzDocument document;
 18 
 19     public String getPath() {
 20         return path.replace("[", "").replace("]", "");
 21     }
 22 
 23     public void setPath(
 24                     String path) {
 25         this.path = path;
 26     }
 27 
 28     public String getRequestMethod() {
 29         return requestMethod;
 30     }
 31 
 32     public void setRequestMethod(
 33                     String requestMethod) {
 34         this.requestMethod = requestMethod;
 35     }
 36 
 37     public String getMethod() {
 38         String a = getA();
 39         return method + a;
 40     }
 41 
 42     private String getA() {
 43         String methodName = method.split("<br>")[2];
 44 
 45         String classPath = methodName.split("\\(")[0];
 46 
 47         String[] packageSplit = classPath.split("\\.");
 48         StringBuilder sb = new StringBuilder();
 49         for (int i = 0; i < packageSplit.length; i++) {
 50             sb.append(packageSplit[i]);
 51             if (i < packageSplit.length - 1) {
 52                 if (i == packageSplit.length - 2) {
 53                     sb.append(".html#");
 54                 } else {
 55                     sb.append("/");
 56                 }
 57             }
 58         }
 59 
 60         sb.append("(");
 61         String methodPath = methodName.split("\\(")[1];
 62         sb.append(methodPath.replace(",", ", "));
 63 
 64         String href = sb.toString();
 65         String a = "<br>\n<a href='http://svn.d.xiaonei.com/toro/waltz/renren-toro-waltz-web/trunk/doc/" + href
 66                         + "' target='_blank'>详细接口文档</a>";
 67         return a;
 68     }
 69 
 70     public void setMethod(
 71                     String method) {
 72         this.method = method;
 73     }
 74 
 75     public List<RequestMethodParameter> getParameters() {
 76         return parameters;
 77     }
 78 
 79     public void setParameters(
 80                     List<RequestMethodParameter> parameters) {
 81         this.parameters = parameters;
 82     }
 83 
 84     public WaltzDocument getDocument() {
 85         return document;
 86     }
 87 
 88     public void setDocument(
 89                     WaltzDocument document) {
 90         this.document = document;
 91     }
 92 
 93     public String getResponseType() {
 94         return responseType;
 95     }
 96 
 97     public void setResponseType(
 98                     String responseType) {
 99         this.responseType = responseType;
100     }
101 
102     public String getResponseParams() {
103         WaltzDocument document = this.getDocument();
104         if (document == null) {
105             return "";
106         }
107         String[] params = document.params();
108         StringBuilder sb = new StringBuilder();
109         for (int i = 0; i < params.length; i++) {
110             sb.append(params[i]).append("<hr>\n");
111         }
112         
113         return sb.toString();
114     }
115 
116     @Override
117     public String toString() {
118         return "Item [path=" + path + ", requestMethod=" + requestMethod + ", method=" + method + ", responseType="
119                         + responseType + ", parameters=" + parameters + ", document=" + document + "]";
120     }
121 
122     @Override
123     public int compareTo(
124                     RequestMethodItem o) {
125         return this.getPath().compareTo(o.getPath());
126     }
127 }
复制代码
复制代码
 1 /**
 2  * 参数 <br>
 3  * 2015年5月7日:下午3:26:31
 4  * 
 5  * @author Keen <br>
 6  */
 7 public class RequestMethodParameter {
 8     private String name;
 9     private String annoation;
10     private String type;
11 
12     public String getName() {
13         return name;
14     }
15 
16     public void setName(
17                     String name) {
18         this.name = name;
19     }
20 
21     public String getAnnoation() {
22         return annoation;
23     }
24 
25     public void setAnnoation(
26                     String annoation) {
27         this.annoation = annoation;
28     }
29 
30     public String getType() {
31         return type;
32     }
33 
34     public void setType(
35                     String type) {
36         this.type = type;
37     }
38 
39     @Override
40     public String toString() {
41         return "Parameter [name=" + name + ", annoation=" + annoation + ", type=" + type + "]";
42     }
43 }
复制代码

jsp

复制代码
  1 <%@ page language="java" pageEncoding="UTF-8"%>  
  2 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
  3 <%@ page import="java.util.Arrays"%>
  4 <html>
  5 <head>
  6 <title>Waltz 接口查看页</title>
  7 
  8 <!-- 新 Bootstrap 核心 CSS 文件 -->
  9 <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css">
 10 
 11 <!-- 可选的Bootstrap主题文件(一般不用引入) -->
 12 <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
 13 
 14 <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
 15 <script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
 16 
 17 <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
 18 <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
 19 
 20 
 21 <!-- datatables -->
 22 <script type="text/javascript" charset="utf8" src="http://cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js"></script>
 23 <script type="text/javascript" charset="utf8" src="http://cdn.datatables.net/plug-ins/1.10.7/integration/bootstrap/3/dataTables.bootstrap.js"></script>
 24 
 25 
 26 </head>
 27 <body>
 28     <table id="paramTable" class="table table-striped table-hover table-bordered table-condensed display">
 29         <thead>
 30             <tr>
 31                 <th>No.</th>
 32                 <th>docment</th>
 33                 <th>author</th>
 34                 <th>status</th>
 35                 <th>path</th>
 36                 <th>requestMethod</th>
 37                 <th>responseType</th>
 38                 <th>responseParams</th>
 39                 <th>controllerMethod</th>
 40                 <th>parameters</th>
 41             </tr>
 42 
 43         </thead>
 44         <tbody>
 45             <c:forEach items="${items}" var="i" varStatus="x">
 46                 <tr>
 47                     <td>${x.count}</td>
 48                     <td>${i.document.info()}</td>
 49                     <td>${i.document.author()}</td>
 50                     <td>${i.document.status()}</td>
 51                     <td>${i.path}<br><a href='http://xxx.com${i.path}' target='_blank'>跳转查看</a></td>
 52                     <td>${i.requestMethod}</td>
 53                     <td>${i.responseType}</td>
 54                     <td>${i.responseParams}</td>
 55                     <td>${i.method}</td>
 56                     <td>
 57                         <c:if test="${i.parameters.size() > 0 }">
 58                         <table class="table table-striped table-hover table-bordered table-condensed">
 59                             <thead>
 60                                 <tr>
 61                                     <th>parameterName</th>
 62                                     <th>parameterType</th>
 63                                     <th>parameterAnnotations</th>
 64                                 </tr>
 65                             </thead>
 66                             <tbody>
 67                                 <c:forEach items="${i.parameters}" var="p">
 68                                     <tr>
 69                                         <td>${p.name}</td>
 70                                         <td>${p.type}</td>
 71                                         <td>${p.annoation}</td>
 72                                     </tr>
 73                                 </c:forEach>
 74                             </tbody>
 75                         </table>
 76                         </c:if>
 77                     </td>
 78                 </tr>
 79             </c:forEach>
 80         </tbody>
 81         <tfoot>
 82             <tr>
 83                 <th>No.</th>
 84                 <th>docment</th>
 85                 <th>author</th>
 86                 <th>status</th>
 87                 <th>path</th>
 88                 <th>requestMethod</th>
 89                 <th>responseType</th>
 90                 <th>responseParams</th>
 91                 <th>controllerMethod</th>
 92                 <th>parameters</th>
 93             </tr>
 94         </tfoot>
 95     </table>
 96     
 97     <script type="text/javascript">
 98         $(document).ready(function() {
 99             // Setup - add a text input to each footer cell
100             $('#paramTable tfoot th').each( function () {
101                 var title = $('#paramTable thead th').eq( $(this).index() ).text();
102                 $(this).html( '<input type="text" placeholder="Search '+title+'" />' );
103             });
104          
105             // DataTable
106             var table = $('#paramTable').DataTable({
107                 paging: false
108             });
109          
110             // Apply the search
111             table.columns().eq(0).each(function(colIdx){
112                 $('input', table.column(colIdx).footer()).on('keyup change',function () {
113                     table.column( colIdx ).search( this.value ).draw();
114                 });
115             });
116         });
117     </script>
118     
119 </body>
120 </html>
复制代码

猜你喜欢

转载自blog.csdn.net/wangzhanzheng/article/details/80241529