当前位置: 首页 > 面试题库 >

用不同的包名称动态加载java中的类

吕冠宇
2023-03-14
问题内容

是否可以在Java中加载类并“伪造”类的包名称/规范名称?我尝试这样做,这很明显,但是我在中收到“类名不匹配”的消息ClassDefNotFoundException

我这样做的原因是我试图加载在默认程序包中编写的API,以便我可以直接使用它而无需使用反射。该代码将在表示包和包名称导入的文件夹结构中针对该类进行编译。即:

./com/DefaultPackageClass.class



// ...
import com.DefaultPackageClass;
import java.util.Vector;
// ...

我当前的代码如下:

public Class loadClass(String name) throws ClassNotFoundException {
    if(!CLASS_NAME.equals(name))
            return super.loadClass(name);

    try {
        URL myUrl = new URL(fileUrl);
        URLConnection connection = myUrl.openConnection();
        InputStream input = connection.getInputStream();
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int data = input.read();

        while(data != -1){
            buffer.write(data);
            data = input.read();
        }

        input.close();

        byte[] classData = buffer.toByteArray();

        return defineClass(CLASS_NAME,
                classData, 0, classData.length);

    } catch (MalformedURLException e) {
        throw new UndeclaredThrowableException(e);
    } catch (IOException e) {
        throw new UndeclaredThrowableException(e); 
    }

}

问题答案:

如Pete所述,可以使用ASM字节码库来完成此操作。实际上,该库实际上带有一个专门用于处理这些类名重新映射(RemappingClassAdapter)的类。这是使用此类的类加载器的示例:

public class MagicClassLoader extends ClassLoader {

    private final String defaultPackageName;

    public MagicClassLoader(String defaultPackageName) {
        super();
        this.defaultPackageName = defaultPackageName;
    }

    public MagicClassLoader(String defaultPackageName, ClassLoader parent) {
        super(parent);
        this.defaultPackageName = defaultPackageName;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        byte[] bytecode = ...; // I will leave this part up to you
        byte[] remappedBytecode;

        try {
            remappedBytecode = rewriteDefaultPackageClassNames(bytecode);
        } catch (IOException e) {
            throw new RuntimeException("Could not rewrite class " + name);
        }

        return defineClass(name, remappedBytecode, 0, remappedBytecode.length);
    }

    public byte[] rewriteDefaultPackageClassNames(byte[] bytecode) throws IOException {
        ClassReader classReader = new ClassReader(bytecode);
        ClassWriter classWriter = new ClassWriter(classReader, 0);

        Remapper remapper = new DefaultPackageClassNameRemapper();
        classReader.accept(
                new RemappingClassAdapter(classWriter, remapper),
                0
            );

        return classWriter.toByteArray();
    }

    class DefaultPackageClassNameRemapper extends Remapper {

        @Override
        public String map(String typeName) {
            boolean hasPackageName = typeName.indexOf('.') != -1;
            if (hasPackageName) {
                return typeName;
            } else {
                return defaultPackageName + "." + typeName;
            }
        }

    }

}

为了说明这一点,我创建了两个类,它们都属于默认包:

public class Customer {

}

public class Order {

    private Customer customer;

    public Order(Customer customer) {
        this.customer = customer;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

}

这是任何重新映射Order 之前 的清单:

> javap -private -c命令
从“ Order.java”编译
公共类Order扩展java.lang.Object {
私人客户客户;

公共秩序(客户);
  码:
   0:加载_0
   1:调用特殊#10; //方法java / lang / Object。“” :()V
   4:aload_0
   5:aload_1
   6:普特菲尔德#13;//字段客户:LCustomer;
   9:返回

公共客户getCustomer();
  码:
   0:加载_0
   1:getfield#13;//字段客户:LCustomer;
   4:阿伦

public void setCustomer(Customer);
  码:
   0:加载_0
   1:aload_1
   2:putfield#13;//字段客户:LCustomer;
   5:返回

}

这是重新映射Order 的列表(com.mycompany用作默认软件包):

> javap -private -c命令
从“ Order.java”编译
公共类com.mycompany.Order扩展了com.mycompany.java.lang.Object {
私人com.mycompany.Customer客户;

公共com.mycompany.Order(com.mycompany.Customer);
  码:
   0:加载_0
   1:调用特殊#30; //方法“ com.mycompany.java/lang/Object"."":()V
   4:aload_0
   5:aload_1
   6:普特菲尔德#32;//字段客户:Lcom.mycompany.Customer;
   9:返回

公共com.mycompany.Customer getCustomer();
  码:
   0:加载_0
   1:getfield#32;//字段客户:Lcom.mycompany.Customer;
   4:阿伦

公共无效setCustomer(com.mycompany.Customer);
  码:
   0:加载_0
   1:aload_1
   2:putfield#32;//字段客户:Lcom.mycompany.Customer;
   5:返回

}

如您所见,重新映射更改了Order对的所有引用com.mycompany.Order和对的所有Customer引用com.mycompany.Customer

该类加载器将必须加载以下任一类的所有类:

  • 属于默认软件包,或
  • 使用属于默认软件包的其他类。


 类似资料:
  • 问题内容: 我知道,Java没有预处理器,因此Java中有些东西或多或少是不可能的。 真的 没有 办法在循环中用动态名称填充这些数组吗?我想要类似的东西: 代替 还是有可以使用的古怪技巧? 问题答案: 我会那样做: 如果要使用该方法:

  • 我需要下载shapely,但我总是出错。 起初,我尝试使用: 它不起作用,所以我在网上搜索它,有人告诉我去这里下载文件:https://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely我下载了名为Shapely1.6.4.post1-cp37-cp37mwin_amd64.whl的文件,因为我的窗口是64位,这是最新的一个。我又试了一次,但没有成功。我在这个问

  • 我得到了(超过)两个Api POSTendpoint。每一个都需要一个json作为参数。但是当我在两个endpoint参数类中使用相同的类名负载时,Swagger就不起作用了。当我改变其中的一个,例如从有效载荷到有效载荷1时,它就不起作用了。当然,我在包装类中设置了正确的名称空间,以便它找到负载。但我希望每次都使用相同的名称“有效载荷”。如何使用相同的类名负载?在这两种情况下,我都可以保留json

  • 我有OSGI bundle(比方说A),它依赖于非OSGI库(比方说B)。B正在使用class.ForName加载一个类(库A中的ClassA是库B中的ClassB类型)。我已经包装了库B,使其成为osgi捆绑包,并导入了库A中所需的包,但我无法使用class.forname加载该类。请注意,库B是第三方库,我没有任何控制这一点。 下面是我创建的支持OSGI的库B的清单文件- manifest-v

  • 问题内容: 在我的Java应用程序中,我使用第三方库。 但是,我发现有些奇怪,有一些嵌套的程序包,有些类的名称可能与程序包的名称相同。 恐怕我不清楚。这是一个例子: 包 在“ com.xx.a”内部有一个名为“ a”的类。 因此,如果我想将此类称为“ a” … 我写: 然后,IDE将认为我的意思是软件包“ com.xx.a.a”。 那我就不能打电话了。 我想知道为什么? 顺便说一句,图书馆提供者似

  • 问题内容: 我查找了语法并搜索了api,但仍然对该过程感到困惑。我还搜索了Stackoverflow。加载类并从中动态创建对象的正确方法是什么?换句话说,我希望用户指定要创建的对象类型,然后创建该类型的对象。我不需要菜单,因为我希望他们能够选择当前目录中的任何类。 问题答案: 假设该类具有无参数构造函数,则最简单的方法是- 参考-java.lang.Class