使用同一类的不同版本加载类:java.lang.LinkageError:尝试为名称复制类定义

使用同一类的不同版本加载类:java.lang.LinkageError:尝试为名称复制类定义,java,jvm,classloader,linkageerror,Java,Jvm,Classloader,Linkageerror,我有一个工作代码,可以用不同的类名动态加载不同的类实现。类文件被加载到内存数据库(apachederby Db)中,类加载器从BLOB列检索.class文件 我想做的是,将.class文件插入为带有version列和启用标志的二进制BLOB,然后classloader将在运行时加载不同版本的类。将有相同数量的已编译类版本的db条目,并且只有一个类的已启用标志设置为TRUE 因为我试图用自定义类加载器加载相同的类名,所以得到以下异常: Exception in thread "main" java

我有一个工作代码,可以用不同的类名动态加载不同的类实现。类文件被加载到内存数据库(apachederby Db)中,类加载器从BLOB列检索.class文件

我想做的是,将.class文件插入为带有version列和
启用标志的二进制BLOB,然后classloader将在运行时加载不同版本的类。将有相同数量的已编译类版本的db条目,并且只有一个类的
已启用
标志设置为TRUE

因为我试图用自定义类加载器加载相同的类名,所以得到以下异常:

Exception in thread "main" java.lang.LinkageError: loader (instance of  com/levent/classloader/DerbyServerClassLoader): attempted  duplicate class definition for name: "com/levent/greeter/Greeter"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at com.levent.classloader.DerbyServerClassLoader.findClass(DerbyServerClassLoader.java:38)
    at com.levent.example.ClientClassLoaderDBVersionDemo.main(ClientClassLoaderDBVersionDemo.java:43)
同一接口(Greeter.java)有两个不同的.class文件(Greeter.class.v1Greeter.class.v2)(插入代码开头)

在测试代码开始时,从lib/classes/folder中检索类文件,并将其作为blob二进制数据插入内存中的db,然后由数据库按顺序检索并加载.class文件。加载具有相同名称的类时,发生异常

我怎样才能解决这个问题?有没有办法卸载一个类,或者重新加载一个同名的类

项目树

java-类加载器 java-连接DerbyDb的实用程序类 ClientClassLoaderDBVersionDemo.java-测试代码 输出 我怎样才能解决这个问题?有没有办法卸载一个类,或者重新加载一个同名的类

无法强制1卸载类。除非卸载旧类,否则无法将新版本的类加载到同一个类加载器中。(这是因为类的真实标识是一个元组,由类的完全限定名和类装入器标识组成。)

解决方案是在新的类加载器中加载新版本的类

目前还不清楚这对您是否可行,但不幸的是,这是唯一可用的选项。JVM以一种您无法破坏的方式执行“重复类定义”检查。该检查具有安全性和JVM稳定性影响



1-没有被任何可访问对象引用的类最终将被GC卸载(modulo JVM命令行选项、版本等)。但是,消除对类的所有引用可能会很棘手。此外,现在反复强制GC运行对整体性能不利。

我认为问题可能来自您使用的父类
类加载器。您并没有重载loadClass方法,所以您是在父类中进行委托,具体地说是在
ClassLoader.getSystemClassLoader()

正如javadoc在

此方法首先在运行时启动序列的早期调用, 此时它创建系统类加载器并将其设置为 调用线程的上下文类加载器

您希望加载更改后的类,但您正在将操作委托给
线程类加载器
,这有点混乱

您可以使用自己的类
ClassLoader

package a;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

public class ReloadTest {

    public static void main(String[] args) throws ClassNotFoundException, IOException {

        final Class<?> clazz = ReloadTest.class;

        System.out.println("Class: " +  clazz.hashCode());

        final URL[] urls = new URL[1];

        urls[0] =  clazz.getProtectionDomain().getCodeSource().getLocation();
        final ClassLoader delegateParent = clazz.getClassLoader().getParent();

        try (final URLClassLoader cl = new URLClassLoader(urls, delegateParent)) {

            final Class<?> reloadedClazz = cl.loadClass(clazz.getName());
            System.out.println("Class reloaded: " + reloadedClazz.hashCode());
            System.out.println("Are the same: " + (clazz != reloadedClazz) );
        }
    }
}
a包;
导入java.io.IOException;
导入java.net.URL;
导入java.net.URLClassLoader;
公共类重载测试{
公共静态void main(字符串[]args)抛出ClassNotFoundException、IOException{
最终类clazz=重载测试.Class;
System.out.println(“类:+clazz.hashCode());
最终URL[]URL=新URL[1];
URL[0]=clazz.getProtectionDomain().getCodeSource().getLocation();
final ClassLoader delegateParent=clazz.getClassLoader().getParent();
try(最终URLClassLoader cl=新URLClassLoader(URL,delegateParent)){
最终类重载clazz=cl.loadClass(clazz.getName());
System.out.println(“类重载:+reloadedClazz.hashCode());
System.out.println(“相同:”+(clazz!=重载clazz));
}
}
}
希望有帮助

p.D:这个链接与同样的问题有关,希望它也有帮助

你看了吗
package com.levent.greeter;

public interface Greet {

    public String getGreetMessage();

}
package com.levent.derbyutility;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DbSingleton {

    private static DbSingleton instance = null;

    private Connection conn = null;

    private DbSingleton() {
        try{
            DriverManager.registerDriver(new org.apache.derby.jdbc.EmbeddedDriver());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static DbSingleton getInstance() {
        if(instance == null) {
            synchronized(DbSingleton.class) {
                if(instance == null) {
                    instance = new DbSingleton();
                }
            }
        }

        return instance;
    }

    public Connection getConnection() throws SQLException {
        if(conn == null || conn.isClosed()) {
            synchronized (DbSingleton.class) {
                if(conn == null || conn.isClosed()) {
                    try{
                        //String dbUrl = "jdbc:derby://localhost:1527/myDB;create=true;user=me;password=mine";
                        String dbUrl = "jdbc:derby://localhost:1527/memory:myDB;create=true";

                        conn = DriverManager.getConnection(dbUrl);
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return conn;
    }

}
package com.levent.example;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

import com.levent.classloader.DerbyServerClassLoader;
import com.levent.derbyutility.DbSingleton;
import com.levent.greeter.Greet;

public class ClientClassLoaderDBVersionDemo {

    // apache derby in-memory db
    private static final String connectionString = "jdbc:derby://localhost:1527/memory:myDB;create=true";
    private static final String classFileName1 = "Greeter.class.v1";
    private static final String classFileName2 = "Greeter.class.v2";
    private static final String className = "com.levent.greeter.Greeter";

    public static void main(String[] args) {
        prepareClass();

        try {
            Greet greet = null;

            DerbyServerClassLoader cl = new DerbyServerClassLoader(connectionString);

            updateVersion(className, "v1");
            Class clazz1 = cl.findClass(className);
            greet = (Greet) clazz1.newInstance();
            System.out.println("Version 1 Greet.getGreetMessage() : " + greet.getGreetMessage());

            updateVersion(className, "v2");
            Class clazz2 = cl.findClass(className);
            greet = (Greet) clazz2.newInstance();
            System.out.println("Version 2 Greet.getGreetMessage() : " + greet.getGreetMessage());           
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static void prepareClass() {
        DbSingleton instance = DbSingleton.getInstance();

        Connection conn = null;

        try {
            conn = instance.getConnection();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }

        Statement sta;

        if (conn != null) {
            try {
                sta = conn.createStatement();
                int count = sta
                        .executeUpdate("CREATE TABLE CLASSES (CLASS_NAME VARCHAR(50), CLASS BLOB, IS_ENABLED BOOLEAN, VERSION VARCHAR(10) )");
                System.out.println("CLASSES Table created");
                sta.close();

                sta = conn.createStatement();

                PreparedStatement psta = conn.prepareStatement("INSERT INTO CLASSES (CLASS_NAME, CLASS, IS_ENABLED, VERSION) values (?, ?, ?, ?)");
                byte[] bytes = null;
                InputStream blobObject = null;

                psta.setString(1, className);
                bytes = readJarFileAsByteArray(classFileName1);
                blobObject = new ByteArrayInputStream(bytes); 
                psta.setBlob(2, blobObject, bytes.length);
                psta.setBoolean(3, false);
                psta.setString(4, "v1");
                count = psta.executeUpdate();

                psta.setString(1, className);
                bytes = readJarFileAsByteArray(classFileName2);
                blobObject = new ByteArrayInputStream(bytes); 
                psta.setBlob(2, blobObject, bytes.length);
                psta.setBoolean(3, false);
                psta.setString(4, "v2");
                count += psta.executeUpdate();

                System.out.println(count + " record(s) created.");
                sta.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static byte[] readJarFileAsByteArray(String classFileName) {
        Path currentRelativePath = Paths.get("");
        String s = currentRelativePath.toAbsolutePath().toString();

        File file = new File(s + "/lib/classes/" + classFileName);
        byte[] fileData = new byte[(int) file.length()];
        DataInputStream dis;
        try {
            dis = new DataInputStream(new FileInputStream(file));
            dis.readFully(fileData);
            dis.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return fileData;
    }

    private static void updateVersion(String className, String version) {
        DbSingleton instance = DbSingleton.getInstance();

        Connection conn = null;

        try {
            conn = instance.getConnection();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }

        Statement sta;

        if (conn != null) {
            try {
                int count = 0;
                sta = conn.createStatement();
                PreparedStatement psta = conn.prepareStatement("UPDATE CLASSES SET IS_ENABLED = ? WHERE CLASS_NAME = ?");
                psta.setBoolean(1, false);
                psta.setString(2, className);
                count = psta.executeUpdate();
                System.out.println(count + " record(s) updated.");

                psta = conn.prepareStatement("UPDATE CLASSES SET IS_ENABLED = ? WHERE CLASS_NAME = ? AND VERSION = ?");

                psta.setBoolean(1, true);
                psta.setString(2, className);
                psta.setString(3, version);

                count = psta.executeUpdate();

                System.out.println(count + " record(s) updated.");
                sta.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}
CLASSES Table created
2 record(s) created.
2 record(s) updated.
1 record(s) updated.
Version 1 Greet.getGreetMessage() : Hail to the King Baby!
2 record(s) updated.
1 record(s) updated.
Exception in thread "main" java.lang.LinkageError: loader (instance of  com/levent/classloader/DerbyServerClassLoader): attempted  duplicate class definition for name: "com/levent/greeter/Greeter"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at com.levent.classloader.DerbyServerClassLoader.findClass(DerbyServerClassLoader.java:38)
    at com.levent.example.ClientClassLoaderDBVersionDemo.main(ClientClassLoaderDBVersionDemo.java:43)
package a;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

public class ReloadTest {

    public static void main(String[] args) throws ClassNotFoundException, IOException {

        final Class<?> clazz = ReloadTest.class;

        System.out.println("Class: " +  clazz.hashCode());

        final URL[] urls = new URL[1];

        urls[0] =  clazz.getProtectionDomain().getCodeSource().getLocation();
        final ClassLoader delegateParent = clazz.getClassLoader().getParent();

        try (final URLClassLoader cl = new URLClassLoader(urls, delegateParent)) {

            final Class<?> reloadedClazz = cl.loadClass(clazz.getName());
            System.out.println("Class reloaded: " + reloadedClazz.hashCode());
            System.out.println("Are the same: " + (clazz != reloadedClazz) );
        }
    }
}