线上排查问题神器Arthas应用(三)-ognl命令

2,292 阅读3分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。

ognl 动态执行线上的代码

能够调用线上的代码,是不是很神奇了。感觉哪段代码执行有问题,但是又没有日志,就可以使用这个方法动态调用目标方法了。

我们下面的案例都是基于这段代码执行,User类:

public class User {
    private int id;
    private String name;
​
​
    public User() {
    }
​
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
​
    public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
}

DeadLockTest类:

public class DeadLockTest {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
    private static List<String> names = new ArrayList<>();
    private List<String> citys = new ArrayList<>();
​
​
​
    public static String add() {
        names.add("zhangsan");
        names.add("lisi");
        names.add("wangwu");
        names.add("zhaoliu");
​
        return "123456";
    }
    public List<String> getCitys() {
        DeadLockTest deadLockTest = new DeadLockTest();
        deadLockTest.citys.add("北京");
​
        return deadLockTest.citys;
    }
​
​
    public static List<User> addUsers(Integer id, String name) {
        List<User> users = new ArrayList<>();
        User user = new User(id, name);
        users.add(user);
        return users;
    }
​
​
    public static void main(String[] args) {
​
​
        new Thread(() -> {
            synchronized (lock1) {
                try {
                    System.out.println("thread1 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
​
                }
                synchronized (lock2) {
                    System.out.println("thread1 end");
                }
            }
        }).start();
​
        new Thread(() -> {
            synchronized (lock2) {
                try {
                    System.out.println("thread2 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
​
                }
                synchronized (lock1) {
                    System.out.println("thread2 end");
                }
            }
        }).start();
    }
}

1)获取静态函数

> 返回值是字符串

ognl '@全路径类名@静态方法名("参数")'

示例1:在DeadLockTest类中有一个add静态方法,我们来看看通过ognl怎么执行这个静态方法。执行命令

ognl '@com.lxl.jvm.DeadLockTest@add()'
  
其中,第一个@后面跟的是类的全名称;第二个@跟的是属性或者方法名,如果属性是一个对象,想要获取属性里面的属性或者方法,直接打.属性名/方法名 即可

运行效果: image

我们看到了这个对象的返回值是123456

> 返回值是对象

ognl '@全路径类名@静态方法名("参数")' -x 2

这里我们可以尝试一下替换-x 2 为 -x 1 ;-x 3;

* 案例1:返回对象的地址。不加 -x 或者是-x 1
ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")'
或
ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 1

返回值

image

* 案例2:返回对象中具体参数的值。加 -x 2
ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 2

返回值

image

* 案例3:返回对象中有其他对象
  • 命令:
ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 2

执行结果:

image

-x 2 获取的是对象的值,List返回的是数组信息,数组长度。

  • 命令:
ognl '@com.lxl.jvm.DeadLockTest@addUsers(1,"zhangsan")' -x 3

执行结果:

image

-x 3 打印出对象的值,对象中List列表中的值。

* 案例4:方法A的返回值当做方法B的入参
ognl '#value1=@com.lxl.jvm.DeadLockTest@getCitys(), #value2=@com.lxl.jvm.DeadLockTest@generatorUser(1,"lisi",#value1), {#value1,#value2}' -x 2

image

> 方法入参是简单类型的列表

ognl '@com.lxl.jvm.DeadLockTest@returnCitys({"beijing","shanghai","guangdong"})'

image

> 方法入参是一个复杂对象

ognl '#value1=new com.lxl.jvm.User(1,"zhangsan"),#value1.setName("aaa"), #value1.setCitys({"bj", "sh"}), #value2=@com.lxl.jvm.DeadLockTest@addUsers(#value1), #value2' -x 3

image

> 方法入参是一个map对象

ognl '#value1=new com.lxl.jvm.User(1,"zhangsan"), #value1.setCitys({"bj", "sh"}), #value2=#{"mum":"zhangnvshi","dad":"wangxiansheng"}, #value1.setFamily(#value2), #value1' -x 2

image

2)获取静态字段

ognl '@全路径类名@静态属性名'

示例:在DeadLockTest类中有一个names静态属性,下面来看看如何获取这个静态属性。执行命令:

ognl '@com.lxl.jvm.DeadLockTest@names'
  
其中,第一个@后面跟的是类的全名称;第二个@跟的是属性或者方法名,如果属性是一个对象,想要获取属性里面的属性或者方法,直接打.属性名/方法名 即可  

运行效果:

image

第一次执行获取属性命令,返回的属性是一个空集合;然后执行add方法,往names集合中添加了属性;再次请求names集合,发现有4个属性返回。

3) 获取实例对象

ognl '#value1=new com.lxl.jvm.User(1,"zhangsan"),#value1.setName("aaa"), #value1.setCitys({"bj", "sh"}), {#value1}' -x 2

获取实例对象,使用new关键字,执行结果:

image

\