底线:可以在运行时将条目添加到系统类路径,并展示了如何。然而,这具有不可逆转的副作用,并且依赖于 Sun JVM 的实现细节。
类路径是final,最字面意义上的:
系统类加载器(从主类路径加载的)is sun.misc.Launcher$AppClassLoader in rt.jar。
rt.jar:sun/misc/Launcher.class(源使用Java Decompiler生成):
public class Launcher
{
<...>
static class AppClassLoader
extends URLClassLoader
{
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
<...>
rt.jar:sun/misc/URLClassLoader.class:
protected Class<?> findClass(final String paramString)
throws ClassNotFoundException
{
<...>
String str = paramString.replace('.', '/').concat(".class");
Resource localResource = URLClassLoader.this.ucp.getResource(str, false);
<...>
但是,即使该字段是 final 的,这并不意味着如果我们以某种方式访问它,我们就不能改变对象本身。该字段没有访问修饰符 - 这意味着,只要我们从同一个包中进行调用,我们就可以访问它。 (以下是IPython 和JPype;这些命令的可读性足以轻松导出它们的Java 对应项)
#jpype doesn't automatically add top-level packages except `java' and `javax'
In [28]: jpype.sun=jpype._jpackage.JPackage("sun")
In [32]: jpype.sun.misc.Launcher
Out[32]: jpype._jclass.sun.misc.Launcher
In [35]: jpype.sun.misc.Launcher.getLauncher().getClassLoader()
Out[35]: <jpype._jclass.sun.misc.Launcher$AppClassLoader at 0x19e23b0>
In [36]: acl=_
In [37]: acl.ucp
Out[37]: <jpype._jclass.sun.misc.URLClassPath at 0x19e2c90>
In [48]: [u.toString() for u in acl.ucp.getURLs()]
Out[48]: [u'file:/C:/Documents%20and%20Settings/User/']
现在,URLClassPath 有一个公共的 addURL 方法。让我们尝试一下,看看会发生什么:
#normally, this is done with Launcher.getFileURL but we can't call it directly
#public static URLClassPath.pathToURLs also does the same, but it returns an array
In [72]: jpype.sun.net.www.ParseUtil.fileToEncodedURL(
jpype.java.io.File(r"c:\Ivan\downloads\dom4j-2.0.0-RC1.jar")
.getCanonicalFile())
Out[72]: <jpype._jclass.java.net.URL at 0x1a04b50>
In [73]: _.toString()
Out[73]: u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar'
In [74]: acl.ucp.addURL(_72)
In [75]: [u.toString() for u in acl.ucp.getURLs()]
Out[75]:
[u'file:/C:/Documents%20and%20Settings/User/',
u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar']
现在,让我们尝试从.jar 加载一些类:
In [78]: jpype.org=jpype._jpackage.JPackage("org")
In [79]: jpype.org.dom4j.Entity
Out[79]: jpype._jclass.org.dom4j.Entity
成功了!
这可能会在沙盒等存在自定义类加载器或安全设置的情况下失败(AppClassLoader.loadClass 在调用 super 之前进行安全检查)。
进一步的代码检查表明addURL 还禁用了URLClassPath 的查找缓存(在一些native 方法中实现),这是不可逆的。最初,lookupCacheEnabled 标志设置为sun.cds.enableSharedLookupCache 系统属性的值。
该界面无法编辑条目。 URL 被添加到URLClassPath 的private ArrayList path 和Stack urls。 urls 是可访问的,但事实证明,它只是在尝试从中加载之前临时保存条目,此时信息将移动到 HashMap lmap 和 ArrayList loaders。 getURLs() 返回path 的副本。因此,理论上可以通过破解可访问字段来对其进行编辑,但它远非可靠并且不会影响getURLs 结果。