如何在java中热交换java.lang类
我知道这不是一个好的要求。但我现在需要这么做 我需要替换如何在java中热交换java.lang类,java,jvm,javassist,hotswap,Java,Jvm,Javassist,Hotswap,我知道这不是一个好的要求。但我现在需要这么做 我需要替换Class.getResourceAsStream(String) 但是ClassLoader.preDefineClass(String,ProtectionDomain)检查全名的类名是否以java开头。,如果是,则抛出异常,不允许加载 有没有办法绕过运行时安全检查而不是编译时 更新 真正的需求是许多旧项目需要迁移到新环境,因为其中一些项目可能已经过时,而其中一些可能无法找到源代码。我被要求在新的环境中模拟旧的环境,以便这些古老的项目能
Class.getResourceAsStream(String)
但是ClassLoader.preDefineClass(String,ProtectionDomain)
检查全名的类名是否以java开头。
,如果是,则抛出异常,不允许加载
有没有办法绕过运行时安全检查而不是编译时
更新
真正的需求是许多旧项目需要迁移到新环境,因为其中一些项目可能已经过时,而其中一些可能无法找到源代码。我被要求在新的环境中模拟旧的环境,以便这些古老的项目能够继续发挥作用
厄帕德(幽灵猫)
我试过你给我的方法
类加载器类
客户端类
它确实适用于this.getClass().getResourceAsStream(String)
,但Class.Class.getResourceAsStream(String)
/“”.getClass().getResourceAsStream(String)
。因为我不能使用我的自定义类加载器加载类
名称以java开头。您不能
该类由类装入器装入。你不能改变这个家伙在做什么——它是用本机代码编写的。换句话说:这是JVM的核心——不允许您篡改它
这就是java安全概念的“整体想法”:用户不能“进入”JVM平台保证的内容
根据OP的编辑,这是关于遗留代码的:对于不是JVM的一部分,但属于自己的代码的类,您似乎想挂接到Class.getResourceAsStream(String)
如果是这样,那么“答案”很简单:您必须确保所有您的类都由您的类加载器加载。javadoc for明确指出:
查找具有给定名称的资源。用于搜索与给定类关联的资源的规则由该类的定义类装入器实现。此方法委托给此对象的类装入器
因此:学习如何使用自定义类加载器。例如,开始吧。你不能
该类由类装入器装入。你不能改变这个家伙在做什么——它是用本机代码编写的。换句话说:这是JVM的核心——不允许您篡改它
这就是java安全概念的“整体想法”:用户不能“进入”JVM平台保证的内容
根据OP的编辑,这是关于遗留代码的:对于不是JVM的一部分,但属于自己的代码的类,您似乎想挂接到Class.getResourceAsStream(String)
如果是这样,那么“答案”很简单:您必须确保所有您的类都由您的类加载器加载。javadoc for明确指出:
查找具有给定名称的资源。用于搜索与给定类关联的资源的规则由该类的定义类装入器实现。此方法委托给此对象的类装入器
因此:学习如何使用自定义类加载器。例如,开始。尝试定义以
java开头的限定名称的类时引发异常。
是的一部分,但这与此无关。任何使用类加载器
上现有类的名称定义类的尝试只能产生两种可能的结果之一:
类加载器定义,则尝试再次定义该类将被拒绝,并引发异常defineClass
不会更改现有类,这根本不在其功能范围内
类加载器
(或引导加载器)加载,则可以在此类加载器
上定义具有相同限定名称的类,但它将是一个完全不同的类,与具有相同名称但定义类加载器不同的类无关java.*
类定义具有相同名称和不同加载程序的类,这只是一个额外的限制,但您的尝试无论如何都不会起作用
如果要在类装入后替换类装入器,则只能使用代理,例如使用的Java代理或调试器。或者,您可以使用-Xbootclasspath/p:…
命令行选项将自己的版本添加到引导类路径中
当然,用特定版本替换它只能在派生此版本的环境中工作。此外,此类应用程序也不允许公开部署
一个更通用的解决方案是使用当前版本的
ClassLoader
的插装代理,并动态修改它,但是,如果您必须选择开发字节码转换代理,那么转换应用程序代码将更容易、更可靠,只需将类的所有调用。getResourceAsStream(String)
替换为自定义方法的调用,而不涉及不需要这种调整的代码。为尝试定义以java开头的限定名称的类引发异常。
,是其中的一部分,但这与此无关。任何使用类加载器
上现有类的名称定义类的尝试只能产生两种可能的结果之一:
类加载器定义,请尝试再次定义它
public class ClassLoaderTest extends ClassLoader {
@Override
public InputStream getResourceAsStream(String name) {
System.out.println("getResourceAsStream " + name);
return new ByteArrayInputStream(new byte[]{1, 3, 5, 7, 9});
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("Load class " + name);
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[0];
try {
b = new byte[is.available()];
is.read(b);
} catch (IOException e) {
e.printStackTrace();
}
return defineClass(name, b, 0, b.length);
}
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
ClassLoaderTest classLoaderTest = new ClassLoaderTest();
Class clazz = Class.forName("com.Client", true, classLoaderTest);
Object client = clazz.newInstance();
Method method = clazz.getDeclaredMethod("method");
method.invoke(client);
}
public class Client {
public void method() {
System.out.println(this.getClass().getResourceAsStream("aaa"));
System.out.println(Class.class.getResourceAsStream("bbb"));
System.out.println("".getClass().getResourceAsStream("ccc"));
}
}