前言
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为反射机制。
我们知道,Java语言不是动态语言,但是他却有非常突出的动态相关机制,反射机制。
代码
我们可以通过反射机制获取一个class的相关信息。
1. 利用Javassist获取class信息
Javassist是一个动态类库,可以用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 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) { 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) { } 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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) { Integer i=0; return i; } private static final int NUM=1; private static String s="1234"; static { s="5678"; } }
|
调用方法 getClassInfoByJavassist(Demo.class),输出class信息。
1 2 3 4 5 6 7 8 9
| 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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++) { 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) { 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public static void getClassInfoByJava8AndReflect(Class clazz){ try { Method[] methods = clazz.getDeclaredMethods(); Field[] fields=clazz.getDeclaredFields(); for(int i=0;i<fields.length;i++) { 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) { 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);
这是在设置私有属性可以访问,显然,这破坏了代码的抽象性,而且可能导致安全问题的产生。