不使用构造方法创建Java对象 Objenesis

fmms 12年前
     <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>