Java通过反射获取class信息

前言

在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为反射机制。

我们知道,Java语言不是动态语言,但是他却有非常突出的动态相关机制,反射机制。

代码

我们可以通过反射机制获取一个class的相关信息。

1. 利用Javassist获取class信息

Javassist是一个动态类库,可以用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。

    public static void getClassInfoByJavassist(Class clazz) {
           ClassPool classPool = ClassPool.getDefault();
            classPool.insertClassPath(new ClassClassPath(clazz));
            try {
                System.out.println("class-->"+clazz.getName());
                CtClass ctClass = classPool.get(clazz.getName());

                //获取常量信息
                CtField[] fields=ctClass.getDeclaredFields();
                for(int i=0;i<fields.length;i++) {
                    System.out.println("object="+fields[i].getName()+"-->value="+fields[i].getConstantValue()+"-->type="+fields[i].getType().getName());
                }

                //获取方法信息
                CtMethod[] ctMethods = ctClass.getMethods();
                for (CtMethod ctMethod : ctMethods) {
                    //排除equals,hash,toString等方法
                    if (!clazz.getName().equals(ctMethod.getDeclaringClass().getName())) {
                        continue;
                    }
                    MethodInfo methodInfo = ctMethod.getMethodInfo();
                    CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
                    LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
                    if (attr == null) {
                        // exception
                    }
                    String[] paramNames = new String[ctMethod.getParameterTypes().length];
                    TreeMap<Integer, String> sortMap = new TreeMap<Integer, String>();
                    for (int i = 0; i < attr.tableLength(); i++) 
                        sortMap.put(attr.index(i), attr.variableName(i));
                    int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
                    paramNames = Arrays.copyOfRange(sortMap.values().toArray(new String[0]), pos, paramNames.length + pos);
                    CtClass[] types=ctMethod.getParameterTypes();
                    for(int i=0;i<paramNames.length;i++) {
                        System.out.println("class="+clazz.getSimpleName()+"-->method="+ctMethod.getName()+"-->isStatic="+Modifier.isStatic(ctMethod.getModifiers())+"-->paramsType="+types[i].getName()+"-->paramsName="+paramNames[i]);
                    }

                }           
            }catch (Exception e) {
                e.printStackTrace();
            }         
    }

我们新建一个Demo.class

  public class Demo {
    public static String getName(String str) {
        String s="123";
        return str+s;
    }

    public Integer doSomething(String str,double a,Map<String, String> map,List<String> list) {
        //doSomething
        Integer i=0;
        return i;
    }

    private static final int NUM=1;

    private static String s="1234";

    static {
        s="5678";
    }
}

调用方法 getClassInfoByJavassist(Demo.class),输出class信息。

class-->com.zwt.reflect.Demo
object=NUM-->value=1-->type=int
object=s-->value=null-->type=java.lang.String
class=Demo-->method=getName-->isStatic=true-->paramsType=java.lang.String-->paramsName=str
class=Demo-->method=doSomething-->isStatic=false-->paramsType=java.lang.String-->paramsName=str
class=Demo-->method=doSomething-->isStatic=false-->paramsType=double-->paramsName=a
class=Demo-->method=doSomething-->isStatic=false-->paramsType=java.util.Map-->paramsName=map
class=Demo-->method=doSomething-->isStatic=false-->paramsType=java.util.List-->paramsName=list 

2. 通过spring里的LocalVariableTableParameterNameDiscoverer获取paramsName,使用jdk自带reflect反射类获取class其他信息

在jdk1.8以下java版本中,根据jdk自带reflect包,可以拿到大部分class信息,唯一拿不到的是参数name,我们可以借助spring包里的LocalVariableTableParameterNameDiscoverer去获取paramsName。

 public static void getClassInfoBySpringAndReflect(Class clazz) {
        try {
            LocalVariableTableParameterNameDiscoverer u = 
                    new LocalVariableTableParameterNameDiscoverer();
                Method[] methods =clazz.getDeclaredMethods();
                Field[] fields=clazz.getDeclaredFields();
                for(int i=0;i<fields.length;i++) {
                    //设置成可以access的,否则get(clazz)报错,无法读取private属性
                    fields[i].setAccessible(true);
                    System.out.println("object="+fields[i].getName()+"-->value="+fields[i].get(clazz)+"-->type="+fields[i].getType().getName());
                }
                for(Method method:methods) {
                    //使用spring LocalVariableTableParameterNameDiscoverer 获取paramsName
                    String[] params = u.getParameterNames(method);
                    Class<?> [] classType=method.getParameterTypes();
                    for (int i = 0; i < params.length; i++) {
                        System.out.println("class="+clazz.getSimpleName()+"-->method="+method.getName()+"-->isStatic="+Modifier.isStatic(method.getModifiers())+"-->paramsType="+classType[i].getName()+"-->paramsName="+params[i]);
                    }
                }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

调用getClassInfoBySpringAndReflect(Demo.class),也可以拿到class信息。

3.使用jdk1.8及以上java版本获取class信息

若jdk版本较高,完全可以不用借助第三方jar包而获取class信息。

jdk1.8以上,添加了Parameter类,可以获取参数paramsName.

    public static void getClassInfoByJava8AndReflect(Class clazz){
        try {
            Method[] methods = clazz.getDeclaredMethods();
            Field[] fields=clazz.getDeclaredFields();
            for(int i=0;i<fields.length;i++) {
                //设置成可以access的,否则get(clazz)报错,无法读取private属性
                fields[i].setAccessible(true);
                System.out.println("object="+fields[i].getName()+"-->value="+fields[i].get(clazz)+"-->type="+fields[i].getType().getName());
            }
            for (Method method : methods) {
                //使用jdk1.8Parameter获取paramsNames
                Parameter[] params = method.getParameters();
                Class<?> [] classType=method.getParameterTypes();
                for (int i = 0; i < params.length; i++) {
                    System.out.println("class="+clazz.getSimpleName()+"-->method="+method.getName()+"-->isStatic="+Modifier.isStatic(method.getModifiers())+"-->paramsType="+classType[i].getName()+"-->paramsName="+params[i]);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

调用getClassInfoByJava8AndReflect(Demo.class),获取class信息。

其他

反射优点:

合理的使用反射机制可以有效降低代码冗余及代码量。并且可以让应用程序实现一些几乎不可能做到的事情。反射属于Java语言里比较高级的一个特性。

反射缺点:

如果不合理的使用反射,可能降低系统性能。

而且非常重要的一点,我们看如上代码,有一句fields[i].setAccessible(true);
这是在设置私有属性可以访问,显然,这破坏了代码的抽象性,而且可能导致安全问题的产生。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值