java的反射可以绕过访问权限,访问到类的非公有方法和成员。可能这点会引起安全性的讨论。反射的使用帮助解决很多复杂的问题,其运行时的类型检查,动态调用,代理的实现等,反射为我们写程序带来了很大的灵活性,很多功能都是基于反射。
利用反射还可以访问内部类、匿名内部类的私有属性。
下面提供一种利用安全管理器及反射访问类非公有方法和成员的方法。
Java 运行时依靠一种安全性管理器来检验调用代码对某一特定的访问而言是否有足够的权限。具体来说,安全性管理器是 java.lang.SecurityManager 类或扩展自该类的一个类,且它在运行时检查某些应用程序操作的权限。换句话说,所有的对象访问在执行自身逻辑之前都必须委派给安全管理器,当访问受到安全性管理器的控制,应用程序就只能执行那些由相关安全策略特别准许的操作。因此安全管理器一旦启动可以为代码提供足够的保护。默认情况下,安全性管理器是没有被设置的,除非代码明确地安装一个默认的或定制的安全管理器,否则运行时的访问控制检查并不起作用。我们可以通过这一点在运行时避开 Java 的访问控制检查,达到我们访问非公有成员变量或方法的目的。为能访问我们需要的非公有成员,我们还需要使用 Java 反射技术。Java 反射是一种强大的工具,它使我们可以在运行时装配代码,而无需在对象之间进行源代码链接,从而使代码更具灵活性。在编译时,Java 编译程序保证了私有成员的私有特性,从而一个类的私有方法和私有成员变量不能被其他类静态引用。然而,通过 Java 反射机制使得我们可以在运行时查询以及访问变量和方法。由于反射是动态的,因此编译时的检查就不再起作用了。
下面的代码演示了如何利用安全性管理器与反射机制访问私有变量。
|
|
其中 getField(instance.getClass(), fieldName) 通过反射机制获得对象属性,使用set方法可以重新设置变量的值,如field.set(instance, newValue); 。如果存在安全管理器,方法首先使用 this 和 Member.DECLARED 作为参数调用安全管理器的 checkMemberAccess 方法,这里的 this 是 this 类或者成员被确定的父类。 如果该类在包中,那么方法还使用包名作为参数调用安全管理器的 checkPackageAccess 方法。 每一次调用都可能导致 SecurityException。当访问被拒绝时,这两种调用方式都会产生 securityexception 异常 。
setAccessible(true) 方法通过指定参数值为 true 来禁用访问控制检查,从而使得该变量可以被其他类调用。我们可以在我们所写的类中,扩展一个普通的基本类 java.lang.reflect.AccessibleObject 类。这个类定义了一种 setAccessible 方法,使我们能够启动或关闭对这些类中其中一个类的实例的接入检测。这种方法的问题在于如果使用了安全性管理器,它将检测正在关闭接入检测的代码是否允许这样做。如果未经允许,安全性管理器抛出一个例外。
除访问私有变量,我们也可以通过这个方法访问私有方法。
|
|
获得私有方法的原理与获得私有变量的方法相同。当我们得到了函数后,需要对它进行调用,这时我们需要通过 invoke() 方法来执行对该函数的调用,代码示例如下:
|
|
利用安全管理器及反射,可以在不修改源码的基础上访问私有成员,为测试带来了极大的方便。尤其是在编译期间,该方法可以顺利地通过编译。但同时该方法也有一些缺点。第一个是性能问题,用于字段和方法接入时反射要远慢于直接代码。第二个是权限问题,有些涉及 Java 安全的程序代码并没有修改安全管理器的权限,此时本方法失效。
常见问题:
java反射调用静态方法
|
|
一、传统通过反射取得函数的参数和返回值
|
|
|
准备一个演示类
package my; public class Data { private static int data; private String name; private Data(){ } private Data(int data,String name){ this.data = data; this.name = name; } private void showName(){ System.out.println("Name is:"+name); } private static void showData(){ System.out.println("Data is:"+getData()); } private static int getData(){ return data; } private void setData(int data){ this.data = data; } private void setString(String name){ this.name = name; } }
2.具体访问步骤
package my; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Start { public static void main(String[] args) { try { Class dataType = Class.forName("my.Data"); // 访问私用的构造方法来创建对象实例 Constructor constructor = dataType .getDeclaredConstructor(new Class[] { int.class, String.class }); constructor.setAccessible(true);//访问私有成员方法,这句很关键 Data data = (Data) constructor.newInstance(new Object[] { new Integer(50), new String("hello") }); // 开始访问私有的静态方法showData来打印对象信息 Method showDataMethod = dataType.getDeclaredMethod("showData", new Class[] {}); showDataMethod.setAccessible(true); showDataMethod.invoke(null, new Object[] {});//output:Data is:50 //开始访问私有方法showName来打印对象信息 Method showNameMethod = dataType.getDeclaredMethod("showName", new Class[] {}); showNameMethod.setAccessible(true); showNameMethod.invoke(data, new Object[] {});//output:Name is:hello // 开始访问的setData方法来更改对象信息 Method setDataMethod = dataType.getDeclaredMethod("setData", new Class[] { int.class }); setDataMethod.setAccessible(true); setDataMethod.invoke(data, new Object[]{new Integer(100)});//设置data域 为100 //开始访问静态私有的成员变量 Field dataField = dataType.getDeclaredField("data"); dataField.setAccessible(true); System.out.println(dataField.getInt(data));//output:100 dataField.setInt(null, 200);//再次修改私有成员data的值 System.out.println(dataField.getInt(data));//output:200 //开始访问私有成员变量 Field nameField = dataType.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(data,new String("Fuck!"));//修改私有变量name的值 showNameMethod.invoke(data, new Object[] {});//Name is:Fuck! } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } }
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public interface MyInterface {
ConcurrentMap<String, String> map=new ConcurrentHashMap<String, String>();
}
class MyImpl implements MyInterface{
private MyImpl(){
map.put("f", "dgd");
map.put("c", "nia");
}
public void PrintMap(){
Iterator it=map.keySet().iterator();
while(it.hasNext()){
String ss=(String)it.next();
System.out.println(ss+":"+map.get(ss));
}
}
}
public static void main(String[] args) throws Exception {
Class dataType = Class.forName("com.jensen.study.reflection.MyImpl");
Constructor constructor = dataType
.getDeclaredConstructor(new Class[] { });
constructor.setAccessible(true);// 访问私有成员方法,这句很关键
MyImpl data = (MyImpl) constructor.newInstance(new Object[] {
});
Field mapField = dataType.getInterfaces()[0].getDeclaredField("map");
mapField.setAccessible(true);
ConcurrentMap<String, String> map=null;
map=(ConcurrentMap<String, String>)mapField.get(new Object());
Iterator it=map.keySet().iterator();
System.out.println("---------------------");
while(it.hasNext()){
String ss=(String)it.next();
System.out.println(ss+":"+map.get(ss));
}
System.out.println("---------------------");
map.put("haha", "dafkasdfjkas");
Method setDataMethod = dataType.getDeclaredMethod("PrintMap",
new Class[] { });
setDataMethod.setAccessible(true);
setDataMethod.invoke(data, new Object[] {});// 设置data域