无法反射到想要的构造函数时该如何根据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仍然能够正常的构造对象并反射数据上去.这一点彻彻底底的震惊了我.
当然还是二话不说上代码.
需要额外关注的是这几行
首先是通过
下面就是反射出了这个类的theUnsafe的Field,然后get()获得这个Unsafe实例.
为什么这里不能直接new对象呢.或者为什么不能反射出constructor然后构造呢?下面引用的这一段很好的解释了这个问题
sun.misc.Unsafe这个类是如此地不安全,以至于JDK开发者增加了很多特殊限制来访问它。它的构造器是私有的,工厂方法getUnsafe()的调用器只能被Bootloader加载。如你在下面代码片段的第8行所见,这个家伙甚至没有被任何类加载器加载,所以它的类加载器是null。它会抛出SecurityException 异常来阻止侵入者。
幸运的是这里有一个Unsafe的变量可以被用来取得Unsafe的实例。我们可以轻松地编写一个复制方法通过反射来实现,如下所示:
所以这里只能通过获取
Unsafe可以接触到虚拟机的底层内存结构,所以使用Unsafe类是可以绕过构造函数直接构造一个对象的.
所以最终JPJson的实现是:
使用Unsafe类成功的绕过构造器构造了一个对象,事实上这样做也是比较合理的,因为JPJson的目标只是给各个Field赋值,而使用者得到的Bean对象也应该只使用其中的各个Field.这是JPJson与使用者之间的一个”隐形的约定”.所以我们直接绕开构造函数,为了避免用户提供了让我们难以构造的Bean类.
在确定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) { /* ... */ } }
所以这里只能通过获取
theUnsafeField来得到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类.
相关文章推荐
- 基于注解的Spring AOP的配置和使用
- 基于vc6.0和mysql55的邮件管理系统有源码
- 烧写mini2440的过程小结
- mongodb的集群部分的问题及其理解整理
- jdk7 HashSet和HashMap源码分析
- 第一章 Adnroid体系与系统架构
- MFC根据前缀批量复制文件工具
- Android Service更新UI的方法之AIDL
- iOS-SDWebImage
- iOS-Block
- <<计算机网络教程>>复习提要
- iOS-推送通知详解
- Linux sort 命令
- Git 版本控制
- iOS-推送,证书申请,本地推送
- iOS-远程推送
- CIDR/Netmask 计算
- mysql 变量报错 (error encountered during command execution)
- iOS抓包工具Charles的使用
- python小脚本-提取邮箱