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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM