Arthas中ognl表达式使用详解,以及通过ognl表达式获取Spring项目上下文中对象参数信息

目录

  • 前言
  • 一、基础操作
    • 1.1、使用`ognl` 表达式获取静态属性和调用静态方法
      • 1.1.1、测试代码
      • 1.1.2、获取静态属性`ognl` 表达式
      • 1.1.3、调用静态方法`ognl` 表达式
    • 1.2、使用`ognl` 表达式操作对象的非静态属性和非静态方法
      • 1.2.1、测试代码
      • 1.2.2、获取非静态属性`ognl` 表达式
      • 1.2.3、调用非静态方法`ognl` 表达式
  • 二、进阶操作
    • 2.1、使用`ognl` 表达式创建一个对象,并对这个对象进行操作
      • 2.1.1、测试代码
      • 2.1.2、`ognl` 表达式
    • 2.2、方法复杂入参`ognl` 表达式
      • 2.2.1、测试代码
      • 2.2.2、自定义对象入参
      • 2.2.3、数组入参
      • 2.2.4、Map入参
  • 三、实践操作
    • 3.1、使用`ognl` 表达式获取Spring上下文中对象,并且进行操作
      • 3.1.1、测试代码
      • 3.1.2、`ognl` 表达式
    • 3.2、使用`ognl` 表达式获取`Nacos`服务列表

前言

      在Arthas中可以通过ognl 表达式在Java程序的运行阶段获取Java类的静态属性值、调用静态方法、new出对象操作成员属性和方法等操作。
      这些能力可以用于排查线上一些奇怪问题,比如感觉线上某个静态属性值不对,可以通过ognl 表达式获取对应静态属性值查看,又或者在Spring项目中注入的对象都是单例的,可以通过getBean(name)的方式获取到具体的单例对象,然后对这个单例对象进行操作,同时也能调用对象中的一些方法,在不提供http接口的情况下实现某些特定的线上测试,本文会对ognl 表达式的一些用法做详细说明。

PS:我这里会使用到SpringBoot工程,在后面会演示一个如何通过ognl 表达式获取Nacos服务列表的操作。

官网地址:https://arthas.gitee.io/doc

  • 基础语法
ognl express -c {hashCode} --classLoaderClass {当前的全路径 ClassLoader 信息} -x {number}
  • 参数说明
参数名称 参数说明
express 执行的表达式
[c:] 执行表达式的 ClassLoader 的 hashcode,默认值是 SystemClassLoader
[classLoaderClass:] 指定执行表达式的 ClassLoader 的 class name
[x] 结果对象的展开层次,默认值 1

一、基础操作

      在基础操作中会说明如果操作静态属性和方法,以及通过静态属性对象操作成员属性和方法。

1.1、使用ognl 表达式获取静态属性和调用静态方法

1.1.1、测试代码

public class OgnlDemo01 {
    public static String s1 = "s1-public-static-v";
    private static String s2 = "s2-private-static-v";
    public static OgnlDemo01 ognlDemo01 = new OgnlDemo01();

    public static void printS12() {
        System.out.println( s1 + "----" + s2);
    }
    public static String getS12() {
        return s1 + "----" + s2;
    }
    public static String setS12(String s1, String s2) {
        OgnlDemo01.s1 = s1;
        OgnlDemo01.s2 = s2;
        return s1 + "----" + s2;
    }
}

1.1.2、获取静态属性ognl 表达式

  • 1、获取公有静态属性
[arthas@1176]$ ognl '@com.kerwin.arthas.demo.OgnlDemo01@s1'     
@String[s1-public-static-v]
  • 2、获取私有静态属性(私有属性同样可以获取到)
[arthas@1176]$ ognl '@com.kerwin.arthas.demo.OgnlDemo01@s2'                                                                                 
@String[s2-private-static-v]
  • 3、获取公有静态属性(自定义对象)
[arthas@1176]$ ognl '@com.kerwin.arthas.demo.OgnlDemo01@ognlDemo01'                                                                         
@OgnlDemo01[                                                                                                                                    
s1=@String[s1-public-static-v],                                                                                                             
s2=@String[s2-private-static-v],                                                                                                            
ognlDemo01=@OgnlDemo01[OgnlDemo01()],                                                                                                   
]

1.1.3、调用静态方法ognl 表达式

  • 1、调用静态方法(无返回值):可以在控制台看见输出打印
[arthas@1176]$ ognl '@com.kerwin.arthas.demo.OgnlDemo01@printS12()'                                                                         
null
  • 2、调用静态方法(有返回值)
[arthas@1176]$ ognl '@com.kerwin.arthas.demo.OgnlDemo01@getS12()'                                                                           
@String[s1-public-static-v----s2-private-static-v] 
  • 3、调用静态方法(有返回值,且有入参):调用方法后属性值被改变
[arthas@6672]$ ognl '@com.kerwin.arthas.demo.OgnlDemo01@setS12("my-s1","my-s2")'                                                            
@String[my-s1----my-s2] 

1.2、使用ognl 表达式操作对象的非静态属性和非静态方法

      要操作非静态属性前提一定是对象已经被new出来,我们通过一个入口找到这个被创建的对象从而操作对象的属性和方法,就比如Spring中可以通过上下文对象获取到一个指定的对象,从而进行操作,又或者通过ognl 表达式直接创建一个对象,然后对这个对象进行操作,又或者使用静态属性对象操作,这里演示会使用静态属性对象ognlDemo02操作方式都差不多,在后续会对其它方式也做讲解。

1.2.1、测试代码

public class OgnlDemo02 {
    public static String s1="s1-public-static-v";
    public String s2="s2-public-v";
    private String s3="s3-private-v";
    public static OgnlDemo02 ognlDemo02=new OgnlDemo02();

    public String getS1() {
        return OgnlDemo02.s1;
    }
    public void setS1(String s1) {
        OgnlDemo02.s1 = s1;
    }
    public String getS2() {
        return s2;
    }
    public void setS2(String s2) {
        this.s2 = s2;
    }
    public String getS3() {
        return s3;
    }
    public void setS3(String s3) {
        this.s3 = s3;
    }
}

1.2.2、获取非静态属性ognl 表达式

  • 1、通过静态属性ognlDemo02对象直接获取公有非静态属性值(这种方法没法获取静态属性)
[arthas@1176]$ ognl '@[email protected]'                                                   
@String[s2-public-v]
  • 2、通过静态属性ognlDemo02对象直接获取私有非静态属性值(这种方法没法获取静态属性)
[arthas@1176]$ ognl '@[email protected]'                                                   
@String[s3-private-v]
  • 3、通过静态属性ognlDemo02对象的成员方法获取对应属性值
[arthas@1176]$ ognl '@[email protected]()'                                               
@String[s1-public-static-v]                                                                                               
[arthas@1176]$ ognl '@[email protected]()'                                               
@String[s2-public-v]

1.2.3、调用非静态方法ognl 表达式

  • 1、通过静态属性ognlDemo02对象调用非静态方法
[arthas@1176]$ ognl '@[email protected]()'                                               
@String[s1-public-static-v]                                                                                               
[arthas@1176]$ ognl '@[email protected]()'                                               
@String[s2-public-v]
[arthas@1176]$ ognl '@[email protected]("my-s2")'                                        
null                                                                                                                      
[arthas@1176]$ ognl '@[email protected]()'                                               
@String[my-s2]

二、进阶操作

2.1、使用ognl 表达式创建一个对象,并对这个对象进行操作

2.1.1、测试代码

public class User {
    private Long uid;
    private String nickName;
    
    public User(Long uid, String nickName) {
        this.uid = uid;
        this.nickName = nickName;
    }
    public Long getUid() {
        return uid;
    }
    public void setUid(Long uid) {
        this.uid = uid;
    }
    public String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
}

2.1.2、ognl 表达式

  • 1、创建一个对象
# 创建一个User对象什么也不做
[arthas@27880]$ ognl 'new com.kerwin.arthas.demo.User(100,"kerwin")'                                              
@User[                                                                                                                        
uid=@Long[100],                                                                                                           
nickName=@String[kerwin],                                                                                             
]
# 创建一个User对象赋值给user,后续可以在别的地方对创建出来的对象进行操作
[arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(100,"kerwin")'                                              
@User[                                                                                                                        
uid=@Long[100],                                                                                                           
nickName=@String[kerwin],                                                                                             
]
  • 2、调用创建出来的对象中的成员方法
[arthas@27880]$ ognl 'new com.kerwin.arthas.demo.User(10001,"kerwin").getNickName()'                                      
@String[kerwin]                                                                                           
]
[arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin").getNickName()'                                      
@String[kerwin]                                                                                           
]
[arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),#user.getNickName()'                                      
@String[kerwin]                                                                                           
]

2.2、方法复杂入参ognl 表达式

2.2.1、测试代码

public class OgnlDemo03 {
    public static OgnlDemo03 ognlDemo03 = new OgnlDemo03();
    private User user;
    private static User staticUser;
    private static List<String> lists;
    private static Map<String,String> maps;

    public User setUser(User user){
        this.user = user;
        return user;
    }
    public static User setStaticUser(User user){
        OgnlDemo03.staticUser = user;
        return user;
    }
    public static User getMyUser(){
       return new User(10002L,"kerwin2");
    }
    public static User changeUser(User user){
        return new User(user.getUid(),user.getNickName()+"---changeUser");
    }
    public static List<String> setLists(List<String> lists){
        OgnlDemo03.lists = lists;
        return lists;
    }
    public static Map<String,String> setMaps(Map<String,String> maps){
        OgnlDemo03.maps = maps;
        return maps;
    }
}

2.2.2、自定义对象入参

  • 1、创建一个User对象将这个对象作为参数传入静态方法中
[arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),@com.kerwin.arthas.demo.OgnlDemo03@setStaticUser(#user)'                                                                                                              
@User[                                                                                                                        
	uid=@Long[10001],                                                                                                         
	nickName=@String[kerwin],                                                                                             
]
  • 2、创建一个User对象,将这个对象作为参数传入静态属性ognlDemo03对象setUser(user)方法中
[arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),@[email protected](#user)'                                                                                                         
@User[                                                                                                                        
	uid=@Long[10001],                                                                                                         
	nickName=@String[kerwin],                                                                                             
]
  • 3、创建一个User对象,在创建一个OgnlDemo03对象,将创建的User对象传入OgnlDemo03对象的setUser(user)方法
[arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),new com.kerwin.arthas.demo.OgnlDemo03().setUser(#user)'                                                                                                               
@User[                                                                                                                        
	uid=@Long[10001],                                                                                                         
	nickName=@String[kerwin],                                                                                             
] 
  • 4、调用getMyUser()方法获取User对象作为changeUser(user)的入参
# {#user1,#user2} 代表将user1、user2这两个对象作为数组输出在控制台,因为这里没有加-x 2默认展开层级为1所以输出的是对象内存地址
[arthas@18904]$ ognl '#user1 = @com.kerwin.arthas.demo.OgnlDemo03@getMyUser(),#user2 = @com.kerwin.arthas.demo.OgnlDemo03@changeUser(#user1),{#user1,#user2}'                                                                                       
@ArrayList[                                                                                                                   
	@User[com.kerwin.arthas.demo.User@30833e75],                                                                              
	@User[com.kerwin.arthas.demo.User@70ca5419],                                                                          
]
# 加上-x 2 可以展开数组内部对象                                                                                          
[arthas@18904]$ ognl '#user1 = @com.kerwin.arthas.demo.OgnlDemo03@getMyUser(),#user2 = @com.kerwin.arthas.demo.OgnlDemo03@changeUser(#user1),{#user1,#user2}' -x 2                                                                                  
@ArrayList[                                                                                                                   
	@User[                                                                                                                        
		uid=@Long[10002],                                                                                                         
		nickName=@String[kerwin2],                                                                                            
	],                                                                                                                        
	@User[                                                                                                                        
		uid=@Long[10002],                                                                                                         
		nickName=@String[kerwin2---changeUser],                                                                               
	],                                                                                                                    
]

2.2.3、数组入参

[arthas@18904]$ ognl '@com.kerwin.arthas.demo.OgnlDemo03@setLists({"k1","k2","k3"})'                                      
@ArrayList[                                                                                                                   
	@String[k1],                                                                                                              
	@String[k2],                                                                                                              
	@String[k3],                                                                                                          
] 

2.2.4、Map入参

[arthas@18904]$ ognl '#map = #{"id":10003L,"nickName":"k3"},@com.kerwin.arthas.demo.OgnlDemo03@setMaps(#map)'             
@LinkedHashMap[                                                                                                               
	@String[id]:@Long[10003],                                                                                                 
	@String[nickName]:@String[k3],                                                                                        
]

三、实践操作

3.1、使用ognl 表达式获取Spring上下文中对象,并且进行操作

3.1.1、测试代码

  • 需要注入IOC容器的Service
@Service
public class OgnlDemoService {
    private String description;

    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
}
  • 自定义一个获取Spring上下文中对象的工具类
@Component
public class SpringApplicationContext implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringApplicationContext.applicationContext = applicationContext;
    }

    /**
     * 通过class获取Bean
     */
    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
    /**
     * 通过name获取 Bean.
     */
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }
}

3.1.2、ognl 表达式

  • 1、获取Spring上下文中的OgnlDemoService对象
# 通过beanName获取
[arthas@30000]$ ognl '@com.kerwin.arthas.utils.SpringApplicationContext@getBean("ognlDemoService")'                       
@OgnlDemoService[                                                                                                             
	description=null,                                                                                                     
]
# 通过class获取                                                                                                                   
[arthas@30000]$ ognl '#OgnlDemoServiceClass [email protected]@class,@com.kerwin.arthas.utils.Sp
ringApplicationContext@getBean(#OgnlDemoServiceClass)'                                                                    
@OgnlDemoService[                                                                                                             
	description=null,                                                                                                     
]
  • 2、操作OgnlDemoService对象中变量和方法
[arthas@30000]$ ognl '@com.kerwin.arthas.utils.SpringApplicationContext@getBean("ognlDemoService").description'           
null                                                                                                                      
[arthas@30000]$ ognl '@com.kerwin.arthas.utils.SpringApplicationContext@getBean("ognlDemoService").setDescription("HelloW orld")'                                                                                                                   
null                                                                                                                      
[arthas@30000]$ ognl '@com.kerwin.arthas.utils.SpringApplicationContext@getBean("ognlDemoService").getDescription()'      
@String[Hello World]

3.2、使用ognl 表达式获取Nacos服务列表

      我这里负载均衡器用的是Ribbon,而Ribbon会使用SpringClientFactory来加载每个服务名称独立的上下文信息,并且会存储在NamedContextFactory中,比如我这里想看当前shopping-order服务在内存中有那些服务地址,先获取到Spring上下文中的SpringClientFactory对象,然后调用getLoadBalancer(name)方法获取到对应服务的负载均衡器,在每个负载均衡器中都实现了BaseLoadBalancerBaseLoadBalancer中的allServerList变量就存储了内存中的服务列表。

[arthas@18316]$ ognl '@com.kerwin.arthas.utils.SpringApplicationContext@getBean("springClientFactory").getLoadBalancer("shopping-order").allServerList'                                                                                 
@ArrayList[                                                                                                                   
	@NacosServer[172.16.8.106:49953],                                                                                         
	@NacosServer[172.16.8.106:8889],                                                                                      
] 

你可能感兴趣的:(java,spring,java,后端,spring,boot)