当前位置: 首页 > 知识库问答 >
问题:

父子类加载器类解析

秋兴思
2023-03-14

我尝试在一个java swing应用程序的缓存实例附近设置两个Oracle Coherence。这里可以找到解决方案。我的案子有点复杂,这就是游戏开始的地方。

在我的情况下,有一个帐户服务。它可以有两个endpoint:SIT和UAT。为了创建两个这样的服务,我需要加载Coherence的两个“实例”,以便用系统变量(tangosol.Coherence.cacheConfig)覆盖endpoint。

我有:

  • the main code of the app is located in the mainapp.jar;
  • the AccountService interface that is located in the account-interfaces.jar;
  • the AccountServiceImpl class that is located in the account-impl.jar and implements the AccountService interface;
  • my main application has the following structure
    bin: startup.bat, startup.sh
    conf: app.properties
    lib: mainapp.jar, account-interfaces.jar, account-impl.jar, coherence.jar
    

    我创建了一个专用的子优先类加载器--InverseClassLoader,并将AppLaunchClassLoader(默认的Thread.currentThread().GetContextClassLoader()classLoader)作为父级。使用InverseClassLoader加载AccountServiceImpl类:

    Class<AccountServiceImpl> acImplClass = contextClassLoader.selfLoad(AccountServiceImpl.class).loadClass(AccountServiceImpl.class);
    Constructor<AccountServiceImpl> acConstructor =
    acImplClass .getConstructor(String.class); 
    AccountService acService = acConstructor .newInstance(serviceURL);
    
    1. 我得到“AccountServiceImpl不能强制转换为AccountService”异常,这意味着这两个类由不同的类加载器加载。但那些类加载器处于父子关系中。所以,即使一个类是由父类(接口'abstract'类型)加载的,它也不能与一个由子类加载器加载的类(具体的impl)一起使用,我说的对吗?那么我们为什么需要这种亲子关系?
    2. 我在代码中指定了AccountService接口,它由默认的类加载器加载。我尝试包装上面的代码是一个线程,并设置InverseClassLoader,它是上下文类加载器。什么都没变。那么我不能使用这样的接口-实现编码(和通常的编码一样),需要一直使用反射来一直调用具体的方法,这是对的吗?(希望有解决方案);
    3. 例如,我列出了要由InverseClassLoader加载的AccountService和AccountServiceImpl类。如果我需要这两个类可以访问的其他类也被InverseClassLoader加载,该怎么办?有没有办法说所有‘相关’类必须由同一个类加载器加载?
    public class InvertedClassLoader extends URLClassLoader {
    
    private final Set<String> classesToNotDelegate = new HashSet<>();
    
    public InvertedClassLoader(URL... urls) {
        super(urls, Thread.currentThread().getContextClassLoader());
    }
    
    public InvertedClassLoader selfLoad(Class<?> classToNotDelegate) {
        classesToNotDelegate.add(classToNotDelegate.getName());
        return this;
    }
    
    @Override
    public Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        if (shouldNotDelegate(className)) {
            System.out.println("CHILD LOADER: " + className);
            Class<?> clazz = findClass(className);
            if (resolve) {
                resolveClass(clazz);
            }
            return clazz;
        }
        else {
            System.out.println("PARENT LOADER: " + className);
            return super.loadClass(className, resolve);
        }
    }
    
    public <T> Class<T> loadClass(Class<? extends T> classToLoad) throws ClassNotFoundException {
        final Class<?> clazz = loadClass(classToLoad.getName());
        @SuppressWarnings("unchecked")
        final Class<T> castedClass = (Class<T>) clazz;
        return castedClass;
    }
    
    private boolean shouldNotDelegate(String className) {
        if (classesToNotDelegate.contains(className) || className.contains("tangosol") ) {
            return true;
        }
        return false;
    }
    

共有1个答案

丰赞
2023-03-14

第1期第一部分我无法复制(见下文)。至于第2部分:类加载器的层次结构是为了防止“X不能强制转换为X”异常。但如果你违反家长第一的规则,你就会陷入困境。

关于问题2:设置线程的上下文类加载器本身不起任何作用,更多的背景信息请参见本文(javaworld.com)。另外,关于问题1,第2部分,引用了文章中的一段话,描述了如果当前类加载器和线程的上下文类加载器之间没有父子关系会发生什么:

请记住,加载和定义类的classloader是该类的内部JVM ID的一部分。如果当前的类加载器加载了一个类X,它随后执行了一个JNDI查找,查找一些类型为Y的数据,那么上下文加载器就可以加载并定义Y。这个Y定义将与当前加载器看到的同名的定义不同。输入模糊类强制转换和加载程序约束冲突异常。

下面是一个简单的演示程序,用于说明从另一个类加载器强制转换到接口可以工作(注意,我使用的是一个简单的Java项目,其类位于bin-folder中,而您的问题中的invertedclassloader位于同一个(测试)包中):

import java.io.File;

public class ChildFirstClassLoading {

    public static void main(String[] args) {

        InvertedClassLoader cl = null;
        try {
            File classesDir = new File(new File("./bin").getCanonicalPath());
            System.out.println("Classes dir: " + classesDir);
            cl = new InvertedClassLoader(classesDir.toURI().toURL());
            cl.selfLoad(CTest.class);
            System.out.println("InvertedClassLoader configured.");
            new CTest("Test 1").test();
            ITest t2 = cl.loadClass(CTest.class)
                    .getConstructor(String.class)
                    .newInstance("Test 2");
            t2.test();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cl != null) {
                try { cl.close(); } catch (Exception ignored) {}
            }
        }
    }

    public interface ITest {
        void test();
    }

    public static class CTest implements ITest {

        static {
            System.out.println("CTest initialized.");
        }

        private String s;
        public CTest(String s) {
            this.s = s;
        }
        public void test() {
            System.out.println(s);
        }
    }

}

如果将itest t2=更改为ctest t2=,您将得到“CTEST不能强制转换为CTEST”异常,但使用该接口可防止该异常。由于这个小演示运行良好,我猜在您的应用程序中会有更多的事情发生,这会破坏类加载。我建议您从类加载工作的情况出发,继续添加代码,直到它破坏类加载。

invertedclassloader看起来很像“子优先类加载器”,有关讨论这种类加载方式的一些好答案,请参阅本问题。子优先类加载器可用于单独加载“相关类”(从第三期开始)。您还可以更新invertedClassLoader以始终“自加载”某些包中的类。记住,“一旦类被类加载器加载,它就会使用这个类加载器加载它需要的所有其他类”(引用自这篇博客文章)。

 类似资料:
  • 一、类加载机制 1.定义: 把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。 在Java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性,Java里天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点来实现的。

  • 本文向大家介绍java 类加载与自定义类加载器详解,包括了java 类加载与自定义类加载器详解的使用技巧和注意事项,需要的朋友参考一下 类加载 所有类加载器,都是ClassLoader的子类。 类加载器永远以.class运行的目录为准。 读取classpath根目录下的文件有以下几种方式: 1 在Java项目中可以通过以下方式获取classspath下的文件: 在Tomcat中tomcat又声明了

  • 框架中所有的类都是通过类加载器(ClassLoader)加载的,通过Loader我们可以实现类的统一管理。下面我们一起来看看Loader提供了哪些加载方法: 1. Loader::import 加载一个类或者加载一个包 方法原型 import( $classPath, $type = IMPORT_APP, $extension=EXT_PHP ) 参数名称 参数说明 $classPath 文件的

  • 加载器,顾名思义,是用于加载元素的,加载的元素可以是库(类),视图文件 , 驱动器 ,辅助函数 , 模型 或其他你自己的文件。 注解 该类由系统自动加载,你无需手工加载。 应用程序"包" 包的视图文件 类参考 应用程序"包" 应用程序包(Package)可以很便捷的将你的应用部署在一个独立的目录中, 以实现自己整套的类库,模型,辅助函数,配置,文件和语言包。 建议将这些应用程序包放置在 appli

  • 本文向大家介绍PHP面向对象程序设计子类扩展父类(子类重新载入父类)操作详解,包括了PHP面向对象程序设计子类扩展父类(子类重新载入父类)操作详解的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了PHP面向对象程序设计子类扩展父类(子类重新载入父类)操作。分享给大家供大家参考,具体如下: 在PHP中,会遇到这样的情况,子类继承父类,但是又需要对父类的属性和方法进行一定的扩展,这时子类可以对属

  • 本文向大家介绍classloader类加载器_基于java类的加载方式详解,包括了classloader类加载器_基于java类的加载方式详解的使用技巧和注意事项,需要的朋友参考一下 基础概念 Classloader 类加载器,用来加载 Java 类到 Java 虚拟机中。与普通程序不同的是。Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机