使用相同类的不同版本进行类加载:java.lang.LinkageError:尝试对name进行重复的类定义
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了使用相同类的不同版本进行类加载:java.lang.LinkageError:尝试对name进行重复的类定义,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含10718字,纯文字阅读大概需要16分钟。
内容图文
我有一个工作代码,动态加载具有不同类名的不同类实现.类文件被加载到内存数据库(Apache Derby Db)中,类加载器从BLOB列中检索.class文件.
我想要做的是,将.class文件作为带有版本列和IS_ENABLED标志的二进制BLOB插入,然后classloader将在运行时加载不同版本的类.将有db条目相同数量的已编译类版本,并且只有一个类IS_ENABLED标志设置为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.v1,Greeter.class.v2)(在代码的开头插入)
在测试代??码的开头,从lib / classes /文件夹中检索类文件,并将其作为blob二进制数据插入到内存数据库中,然后,数据库按顺序检索.class文件并加载.在加载具有相同名称的类时,会发生异常.
我怎么解决这个问题?有没有办法卸载一个类,或者无论如何重新加载一个具有相同名称的类?
项目树
DerbyServerClassLoader.java – 类加载器
package com.levent.classloader;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DerbyServerClassLoader extends ClassLoader {
private ClassLoader parent;
private String connectionString;
public DerbyServerClassLoader(String connectionString) {
this(ClassLoader.getSystemClassLoader(), connectionString);
}
public DerbyServerClassLoader(ClassLoader parent, String connectionString) {
super(parent);
this.parent = parent;
this.connectionString = connectionString;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
Class cls = null;
try {
cls = parent.loadClass(name); // Delegate to the parent Class Loader
} catch (ClassNotFoundException clnfE) { // If parent fails, try to locate and load the class
byte[] bytes = new byte[0];
try {
bytes = loadClassFromDatabase(name);
} catch (SQLException sqlE) {
throw new ClassNotFoundException("Unable to load class", sqlE);
}
return defineClass(name, bytes, 0, bytes.length);
}
return cls;
}
private byte[] loadClassFromDatabase(String name) throws SQLException {
PreparedStatement pstmt = null;
Connection connection = null;
try {
connection = DriverManager.getConnection(connectionString);
String sql = "SELECT CLASS FROM CLASSES WHERE CLASS_NAME = ? AND IS_ENABLED = ?";
pstmt = connection.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setBoolean(2, true);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
Blob blob = rs.getBlob(1);
byte[] data = blob.getBytes(1, (int) blob.length());
return data;
}
} catch (SQLException e) {
System.out.println("Unexpected exception: " + e.toString());
} catch (Exception e) {
System.out.println("Unexpected exception: " + e.toString());
} finally {
if (pstmt != null) {
pstmt.close();
}
if(connection != null) {
connection.close();
}
}
return null;
}
}
Greet.java – Greeter界面
package com.levent.greeter;
public interface Greet {
public String getGreetMessage();
}
DbSingleton.java – 连接DerbyDb的实用程序类
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;
}
}
ClientClassLoaderDBVersionDemo.java – 测试代码
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)
解决方法:
我认为问题将来自您正在使用的父ClassLoader.您没有重载loadClass方法,因此您在父类中委派,具体在ClassLoader.getSystemClassLoader()中.
正如javadoc在https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#getSystemClassLoader()所说
This method is first invoked early in the runtime’s startup sequence,
at which point it creates the system class loader and sets it as the
context class loader of the invoking Thread.
您想要加载更改的类但是您委托Thread ClassLoader操作,有点令人困惑.
您可以使用自己的Class 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) );
}
}
}
希望有帮助!
P.D:这个链接与同样的问题有关,也许它也有帮助Reload used classes at runtime Java
内容总结
以上是互联网集市为您收集整理的使用相同类的不同版本进行类加载:java.lang.LinkageError:尝试对name进行重复的类定义全部内容,希望文章能够帮你解决使用相同类的不同版本进行类加载:java.lang.LinkageError:尝试对name进行重复的类定义所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。