目前的的ActionReporter能夠定位到當前方法調用過的Controller,點擊eclipse控制檯中的連接能夠定位到改類的第一行,可是並不能直接定位到調用方法的那一行。java
爲了進一步提供開發者的體驗,我增強了ActionReporter的源代碼定位功能,能直接定位到調用的方法上面。先看下面效果圖。 spring
當請請求調用了MobileBindController的heart方法,控制能夠直接打出heart方法在 MobileBindController.java中的行數。app
在java代碼中雖然有能獲得當前方法棧上的代碼行數,可是因爲jfinal中的aop並無使用第三方的字節碼工具修改原類的字節碼,全部沒有辦法在方法調用以前知道要調用方法的行數。若是使用spring aop能夠把這個功能切到目標方法調用的前面,應該能夠達到效果。(沒實際操做過還..)eclipse
在不修改字節碼的狀況下,我暫時只想到利用源碼來計算行數..反正都是開發階段,這樣其實也無大礙..原理很簡單.獲得調用的類和方法而後到對應源文件去找該方法的字符串所在行。實現不夠優雅可是功能仍是挺實用的。工具
代碼中刪除字符串空格的方法直接copy自commons-lang。ui
代碼再jdk7下編寫的。7如下的須要把遍歷文件那段代碼稍微替換如下就ok了。url
package com.jfinal.core; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.jfinal.aop.Interceptor; /** * ActionReporter */ final class ActionReporter { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * Report action before action invoking when the common request coming */ static final boolean reportCommonRequest(Controller controller, Action action) { String content_type = controller.getRequest().getContentType(); if (content_type == null || content_type.toLowerCase().indexOf("multipart") == -1) { // if (content_type == null || content_type.indexOf("multipart/form-data") == -1) { doReport(controller, action); return false; } return true; } /** * Report action after action invoking when the multipart request coming */ static final void reportMultipartRequest(Controller controller, Action action) { doReport(controller, action); } private static final void doReport(Controller controller, Action action) { StringBuilder sb = new StringBuilder("\nJFinal action report -------- ").append(sdf.format(new Date())).append(" ------------------------------\n"); Class<? extends Controller> cc = action.getControllerClass(); sb.append("Controller : ").append(cc.getName()).append(".(").append(cc.getSimpleName()).append(".java:") .append(lineNum("publicvoid"+action.getMethodName()+"(){", fileName(cc))).append(")"); sb.append("\nMethod : ").append(action.getMethodName()).append("\n"); String urlParas = controller.getPara(); if (urlParas != null) { sb.append("UrlPara : ").append(urlParas).append("\n"); } Interceptor[] inters = action.getInterceptors(); if (inters.length > 0) { sb.append("Interceptor : "); for (int i=0; i<inters.length; i++) { if (i > 0) sb.append("\n "); Interceptor inter = inters[i]; Class<? extends Interceptor> ic = inter.getClass(); sb.append(ic.getName()).append(".(").append(ic.getSimpleName()).append(".java:") .append(lineNum("publicvoidintercept", fileName(inter.getClass()))).append(")"); } sb.append("\n"); } // print all parameters HttpServletRequest request = controller.getRequest(); @SuppressWarnings("unchecked") Enumeration<String> e = request.getParameterNames(); if (e.hasMoreElements()) { sb.append("Parameter : "); while (e.hasMoreElements()) { String name = e.nextElement(); String[] values = request.getParameterValues(name); if (values.length == 1) { sb.append(name).append("=").append(values[0]); } else { sb.append(name).append("[]={"); for (int i=0; i<values.length; i++) { if (i > 0) sb.append(","); sb.append(values[i]); } sb.append("}"); } sb.append(" "); } sb.append("\n"); } sb.append("--------------------------------------------------------------------------------\n"); System.out.print(sb.toString()); } private static String fileName(Class clazz) { String controllerFile = System.getProperty("user.dir")+File.separator+"src"; for (String temp : clazz.getName().split("\\.")) { controllerFile = controllerFile+File.separator+temp; } return controllerFile+".java"; } private static int lineNum(String codeFragment, String fileName) { List<String> lines = new ArrayList<>(); int lineNum = 1; Path path = Paths.get(fileName); try { lines = Files.readAllLines(path, Charset.forName("utf-8")); for (int i = 0; i <lines.size(); i++) { String line = lines.get(i); if (codeFragment.equals(deleteWhitespace(line))) { lineNum=i+1; break; } } } catch(NoSuchFileException e1){ // interceptor in jfinal.jar } catch (IOException e2) { e2.printStackTrace(); } return lineNum; } private static String deleteWhitespace(String str) { if (isEmpty(str)) { return str; } int sz = str.length(); char[] chs = new char[sz]; int count = 0; for (int i = 0; i < sz; i++) { if (!Character.isWhitespace(str.charAt(i))) { chs[count++] = str.charAt(i); } } if (count == sz) { return str; } return new String(chs, 0, count); } private static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0; } }
在目前官方jar中沒有采用次java類的狀況下,要使用這個功能的朋友請在本身的src下面創建com.jfinal.core包,放入和jar中同名的此類。spa
在類加載的時候classes會在lib以前加載,因此類加載器中加載的是咱們改寫事後的com.jfinal.core.ActionReportercode