zoukankan      html  css  js  c++  java
  • [改善Java代码]使用Throwable获得栈信息

    AOP(Aspect Oriented Programming面向切面编程)可以很轻松的控制一个方法调用哪些类,也能够控制哪些方法允许被调用,一般来说切面编程(比如AspectJ)只能控制到方法级别,不能实现代码级别的植入(Weave),比如一个方法被类A的m1方法调用时返回1,在类B的m2方法调用时返回0(同参数的情况下),这就要求被调用者具有识别调用者的能力.在这种情况下,可以使用Throwable获得栈信息,然后鉴别调用者并分别输出,代码如下:

     1 public class Client {
     2     public static void main(String[] args) {
     3         Invoker.m1();
     4         Invoker.m2();
     5     }
     6 
     7 }
     8 
     9 class Foo {
    10     public static boolean m() {
    11         // 取得当前栈信息
    12         StackTraceElement[] sts = new Throwable().getStackTrace();
    13         // 检查是否是m1方法调用
    14         for (StackTraceElement st : sts) {
    15             if (st.getMethodName().equals("m1")) {
    16                 return true;
    17             }
    18         }
    19         return false;
    20     }
    21 }
    22 
    23 class Invoker {
    24     // 该方法打印出true
    25     public static void m1() {
    26         System.out.println(Foo.m());
    27     }
    28 
    29     // 该方法打印出false
    30     public static void m2() {
    31         System.out.println(Foo.m());
    32     }
    33 }

    Invoker类,两个方法m1和m2都调用了Foo的m方法,都是无参调用,返回值却不相同.这是因为Throwable类发挥效能了.

    JVM在创建一个Throwable类及其子类时会把当前线程的栈信息记录下来,以便在输出异常时准确定位异常原因,看Throwable的源代码...

    public class Throwable implements Serializable {
        //出现异常的栈记录
        private StackTraceElement[] stackTrace;
        //默认的构造函数
        public Throwable() {
            //记录栈帧
            fillInStackTrace();
        }
        //本地方法,抓取执行时的栈信息
        public synchronized native Throwable fillInStackTrace() {}
    }

    出现异常时(或主动声明一个Throwabke对象时),JVM会通过fillInStackTrace方法记录下栈帧信息,然后生成一个Throwable对象,这样我们就可以知道类间的调用顺序,方法名称以及当前行号等了.

    获得栈信息可以对调用者进行判断,然后决定不同的输出,比如上面的m1和m2方法,同样是输入参数,同样的调用方法,但是输出却不同,这看起来像一个Bug:方法m1电泳m方法是正常显示,而方法m2调用却返回错误数据.

    因此我们虽然可以依据调用者不同产生不同的逻辑,但这仅仅局限在对方法的广泛认知上.

    更多的时候我们用m方法的变形体代码如下:

     1 public class Client {
     2     public static void main(String[] args) {
     3         Invoker.m1();
     4         Invoker.m2();
     5     }
     6 
     7 }
     8 
     9 class Foo {
    10     public static boolean m() {
    11         // 取得当前栈信息
    12         StackTraceElement[] sts = new Throwable().getStackTrace();
    13         // 检查是否是m1方法调用
    14         for (StackTraceElement st : sts) {
    15             if (st.getMethodName().equals("m1")) {
    16                 return true;
    17             }
    18         }
    19         throw new RuntimeException("除m1方法外,该方法不允许其他方法调用");
    20     }
    21 }
    22 
    23 class Invoker {
    24     // 该方法打印出true
    25     public static void m1() {
    26         System.out.println(Foo.m());
    27     }
    28 
    29     // 该方法打印出false
    30     public static void m2() {
    31         System.out.println(Foo.m());
    32     }
    33 }

    只是把return false 替换成了一个运行期异常,除了m1方法外,其他方法调用都会产生异常.除了m1方法外,其他方法调用都会产生异常,该方法常用作离线注册码校验,当破解者试图暴力破解时,由于主执行者不善期望的值,因此会返回一个经过包装和混淆的异常信息,大大增加了破解的难度.

  • 相关阅读:
    基于MATLAB静态目标分割的药板胶囊检测
    基于YCbCr色彩模型的简易肤色识别器
    思考一个问题STM32的
    压力校准仪开发日志-10-21 迪文屏通信命令记录
    迪文屏使用须知
    结构体变量字节填充
    sizeof 的妙用
    结构体内存的对齐方式
    内存对齐
    中国游戏产业走向世界
  • 原文地址:https://www.cnblogs.com/DreamDrive/p/5622976.html
Copyright © 2011-2022 走看看