不使用构造方法创建Java对象 Objenesis
fmms
13年前
<p>如果一个类没有参数为空的构造方法时候,那么你直接调用newInstance方法试图得到一个实例对象的时候是会抛出异常的。能不能有 办法绕过构造方法来实例化一个对象呢?</p> <p><strong>Objenesis </strong>为其提供了在四个不同的jvm上的解决方案。</p> <ul> <li> Sun Hotspot VM, versions 1.3, 1.4, 1.5 and 1.6 </li> <li> GCJ version 3.4.4 (tested on Windows/Cygwin) </li> <li> BEA JRockit versions 7.0 (1.3.1), 1.4.2 and 1.5 </li> <li> Aonix PERC (no serialization support), tested on version 5.0.0667 </li> </ul> <p>从运行平台上得到几个关键的参数,如下:</p> <div> <div class="dp-highlighter"> <ol class="dp-j"> <li class="alt"><span><span class="comment">/** JVM version */</span><span> </span></span></li> <li><span class="keyword">protected</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> String VM_VERSION = System.getProperty(</span><span class="string">"java.runtime.version"</span><span>); </span></li> <li class="alt"><span> </span></li> <li><span class="comment">/** JVM version */</span><span> </span></li> <li class="alt"><span class="keyword">protected</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> String VM_INFO = System.getProperty(</span><span class="string">"java.vm.info"</span><span>); </span></li> <li><span> </span></li> <li class="alt"><span class="comment">/** Vendor version */</span><span> </span></li> <li><span class="keyword">protected</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> String VENDOR_VERSION = System.getProperty(</span><span class="string">"java.vm.version"</span><span>); </span></li> <li class="alt"><span> </span></li> <li><span class="comment">/** Vendor name */</span><span> </span></li> <li class="alt"><span class="keyword">protected</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> String VENDOR = System.getProperty(</span><span class="string">"java.vm.vendor"</span><span>); </span></li> <li><span> </span></li> <li class="alt"><span class="comment">/** JVM name */</span><span> </span></li> <li><span class="keyword">protected</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> String JVM_NAME = System.getProperty(</span><span class="string">"java.vm.name"</span><span>); </span></li> </ol> </div> </div> <p>然后根据得到的参数进行判断:<br /> <br /> 根据得到平台提供的jvm版本和供应商来选择不同的实例化策略。<br /> 说实话,这几个平台里面我还 是对sun公司提供的相对熟悉一些,所以除了sun公司提供的jvm对于的实例策略我在这里就不介绍了,<br /> 大家有兴趣的话可以去项目主页下载下来细 细研究。<br /> <br /> 现在我们仅仅关注sun公司的,并且版本大于1.3的。<br /> 版本为1.3的jvm具体实例化策略这里不做讨论了,有兴趣的可 以去看objenesis的实现。<br /> <br /> 代码如下:</p> <div> <div class="dp-highlighter"> <ol class="dp-j"> <li class="alt"><span><span class="keyword">import</span><span> sun.reflect.ReflectionFactory; </span></span></li> <li><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> SunReflectionFactoryInstantiator </span><span class="keyword">implements</span><span> ObjectInstantiator { </span></li> <li class="alt"><span> </span></li> <li><span class="keyword">private</span><span> </span><span class="keyword">final</span><span> Constructor mungedConstructor; </span></li> <li class="alt"><span> </span></li> <li><span class="keyword">public</span><span> SunReflectionFactoryInstantiator(Class type) { </span></li> <li class="alt"><span> </span></li> <li><span>ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); </span></li> <li class="alt"><span>Constructor javaLangObjectConstructor; </span></li> <li><span> </span></li> <li class="alt"><span class="keyword">try</span><span> { </span></li> <li><span>javaLangObjectConstructor = Object.</span><span class="keyword">class</span><span>.getConstructor((Class[]) </span><span class="keyword">null</span><span>); </span></li> <li class="alt"><span>} </span></li> <li><span class="keyword">catch</span><span>(NoSuchMethodException e) { </span></li> <li class="alt"><span class="keyword">throw</span><span> </span><span class="keyword">new</span><span> Error(</span><span class="string">"Cannot find constructor for java.lang.Object!"</span><span>); </span></li> <li><span>} </span></li> <li class="alt"><span>mungedConstructor = reflectionFactory.newConstructorForSerialization(type, </span></li> <li><span>javaLangObjectConstructor); </span></li> <li class="alt"><span>mungedConstructor.setAccessible(</span><span class="keyword">true</span><span>); </span></li> <li><span>} </span></li> <li class="alt"><span> </span></li> <li><span class="keyword">public</span><span> Object newInstance() { </span></li> <li class="alt"><span class="keyword">try</span><span> { </span></li> <li><span class="keyword">return</span><span> mungedConstructor.newInstance((Object[]) </span><span class="keyword">null</span><span>); </span></li> <li class="alt"><span>} </span></li> <li><span class="keyword">catch</span><span>(Exception e) { </span></li> <li class="alt"><span class="keyword">throw</span><span> </span><span class="keyword">new</span><span> ObjenesisException(e); </span></li> <li><span>} </span></li> <li class="alt"><span>} </span></li> <li><span>} </span></li> </ol> </div> </div> <p><br /> 通过<strong><span style="color:#ff0000;">sun.reflect.ReflectionFactory</span></strong>这 个类来实例化一个class那么就绕过了其类的构造方法,我们可以暂且称之为绕道方式实例一个对象。<br /> 希望上面的代码能给大家起到一定的帮助,另外<strong>easymock</strong>的 最新版本已经使用了Objenesis来实例化一个Class获取对象。<br /> <br /> </p> <p><strong>项目主页:</strong><a href="http://www.open-open.com/lib/view/home/1326941374233" target="_blank">http://www.open-open.com/lib/view/home/1326941374233</a></p> <div></div>