目前的的ActionReporter可以定位到当前方法调用过的Controller,点击eclipse控制台中的链接可以定位到改类的第一行,但是并不能直接定位到调用方法的那一行。
为了进一步提供开发者的体验,我加强了ActionReporter的源代码定位功能,能直接定位到调用的方法上面。先看下面效果图。
当请请求调用了MobileBindController的heart方法,控制可以直接打出heart方法在 MobileBindController.java中的行数。
在java代码中虽然有能得到当前方法栈上的代码行数,但是由于jfinal中的aop并没有使用第三方的字节码工具修改原类的字节码,所有没有办法在方法调用之前知道要调用方法的行数。如果使用spring aop可以把这个功能切到目标方法调用的前面,应该可以达到效果。(没实际操作过还..)
在不修改字节码的情况下,我暂时只想到利用源码来计算行数..反正都是开发阶段,这样其实也无大碍..原理很简单.得到调用的类和方法然后到对应源文件去找该方法的字符串所在行。实现不够优雅但是功能还是挺实用的。
代码中删除字符串空格的方法直接copy自commons-lang。
代码再jdk7下编写的。7以下的需要把遍历文件那段代码稍微替换以下就ok了。
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中同名的此类。
在类加载的时候classes会在lib之前加载,所以类加载器中加载的是我们改写过后的com.jfinal.core.ActionReporter