您的位置:首页 > 其它

无法反射到想要的构造函数时该如何根据Class对象构造Class实例?

2015-12-15 01:12 393 查看
这还是一个在开发JPJson的过程中发现的问题.

在确定JPJson能够接受的javaBean的Class定义时,我原本想严格按照JavaBean的定义来.

即类中只有private的Field和为想暴露的Field提供get和set方法.但是在实现之前我还是测试了一下Gson是如何做的.在测试Gson时,我发现Gson的适应能力是非常强的.类中只要有这个Field,那么Gson就会把数据attach上去,无论是private还是public.甚至最让我震惊的就是如果Class内没有定义一个空参数的构造函数的时候,Gson仍然能够正常的构造对象并反射数据上去.这一点彻彻底底的震惊了我.

当然还是二话不说上代码.

[code]  Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
  Field f = unsafeClass.getDeclaredField("theUnsafe");
  f.setAccessible(true);
  final Object unsafe = f.get(null);
  final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
  return new UnsafeAllocator() {
    @Override
    @SuppressWarnings("unchecked")
    public <T> T newInstance(Class<T> c) throws Exception {
      return (T) allocateInstance.invoke(unsafe, c);
    }
  };


需要额外关注的是这几行

[code]Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object unsafe = f.get(null);
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);


首先是通过
Class.forName()
方法获取一个类对象
sun.misc.Unsafe
的实例.这里其实一般情况下不使用反射来获取这个Class对象也是可以的.但是仅限于HotSpot虚拟机.所以使用了
forName()
方法以保证普遍的适应性,当没有这个对象时,以便能够抛出异常.而在我的实现中,为了图省事就直接使用了
Unsafe.class
.

下面就是反射出了这个类的theUnsafe的Field,然后get()获得这个Unsafe实例.

为什么这里不能直接new对象呢.或者为什么不能反射出constructor然后构造呢?下面引用的这一段很好的解释了这个问题

sun.misc.Unsafe这个类是如此地不安全,以至于JDK开发者增加了很多特殊限制来访问它。它的构造器是私有的,工厂方法getUnsafe()的调用器只能被Bootloader加载。如你在下面代码片段的第8行所见,这个家伙甚至没有被任何类加载器加载,所以它的类加载器是null。它会抛出SecurityException 异常来阻止侵入者。


[code]public final class Unsafe {
   ...
   private Unsafe() {}
   private static final Unsafe theUnsafe = new Unsafe();
   ...
   public static Unsafe getUnsafe() {
      Class cc = sun.reflect.Reflection.getCallerClass(2);
      if (cc.getClassLoader() != null)
          throw new SecurityException("Unsafe");
      return theUnsafe;
   }
   ...
}


幸运的是这里有一个Unsafe的变量可以被用来取得Unsafe的实例。我们可以轻松地编写一个复制方法通过反射来实现,如下所示:


[code]public static Unsafe getUnsafe() {
   try {
           Field f = Unsafe.class.getDeclaredField("theUnsafe");
           f.setAccessible(true);
           return (Unsafe)f.get(null);
   } catch (Exception e) { 
       /* ... */
   }
}


所以这里只能通过获取
theUnsafe
Field来得到Unsafe的一个实例.

Unsafe可以接触到虚拟机的底层内存结构,所以使用Unsafe类是可以绕过构造函数直接构造一个对象的.

所以最终JPJson的实现是:

[code]private <T> T construct(Class<T> tClass){
    T obj = null;
    try {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        final Unsafe un = (Unsafe) f.get(null);
        obj = (T) un.allocateInstance(tClass);
    } catch (Exception e) {
        throw new RuntimeException("construct object failed");
    }
    return obj;
}


使用Unsafe类成功的绕过构造器构造了一个对象,事实上这样做也是比较合理的,因为JPJson的目标只是给各个Field赋值,而使用者得到的Bean对象也应该只使用其中的各个Field.这是JPJson与使用者之间的一个”隐形的约定”.所以我们直接绕开构造函数,为了避免用户提供了让我们难以构造的Bean类.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: