??xml version="1.0" encoding="utf-8" standalone="yes"?>
作者:dozb
一般情况下Java应用的开发者ؓ了保护代码不被别人抄袭,在生成class文g的时候都java文gq行了淆,q种class文g用反~译工具得到的结果很隄懂,q且不能q行~译。本文从研究的角度,析如何Lq种反编译过来的文g?/p>
例子一Q赋?/font>
反编译过来的代码如下Q?br />
Node node;
Node node1 = _$3.getChildNodes().item(0);
node1;
node1;
JVM INSTR swap ;
node;
getChildNodes();
0;
item();
getChildNodes();
0;
item();
getNodeValue();
String s;
s;
原始语句Q?br />
Node node;
Node node1 = currDocument.getChildNodes().item(0);
node = node1;
String s = node.getChildNodes().item(0).getChildNodes().item(0).getNodeValue();
注解Q?br />
JVM INSTR swap ; //赋D?br />
l习Q?br />
String s1;
String s8 = node.getChildNodes().item(1).getChildNodes().item(0).getNodeValue();
s8;
s8;
JVM INSTR swap ;
s1;
10;
Integer.parseInt();
int i;
i;
例子二:不带参数创徏对象
反编译过来的代码如下Q?br />
JVM INSTR new #244 <Class CrossTable>;
JVM INSTR dup ;
JVM INSTR swap ;
CrossTable();
CrossTable crosstable;
crosstable;
原始语句Q?br />
CrossTable crosstable = new CrossTable();
注解Q?br />
l习Q?br />
JVM INSTR new #246 <Class Database>;
JVM INSTR dup ;
JVM INSTR swap ;
Database();
Object obj;
obj;
例子三:带参数创建对?br />
反编译过来的代码如下Q?br />
JVM INSTR new #262 <Class StringBuffer>;
JVM INSTR dup ;
JVM INSTR swap ;
String.valueOf(s2);
StringBuffer();
s.substring(j, i);
append();
s6;
append();
toString();
s2;
原始语句Q?br />
s2 = (new StringBuffer(String.valueOf(s2))).append(s.substring(j, i)).append(s6).toString();
注解Q?br />
此语句实际上是:s2 += s.substring(j, i) + s6;
l习Q?/p>
例子四:for循环
反编译过来的代码如下Q?br />
int k = 0;
goto _L4
_L8:
...
k++;
_L4:
if(k < as.length) goto _L8; else goto _L7
原始语句Q?br />
for(int k=0;k < as.length;k++)
{
...
}
注解Q?/p>
例子五:while循环
反编译过来的代码如下Q?br />
String s1 = "";
goto _L1
_L3:
JVM INSTR new #262 <Class StringBuffer>;
JVM INSTR dup ;
JVM INSTR swap ;
String.valueOf(s1);
StringBuffer();
_$2(resultset, s, l);
append();
toString();
s1;
_L1:
if(resultset.next()) goto _L3; else goto _L2
原始语句Q?br />
String s1 = "";
while(resultset.next())
{
s1 = s1 + resultSetToString(resultset, s, l);
走出ClassLoader的迷?/p>
System、Current和Context ClassLoaderQ分别在何种情Ş下用?
1、问题:在何U情形下使用thread.getcontextclassloader()?
管没经帔R到这个问题,但是惌得准的{案q不那么ҎQ特别是在开发应用框架的时候,你需要动态的加蝲一些类和资源,不可避免的你会被此困扰。一般来_动态蝲入资源有三种ClassLoader可以选择QSystem ClassLoaderQ也叫App ClassLoaderQ、当前类的ClassLoader和CurrentThread的Context ClassLoader。那么, 如何选择使用Q?/code>
首先可以单排除的是System ClassLoaderQ这个ClassLoader负责从参?classpath?cp、和操作pȝCLASSPATH中蝲入资源。ƈ且,MClassLoader的getSystemXXX()Ҏ都是有以上几个\径指定的。我们应该很需要编写直接用ClassLoader的程序,否则你的代码只能在命o行运行,发布你的代码成ؓejb、web应用或者java web start应用Q我肯定他们会崩溃!
接下来,我们只剩下两个选择了:当前ClassLoader和Thread Context ClassLoader
Current ClassLoaderQ当前类所属的ClassLoaderQ在虚拟ZcM间引用,默认是使用q个ClassLoader。另外,当你使用Class.forName(), Class.getResource()q几个不带ClassLoader参数的方法是Q默认同样适用当前cȝClassLoader。你可以通过ҎXX.class.GetClassLoader()获取?/code>
Thread Context ClassLoaderQ没一个Thread有一个相兌pȝContext ClassLoaderQ由nativeҎ建立的除外)Q可以通过Thread.setContextClassLoader()Ҏ讄。如果你没有d讄QThread默认集成Parent Thread?Context ClassLoaderQ注意,是parent Thread 不是父类Q。如?你整个应用中都没有对此作M处理Q那?所有的Thread都会以System ClassLoader作ؓContext ClassLoader。知道这一点很重要Q因Zweb服务器,java企业服务器用一些复杂而且_y的ClassLoaderl构d现诸如JNDI、线E池和热部v{功能以来,q种单的情况发的少见了?/span>
q篇文章中ؓ什么把Thread Context ClassLoader攑֜首要的位|,别hq没有大张旗鼓的介绍?很多开发者都Ҏ不甚了解Q因为sun没有提供很好的说明文档?/span>
事实上,Context ClassLoader提供一个突破委托代理机制的后门。虚拟机通过父子层次关系l织理ClassLoaderQ没有个ClassLoader都有一个Parent ClassLoaderQBootStartp不在此范围之内)Q当要求一个ClassLoader装蝲一个类是,他首先请求Parent ClassLoader去装载,只有parent ClassLoader装蝲p|Q才会尝试自p载?/span>
但是Q某些时候这U顺序机制会造成困扰Q特别是jvm需要动态蝲入有开发者提供的资源时。就以JNDIZQJNDI的类是由bootstarp ClassLoader从rt.jar中间载入的,但是JNDI具体的核心驱动是由正式的实现提供的,q且通常会处?cp参数之下Q注Q也是默认的System ClassLoader理Q,q就要求bootstartp ClassLoader去蝲入只有SystemClassLoader可见的类Q正常的逻辑没办法处理。怎么办呢Qparent可以通过获得当前调用Thread的方法获得调用线E的Context ClassLoder 来蝲入类?/span>
带补充一句,JAXP?.4之后也换成了cMJNDI的ClassLoader实现Q嘿嘿,刚刚我说什么来着QSUN文档~Z ^_^
介绍完这些之后,我们走到的十字\口,M选择都不是万能的。一些h认ؓContext ClassLoader会是新的标准。但?一旦你的多U程需要通讯某些׃n数据Q你会发玎ͼ你将有一张极其丑陋的ClassLoader分布图,除非所有的U程使用一LContext ClassLoader。ƈ且委z用当前ClassLoder对一些方法来说是默认l承来的Q比如说Class.forName()。尽你明确的在M你能控制的地方用Context ClassLoaderQ但是毕竟还有很多代码不归你(备注Q想起一个关于UNIX名字来源的笑话)?/span>
某些应用服务器用不同的ClassLoder作ؓContext ClassLoader和当前ClassLoaderQƈ且这些ClassLoader有着相同的ClassPathQ但没有父子关系Q这使得情况更复杂。请列位看官Q花几秒钟时间想一惻IZ么这样不好?被蝲入的cd虚拟机内部有一个全名称Q不同的ClassLoader载入的相同名U的cL不一LQ这隐藏了cd转换错误的隐患。(注:奶奶?俺就遇到q,JBOSSClassLoader机制蛮挫的)
q种混ؕ事实上在javacM也有Q试着ȝQ何一个包含动态加载的java规范的ClassLoader机制Q以下是一个清单:
Class.forName()
use the current classloaderjava.protocol.handler.pkgs
system property are looked up in the bootstrap and system classloaders only而且关于q些资源的类加蝲机制文档时很?/p>
java开发h员应该怎么做?
如果你的实现是利用特定的框架Q那么恭喜你Q实现它q比实现框架要简单得多!例如Q在web应用和EJB应用中,你仅仅只要?Class.getResource()p够了?/code>
其他的情形下Q俺有个Q这个原则是俺工作中发现的,侉|必究,抵制盗版。)Q?/code>
下面q个c?/span>可以在整个应用中的Q何地方用,作ؓ一个全局的ClassLoaderQ所有的CZ代码可以?a >download下蝲Q:
通过ClassLoaderResolver.getClassLoader()Ҏ获得一个ClassLoader的引用,q且利用正常的ClassLoader的apid载资源,你也可以使用 ResourceLoader
API作ؓ备选方?/p>
而决定用何UClassLoader{略是由接口实现的,q是一U插件机Ӟ方便变更?/p>
ClassLoadContext.getCallerClass()q回调用者给ClassLoaderResolver 或?ResourceLoaderQ因此能获得调用者的ClassLoader。需要注意的是,调用者是不会变的 (注:作者用的final修饰?。俺的方法不需要对现有的业务方法做扩展Q而且可以作ؓ静态方法是用。而且Q你可以Ҏ自己的业务场景实现独特的ClassLoaderContext?/span>
看出来没Q这是一U很熟悉的设计模式,XD Q把获得ClassLoader的策略从业务中独立出来,q个{略可以?L用ContextClassLoader"或?L用当前ClassLoader"。想预先知道那种{略是正的比较困难Q那么这U模式可以让你简单的改变{略?/span>
俺写了一个默认的实现Q基本可以对?5%的场景(enjoy yourselfQ?/span>
上面的逻辑比较单,如果当前ClassLoader和Context ClassLoader是父子关p,那就总选儿子,Ҏ委托原则Q这个很Ҏ理解?/p>
如果两hqQ选择正确的ClassLoader很重要,q行时不允许含糊。这U情况下Q我的代码选择Context ClassLoaderQ这是俺个h的经验之谈)Q当然也不要担心不能改变Q你能随便根据需要改变。一般而言QContext ClassLoader比较适合框架Q而Current ClassLoader在业务逻辑中用的更多?/p>
最后,查确保选中的ClassLoader不是System ClassLoader的parentQ一旦高于System ClassLoader Q请使用System ClassLoaderQ你的类部v在Ext路径下面Q就会出现这U情况)?/p>
h意,俺故意没x被蝲入资源的名称。Java XML API 成ؓjava 核心api的经历告诉我们,Ҏ资源名称qo是很不cool的idea。而且 我也没有ȝ认到底哪个ClassLoader被取得了Q因为只要清楚原理,q很Ҏ被推理出来。(哈哈Q俺是强淫)
管讨论java 的ClassLoader不是一个很cool的话题(译者注Q当q不coolQ但是现在很coolQ,而且Java EE的ClassLoader{略发的依赖各U^台的升。如果这没有一个更好的设计的话Q将会变成一个大大的问题。不敢您是否同意俺的观点Q俺重你说话的权利Q所以请l俺分n您的意见l验?/p>
作者介l:
Vladimir RoubtsovQ曾l用多U语a有超q?3q的~程l历Q恩 现在应该过15q了 hohoQ,95q开始接触javaQhoho 俺是99q看的第一本java书)。现在ؓTrilogy in Austin, Texas开发企业Y件?/p>
译完了QMMD 译q是很麻烦的?XD ........