如何在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"));
        }
    }