java 断点跳到注释,给注解打断点的一种方法

Controllor中有如下的方法,

@HttpFeatures(contentType="application/json")

@Get("send/{appId:[0-9]+}")

@Post("send/{appId:[0-9]+}")

public String sendMail(@Param("data") String dataBase64,@Param("appId") long appId) {

//...

}

想知道这几个注解何时被谁于何处给调用. 又不能在注解所在行直接加断点.  只好退而求其次在Method(java.lang.reflect.Method)的注解相关方法中打断点

 T java.lang.reflect.Method.getAnnotation(Class annotationClass)

Annotation[] java.lang.reflect.Method.getDeclaredAnnotations()

Annotation[][] java.lang.reflect.Method.getParameterAnnotations()

但是在断点处,看不到变量的实际值.如下所示:

40747816914451e7b6ea6e77679e7923.png

于是在本地覆盖java.lang.reflect.Method(即在当前工程中创建一个包为java.lang.reflect,在该包下创建一个名为Method的类,内容和jdk源码一样.)并在相关处添加断点. 但压根就不进来,还是找jdk中的Method.看来没办法覆盖java核心类库(如rt.jar)中的类了(因为它们在程序启动时是被顶层(或根)类加载器所加载,一旦完成加载,就不会重复加载(同名类)了.).

那么假如将rt.jar中的Method.class给删除了呢,再在本地覆盖Method,不就可以加载本地的Method了吗. 但证实不可行,因为立即就报错了:

Error occurred during initialization of VM

java/lang/NoClassDefFoundError: java/lang/reflect/Method

显然Method被其他核心类所引用.

只好自己编译一个jdk了(且是debug版的jdk,否则调试时还是看不到变量值). 幸好Ubuntu中编译jdk还是挺顺利的.

于是修改项目的JRE为新编译的支持debug的jdk.如下所示:

98915a4d8525051e41a0b3bb560e2ee5.png

这时可以清晰的看到变量值了.如下所示:

a8cf5171c8e8a8722ef2a4ae2f37b6dc.png

但是有太多注解了,而只想看到net.paoding.rose.web.annotation包下的那些注解.如net.paoding.rose.web.annotation.HttpFeatures.

于是修改Method的方法(当然需要重新编译了),如

public  T getAnnotation(Class annotationClass){

//添加了如下的代码

if(annotationClass.getName().startsWith("net.paoding.rose.web.annotation"))

System.out.println(annotationClass.getName());

//......

}

将断点放在if方法内部, 这样进入断点的注解就是想要探究的注解了.

其他几个方法做同样的处理.

虽然还是有一些干扰项,如net.paoding.rose.web.annotation.Ignored.但还是可以接受的.

很快就知道HttpFeatures注解是被谁在何处给调用的了.

b50ab9883fcebb5d145d6025bba9389a.png

上图左侧显示,是在ActionEngin的构造方法中98行被调用.该行代码如下所示:

HttpFeatures httpFeatures = method.getAnnotation(HttpFeatures.class);

接下来看Get和Post在何处被调用. 这次是调用的Method另一个方法:

public Annotation[] getDeclaredAnnotations(){

//对源代码做了如下的修改:

//return AnnotationParser.toArray(declaredAnnotations()); //原代码

Annotation[] annotations = AnnotationParser.toArray(declaredAnnotations());

for(Annotation anno : annotations)

if(anno.toString().indexOf("net.paoding.rose.web.annotation")!=-1)

System.out.println(anno.toString());

return annotations;

}

这时在debug视图中的看到的内容为:

f4fa9b6df1c871c6a118229f4d467267.png

由左侧可知,是被ControllerRef在collectsShotcutMappings方法中所调用. 其相关代码为:

Annotation[] annotations = method.getAnnotations();

for (Annotation annotation : annotations) {

if (annotation instanceof Delete) {

restMethods.put(ReqMethod.DELETE, ((Delete) annotation).value());

} else if (annotation instanceof Get) {

restMethods.put(ReqMethod.GET, ((Get) annotation).value());

...

接下来看方法参数中的注解(如@Param("data") String dataBase64)何时何处被调用,显然应该在getParameterAnnotations方法中打断点.

同样在getParameterAnnotations添加了如下的代码:

for(int i=0; i

for(int j=0; j

if(result[i][j].toString().indexOf("net.paoding.rose.web.annotation")!=-1)

System.out.println(result[i][j].toString());

}

}

这时debug视图中的内容为:

f77c7817a3fc5f51ba46f06de9ef29a5.png

由左侧可知,是在ParameterNameDiscovererImpl.getParameterNames处调用的,相关代码为:

Annotation[][] parameterAnnotations = method.getParameterAnnotations();

String[] names = new String[parameterTypes.length];

Map counts = new HashMap();

for (int i = 0; i 

Annotation[] annotations = parameterAnnotations[i];

for (Annotation annotation : annotations) {

String name = null;

if (annotation instanceof Param) {

name = ((Param) annotation).value();

......

同理可以修改Class类,在注解相关方法中添加一些代码,断点打在相应位置,可以得到类注解(如@Path("/mail"))的调用信息.

注:

若有Rose的源代码的话,导入到Eclipse中,直接在java类中搜索Get, Post, HttpFeatures即可.

补充:

Ubuntu环境编译OPENJDK(摘自周志明深入理解JAVA虚拟机)

安装依赖包

sudo apt-get install build-essential gawk m4 openjdk-6-jdk libasound2-dev libcups2-dev libxrender-dev xorg-dev xutils-dev x11proto-print-dev binutils libmotif3 libmotif-dev ant

在解压后的openjdk目录下创建如下的脚本,内容为:

export LANG=C

#启动JDK 需修改为当前系统已安装的jdk目录 毕竟jdk中的大部分类还是java写的,编译还得依赖已有的环境

export ALT_BOOTDIR=${JAVA_HOME}

export ALLOW_DOWNLOADS=true

#并行编译的线程数,设置为和CPU内核数量一致即可

export HOTSPOT_BUILD_JOBS=4

export ALT_PARALLEL_COMPILE_JOBS=4

export SKIP_COMPARE_IMAGES=true

export USE_PRECOMPILED_HEADER=true

export SKIP_FASTDEBUG_BUILD=false

export DEBUG_NAME=fastdebug

BUILD_DEPLOY=false

BUILD_INSTALL=false

#编译后的JDK输出目录 ${openjdk_install_path}为当前openjdk目录

export ALT_OUTPUTDIR=${openjdk_install_path}/build

unset JAVA_HOME

unset CLASSPATH

make 2>&1 | tee $ALT_OUTPUTDIR/build.log

注: 标红部分表示需修改为自己环境的路径.

执行该脚本,若一切顺利的话,在${openjdk_install_path}下会看到一个目录(build-fastdebug),进入该目录,会有一个j2sdk-image的目录,该目录便是编译好的jdk对应的目录. 若修改了源码(源码的路径为:${openjdk_install_path}/jdk/src/share/classes)的话, 当然需要重新编译(即再次执行上述脚本).

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值