文档章节

给注解打断点的一种方法

zgw06629
 zgw06629
发布于 2015/04/27 12:32
字数 1227
阅读 142
收藏 0

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 extends Annotation> T java.lang.reflect.Method.getAnnotation(Class<T> annotationClass)
Annotation[] java.lang.reflect.Method.getDeclaredAnnotations()
Annotation[][] java.lang.reflect.Method.getParameterAnnotations()

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

于是在本地覆盖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.如下所示:

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

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

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

public <T extends Annotation> T getAnnotation(Class<T> annotationClass){
    //添加了如下的代码
    if(annotationClass.getName().startsWith("net.paoding.rose.web.annotation"))
        System.out.println(annotationClass.getName());
    //......
}

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

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

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

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



上图左侧显示,是在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视图中的看到的内容为:

由左侧可知,是被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<result.length; i++){
    for(int j=0; j<result[i].length; j++){
        if(result[i][j].toString().indexOf("net.paoding.rose.web.annotation")!=-1) 
            System.out.println(result[i][j].toString()); 
    }
}

这时debug视图中的内容为:

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

Annotation[][] parameterAnnotations = method.getParameterAnnotations();
String[] names = new String[parameterTypes.length];
Map<String, Integer> counts = new HashMap<String, Integer>();
for (int i = 0; i < names.length; 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虚拟机)

  1. 下载openjdk(http://download.java.net/openjdk/jdk7/)

  2. 安装依赖包
    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

  3. 在解压后的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

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

     

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



© 著作权归作者所有

共有 人打赏支持
zgw06629
粉丝 16
博文 54
码字总数 30471
作品 0
海淀
程序员
aspect文件debug不了

用eclipse开发,java文件中添加@aspect做切面,切面方法用@Around注解的,可以确定的是切面方法都执行到了,但是在@Around方法里打断点进不去,不知什么原因,有谁碰到过这种问题吗,怎么解决...

有时候
2014/03/14
111
0
SpringBoot同时集成Redis和Guava作为缓存组件--进一步分析代码

请先看 https://my.oschina.net/u/3866531/blog/1840386 CompositeCacheManager类 Composite,混合的,混成的 Spring提供CompositeCacheManager的主要目的就是为了混合使用多种缓存时进行管理...

karma123
07/05
0
0
使用spring-aspects包无效的问题

@EnableTransactionManagement(mode=AdviceMode.ASPECTJ) @EnableCaching(mode=AdviceMode.ASPECTJ) 使用以上注解,事务和缓存没有任何反应,打断点跟踪发现应该切入的对象也没有被代理。加入...

吃菜的鸟
2016/05/27
279
2
Android CoordinatorLayout之源码解析

源码基于,不同版本可能有所差异。 一、开始 上一篇Android CoordinatorLayout之自定义Behavior中,我们简单介绍了以及如何自定义。所以这次我们从源码的角度分析的内部实现机制,以便它更好...

Othershe
2017/12/15
0
0
Spring对Configuraion的解析

Spring配置Bean有多种形式,第一种常用的就是通过XML文件配置,另外一种就是通过@Configuraion声明类,表明是一个配置文件,他的本质作用和XML是相同的,作为Bean的载体。今天我们来和大家聊...

数齐
05/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

[雪峰磁针石博客]软件测试专家工具包1web测试

web测试 本章主要涉及功能测试、自动化测试(参考: 软件自动化测试初学者忠告) 、接口测试(参考:10分钟学会API测试)、跨浏览器测试、可访问性测试和可用性测试的测试工具列表。 安全测试工具...

python测试开发人工智能安全
今天
2
0
JS:异步 - 面试惨案

为什么会写这篇文章,很明显不符合我的性格的东西,原因是前段时间参与了一个面试,对于很多程序员来说,面试时候多么的鸦雀无声,事后心里就有多么的千军万马。去掉最开始毕业干了一年的Jav...

xmqywx
今天
2
0
Win10 64位系统,PHP 扩展 curl插件

执行:1. 拷贝php安装目录下,libeay32.dll、ssleay32.dll 、 libssh2.dll 到 C:\windows\system32 目录。2. 拷贝php/ext目录下, php_curl.dll 到 C:\windows\system32 目录; 3. p...

放飞E梦想O
今天
0
0
谈谈神秘的ES6——(五)解构赋值【对象篇】

上一节课我们了解了有关数组的解构赋值相关内容,这节课,我们接着,来讲讲对象的解构赋值。 解构不仅可以用于数组,还可以用于对象。 let { foo, bar } = { foo: "aaa", bar: "bbb" };fo...

JandenMa
今天
1
0
OSChina 周一乱弹 —— 有人要给本汪介绍妹子啦

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享水木年华的单曲《中学时代》@小小编辑 手机党少年们想听歌,请使劲儿戳(这里) @须臾时光:夏天还在做最后的挣扎,但是晚上...

小小编辑
今天
68
8

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部