一种在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和其他辅助类的列表。

通过这个问题,可以明显的认识到“接口”的一个强大功能:隔绝底层实现的细节,只关心结果。

时间: 2025-01-17 23:59:37

一种在Java中跨ClassLoader的方法调用的实现的相关文章

聊聊JVM(三)两种计算Java对象大小的方法

这篇说说如何计算Java对象大小的方法.之前在聊聊高并发(四)Java对象的表示模型和运行时内存表示 这篇中已经说了Java对象的内存表示模型是Oop-Klass模型. 普通对象的结构如下,按64位机器的长度计算 1. 对象头(_mark), 8个字节 2. Oop指针,如果是32G内存以下的,默认开启对象指针压缩,4个字节 3. 数据区 4.Padding(内存对齐),按照8的倍数对齐 数组对象结构是 1. 对象头(_mark), 8个字节 2. Oop指针,如果是32G内存以下的,默认开启对

java类加载机制及方法调用

类加载机制 概述 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Using)和卸载(Unloading)7个阶段.其中验证.准备.解析3个部分统称为连接(Linking) 于初始化阶段,虚拟机规范则是严格规定了有且只有5种情况必须立即对类进行"初始化"(而加载.验证.准备自然需要在此之前开始): 1

简谈 JavaScript、Java 中链式方法调用大致实现原理

相信,在 JavaScript .C# 中都见过不少链式方法调用,那么,其中实现该类链式调用原理,大家有没有仔细思考过?其中 JavaScript 类库:jQuery 中就存在大量例子,而在 C# 中,其中 lambda 表达式的大量使用以及扩展方法的新增,使链式调用也见的不少. 首先,就谈谈 JavaScript 中链式调用,其实,也是就是运用之前提及的 this . var Person=function(name,age){ this.Name=name; this.Age=age; };

【求助】Java可变长参数方法调用问题

不说废话,直接上代码: 1 package mytest; 2 3 import java.util.List; 4 5 public class TestClass { 6 7 public void method(List<Object> list){ 8 System.out.println("method with param List<Object> invoked!"); 9 } 10 11 public void method(Object...

Java使用Hessian远程方法调用

<dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.38</version> </dependency> /** * Copyright (C),HTF<br> * 服务接口 * * @author muzhongjiang * @date 2014年8月5日 */ publ

java实例变量及方法调用顺序

public class Base { private String name="base"; public Base(){ sayHello(); } void sayHello() { System.out.println("Base hello " + name); } } class Derived extends Base { private String name="Derived"; public Derived(){ sayHel

JDK7动态方法调用

在JDK7中,Java提供了对动态语言特性的支持,实现了JSR 292 <Supporting Dynamically Typed Languages on the Java Platform>规范,这是Java语言发展的一重大进步,而提供对动态语言特性支持也是Java发展的一大趋势与方向.那么动态性表现在哪里呢?其一在Java API层面,新增了java.lang.invoke包,主要包含了CallSite.MethodHandle.MethodType等类:其二,在Java字节码指令层面,

JVM学习笔记(二)--方法调用之静态分配和动态分配

本篇文章从JVM的角度来理解Java学习中经常提到的重载和重写. 方法调用:方法调用不等同于方法执行,在Java虚拟机中,方法调用仅仅是为了确定调用哪个版本的方法.方法调用分为解析调用和分派.解析调用一定是静态的,而分派可以是静态的,也可以是动态的.我们这里只介绍分派中的静态分配和动态分配. 静态分配:所有依赖静态类型来定位方法执行版本的分派动作称为静态分配. 下面看个例子,顺便来猜一下结果(面试中经常遇到): 1 class Human { 2 3 } 4 5 class Man extend

WebLogic12.1.1中跨域问题的探讨以及几种常见中间件中跨域问题的解决方法

1.问题描述 扬州现场中最开始安装了中间件WebLogic12.1.1版本,按照公司之前解决WebLogic12的方法,我们在中间件中发布了一个虚拟路径为/,根目目录文件为root的服务. 这个root文件中包含的文件如下: 在程序启动发起跨域访问时,出现了下面这个问题: 如图可见,我们虽然可以访问到crossdomain.xml,并且能得到里面的完整内容,可是程序无法走下去,后面Flash端的跨域访问无法被触发. 2.解决思路 2.1是否是crossdomain.xml内容不对 不同的Flas