一种在Java中跨ClassLoader的方法调用的实现

ClassNotFoundException或者NoClassDefFoundError

在程序运行时我们可能遇到"ClassNotFoundException"或者"NoClassDefFoundError",遇到这样的问题时,当然,我们首先要检查我们的classpath的配置是否正确,需要的class是否已经按预期打包到运行时环境。但除了这些,我们还会遇到class已经被另外ClassLoader加载的情况,而且当前的ClassLoader层次被应用服务器控制,我们甚至无法改变。

以JBoss EAP 6为例,JBoss EAP 6开始使用了新的基于modules的类层次管理,对于已经有依赖声明的module之间才可以用正常方式调用,否则很难跨module调用。我们在(监控)项目中就遇到了web module需要访问message module的运行时信息,此时就必须访问真实的运行时来获取动态的数据,所以用冗余的方式加载目标类都是不能达到目标的。

ClassNotFoundException vs. NoClassDefFoundError

顺便提一下这两者的区别,细节区别就比较多了,简单来说,“ClassNotFoundException”一般是通过类名这个String尝试加载时失败报的,如Class.forName方法加载类,或者ClassLoader.loadClass、ClassLOader.findSystemClass也会出现同样的错误,而用其他方式试图显式加载类则会抛出NoClassDefFoundError。

 

基于接口编程的跨ClassLoader调用

在我们的项目中,由于module的隔离,“javax.jms.ConnectionFactory"或者“javax.jms.Queue”在当前的ClassLoader中都不可访问,最终我们通过如下的方式来实现:

图示说明:

  • 当前工作上下文为ClassLoader1,目标上下文为ClassLoader2.
  • 最终通过增加3个如图中砖红色的类,并通过Caller类的调用实现。 
    • ClassLoader-sub的实现要以ClassLoader2为parent(父加载器),ClassLoader-sub的必要性在于强制加载CallImpl类,并在实现中调用父加载器的基础类实现功能,类似一个适配器的模式。在我们的环境中,“javax.jms.ConnectionFactory"和“javax.jms.Queue”只在这一层可访问。
    • 重要:Caller中的调用需要使用反射创建CallImpl的实例,并只能通过接口Call在Caller中引用,使用反射创建CallImpl时需要的ClassLoader-sub的父加载器可以通过JNDI查询ClassLoader2中的object。例如,我们的代码片段如:
      String jndiName = getJNDIName();
      InitialContext ctx = new InitialContext();
      ClassList classList = new ClassList(false, null, null, Arrays.asList(CLASS_LIST));
      ClassLoader helperLoader = new JMSCollectorClassLoader(ctx.lookup(jndiName).getClass().getClassLoader(), classList);
      Class helperClass = helperLoader.loadClass("CallImpl");
      Constructor jmsQueueHelperConstructor = helperClass.getConstructor(String.class);
      Call mJmsQueueHelper = (Call) jmsQueueHelperConstructor.newInstance(jndiName);

       其中,CLASS_LIST是需要ClassLoader-sub加载的CallImpl和其他辅助类的列表。

 

转载请注明原文链接:http://www.cnblogs.com/1xin/p/6628018.html

转载于:https://www.cnblogs.com/1xin/p/6628018.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java的Emit是一种动态生成类的技术,可以在运行时根据需要生成类和方法。它通常用于框架和库的实现,以及在某些情况下用于优化性能。 在Java,可以使用开源库ASM来实现Emit。ASM是一个字节码操作框架,可以用于生成、转换和分析Java字节码。使用ASM,可以直接生成字节码,而不是使用反射或者动态代理。 下面是一个简单的例子,演示了如何使用ASM生成一个类,该类包含一个方法,该方法将两个整数相加并返回结果: ``` import org.objectweb.asm.*; public class DynamicClassGenerator { public static byte[] generateClass() throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "DynamicClass", null, "java/lang/Object", null); MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "add", "(II)I", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ILOAD, 0); mv.visitVarInsn(Opcodes.ILOAD, 1); mv.visitInsn(Opcodes.IADD); mv.visitInsn(Opcodes.IRETURN); mv.visitMaxs(2, 2); mv.visitEnd(); cw.visitEnd(); return cw.toByteArray(); } public static void main(String[] args) throws Exception { ClassLoader cl = new ClassLoader() { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if (name.equals("DynamicClass")) { byte[] bytes = generateClass(); return defineClass(name, bytes, 0, bytes.length); } return super.findClass(name); } }; Class<?> clazz = cl.loadClass("DynamicClass"); int result = (int) clazz.getDeclaredMethod("add", int.class, int.class).invoke(null, 1, 2); System.out.println(result); } } ``` 在这个例子,我们首先创建了一个ClassWriter对象,它将用于生成类。然后我们调用ClassWriter的visit方法来定义类的基本信息,包括类名、父类和接口。在这个例子,我们生成的类继承自Object类。 接下来,我们使用ClassWriter的visitMethod方法来生成类的方法。在这个例子,我们只定义了一个静态方法add,它有两个int类型的参数和一个int类型的返回值。我们在这个方法使用了一些ASM指令,例如ILOAD、IADD和IRETURN,来实现相加并返回结果的功能。 最后,我们调用ClassWriter的visitEnd方法来结束类的定义,并将生成的字节码返回。 在main方法,我们创建了一个ClassLoader对象,并重写了它的findClass方法,以便在需要时动态生成类。我们通过调用ClassLoader的loadClass方法来加载生成的类,并使用反射调用add方法进行测试。在这个例子,我们将1和2作为参数传递给add方法,它将返回3,并将结果打印到控制台上。 这只是一个简单的例子,ASM还提供了许多其他功能,例如生成字段、注解、异常处理器等。如果你想深入学习Emit技术,可以查看ASM的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值