0 0

classloader 加载同名类问题25

近期研究classloader,

有个疑问:

有人说:每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能 出现两个同名的类。 有些疑问,同名类是指的类名相同,如果包路径不同的话算不算同名类,这种类加载时会不会出现问题。

自己在自己的系统中写了一套测试内容,使用main方法执行自定义classloader加载系统所有的类,遇到2个类名相同,但是包路径不同,加载出现java.lang.LinkageError: duplicate class definition异常。

但是如果使用tomcat启动的时候这两个类却可以顺利通过加载,看了下tomcat启动的时候加载类使用的是WebappClassloader。

 

为什么? 大家说说呗。

 


问题补充:自定义的loader代码:
package cn.sforce.test.startup.classloader;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

import cn.sforce.test.startup.TestOut;
import cn.sforce.test.startup.TestURLStreamHandler;
import cn.sforce.test.startup.Util;


public class UnitClassLoader extends ClassLoader
{
protected static TestOut log = TestOut.getLogger( UnitClassLoader.class.getName() );

/**
* 驱动程序列表
*/
private static String driverLibList[] = null;

/**
* WEB公共库
*/
private static String freezeLibList[] = null;

/**
* 应用的类路径
*/
protected String webPath = null;
protected String webClazzPath = null;

/**
* 父装载器
*/
private ClassLoader parent = null;

// 从库中加载的类
private HashMap<String, FreezeResource> clazzList = new HashMap<String, FreezeResource>();
private HashMap<String, FreezeResource> fileList = new HashMap<String, FreezeResource>();

// 工程类装载器
private ArrayList<ProjLoaderInfo> jarLoaderList = new ArrayList<ProjLoaderInfo>();

public UnitClassLoader( ClassLoader parent, String webPath, String... projPath ) throws Exception
{
super( parent );
this.parent = parent;
this.webPath = webPath;
this.webClazzPath = webPath + "/WEB-INF/classes/";

// 加载系统JAR文件
loadAllJar();

// 初始化类装载器
URL root = System.class.getResource( "/" );
String classPath = root.getPath().replace( '\\', '/' );
if( classPath != null ){
//linkProjSource( classPath );
}

// 关联类的装载器
if( projPath != null ){
for( String path : projPath ){
//linkProjSource( path );
}
}
}

/**
* 加载系统JAR文件
* @throws Exception
*/
private void loadAllJar() throws Exception
{
String driverPath = this.getClass().getResource("/").getFile();
if( driverPath.endsWith("/bin/") ){
driverPath = driverPath.substring( 0, driverPath.length()-4 );
}
else if( driverPath.endsWith("/classes/") ){
driverPath = driverPath.substring( 0, driverPath.length()-8 );
}

driverPath += "lib/";
log.info( "驱动程序库文件的目录 = " + driverPath );
driverLibList = Util.getFileList( driverPath, "jar" );
for( int ii=0; ii<driverLibList.length; ii++ ){
String fileName = driverPath + driverLibList[ii];
JarLoader.loadAllFile( clazzList, fileList, fileName );
}

// 不加载的库
String ex[] = {
"axiom-api-1.2.5.jar",
"axiom-dom-1.2.5.jar",
"axiom-impl-1.2.5.jar",
"axis2-adb-1.3.jar",
"axis2-kernel-1.3.jar",
"axis2-metadata-1.3.jar",
"axis2-xmlbeans-1.3.jar",
"neethi-2.0.2.jar",
"wsdl4j-1.6.2.jar",
"ws-policy-SNAPSHOT.jar",
"je-3.1.0.jar",
"jfreechart-1.0.12.jar",
"jcommon-1.0.10.jar",
"itext-2.1.7.jar",
"itext-rtf-2.1.7.jar",
"iTextAsian.jar",
"XmlSchema-1.4.2.jar",
"poi-3.6-20091214.jar",
"poi-contrib-3.6-20091214.jar",
"poi-scratchpad-3.6-20091214.jar",
"mail.jar",
"lucene-core-2.0.0.jar",
"ant.jar",
"antlr-2.7.5.jar",
"jasperreports-4.0.2.jar",
"annogen-0.1.0.jar",
"commons-digester-1.7.jar",
"commons-fileupload-1.2.1.jar",
"commons-httpclient-3.0.1.jar",
"sforce-commons-el-1.0.jar",
"org-netbeans-lib-cvsclient.jar",
"servlet-api.jar",
"velocity-1.5.jar",
"wstx-asl-3.2.5.jar",
"xalan-2.7.0.jar"
};

int x = 0;
String libPath = webPath + "/WEB-INF/lib/";
freezeLibList = Util.getFileList( libPath, "jar" );
for( int ii=0; ii<freezeLibList.length; ii++ ){
String fileName = freezeLibList[ii];
for( x=ex.length-1; x>=0; x-- ){
if( ex[x].compareTo(fileName) == 0 ){
break;
}
}

if( x < 0 ){
JarLoader.loadAllFile( clazzList, fileList, libPath + fileName );
}
}
}

/**
* @param clazzName
* @param resolve
* @return java.lang.Class
* @throws java.lang.ClassNotFoundException
*/
public Class<?> loadClass(String clazzName, boolean resolve)
throws LinkageError, ClassNotFoundException
{
Class<?> clazz = null;

// 加载关联项目的类
for( ProjLoaderInfo l : jarLoaderList ){
clazz = l.loadClass( clazzName, resolve );
if( clazz != null ){
return clazz;
}
}

// 如果不需要刷新,从缓存中装入
clazz = findLoadedClass(clazzName);
if( clazz != null ){
return clazz;
}

// 读类文件并装入
FreezeResource c = clazzList.get( clazzName );
if( c != null ){
byte data[] = c.getClazzData();
clazz = loadClass( clazzName, data );
if( clazz != null ){
if( resolve ){

System.out.println("unit加载类---------->"+clazz.getName());
    resolveClass(clazz);
    }

return clazz;
}
}

    try{
byte[] data = loadClazzData( clazzName );
if( data != null ){
clazz = loadClass( clazzName, data );
}
    }
    catch( LinkageError e ){
    throw e;
    }
catch( Throwable ex ){

}

// 如果上面没有成功,尝试用默认的ClassLoader装入它
    if (clazz == null){
try{
//clazz = findSystemClass(name);
clazz = this.getParent().loadClass(clazzName);
}
catch( ClassNotFoundException e ){
throw new ClassNotFoundException( "加载类[" + clazzName + "]时错误", e );
}
    }

    if( clazz == null ){
throw new ClassNotFoundException( "加载类[" + clazzName + "]时错误" );
    }
    else if( resolve ){
    resolveClass(clazz);
    }
   
    return clazz;   
}

/**
* @param className
* @return java.lang.Class
* @throws java.lang.ClassNotFoundException
*/
public Class<?> loadClass(String className) throws ClassNotFoundException
{
return loadClass( className, false );
}

/**
* @param className
* @return java.lang.Class
* @throws java.lang.ClassNotFoundException
*/
public Class<?> findClass(String className) throws ClassNotFoundException
{
// 先查找缓冲区
Class<?> clazz = findLoadedClass( className );
if( clazz == null ){
byte[] data = loadClazzData( className );
if( data != null ){
clazz = loadClass( className, data );
}
}

if( clazz == null ){
throw new ClassNotFoundException( "加载类[" + className + "]时错误" );
}

return clazz;
}

/**
* @param className
* @return java.lang.Class
* @throws java.lang.ClassNotFoundException
*/
protected Class<?> loadClass(String className, byte[] data) throws ClassNotFoundException
{
Class<?> clazz;
try{
clazz = defineClass(className, data, 0, data.length);
}
catch( ClassFormatError e ){
log.error( "类文件[" + className + "]格式错误" );
throw new ClassNotFoundException( "类文件[" + className + "]格式错误", e );
}

return clazz;
}

/**
* @param clazzName
* @return byte[]
* @throws java.lang.ClassNotFoundException
*/
protected byte[] loadClazzData( String clazzName )
{
InputStream is = null;

try {
// 从当前工程加载类
String fileName = webClazzPath + clazzName.replace('.', '/') + ".class";
File fp = new File( fileName );
if( !fp.exists() ){
throw new ClassNotFoundException( "类文件[" + clazzName + "]不存在" );
}

try {
is = new FileInputStream( fp );
}
catch( Throwable e ){
throw new ClassNotFoundException( "加载类[" + clazzName + "]时错误", e );
}

return Util.readFile( is );
}
catch( Throwable e ){
return null;
}
finally{
Util.closeInputStream( is );
}
}

/**
* 是否有效的类
* @param clazz
* @return
*/
protected boolean isLoadedClass( Class<?> clazz )
{
ClassLoader loader = clazz.getClassLoader();
if( loader == this ){
return true;
}

for( ProjLoaderInfo l : jarLoaderList ){
if( l.getLoader() == loader ){
return true;
}
}

return false;
}

/**
* 检查类是否已经更新
* @return
*/
public void chechClazzModify()
{
boolean flag = false;
for( ProjLoaderInfo l : jarLoaderList ){
// 不要检查junit的类
String path = l.getClassPath();
if( path.indexOf("junit-") < 0 && path.indexOf("freeze") < 0 ){
if( l.chechClazzModify() ){
flag = true;
break;
}
}
}

// 清空所有类装载器
if( flag ){
System.out.println( "清空所有已经装载的类" );

for( ProjLoaderInfo l : jarLoaderList ){
// 不要清空junit的类
String path = l.getClassPath();
if( path.indexOf("junit-") < 0 && path.indexOf("freeze") < 0 ){
l.clear();
}
}
}
}

public InputStream getResourceAsStream( String fileName )
{
int ptr = fileName.indexOf( ':' );
if( ptr > 0 ){
String f1 = fileName.substring( 0, ptr );
String f2 = fileName.substring( ptr+1 );
if( f1.endsWith("/test-unit") ){
FreezeResource t = fileList.get( f2 );
if( t != null ){
byte data[] = t.getClazzData();
return new ByteArrayInputStream( data );
}
}
}

InputStream is = this.getClass().getResourceAsStream( fileName );
if( is != null ){
return is;
}

FreezeResource t = fileList.get( fileName );
if( t != null ){
byte data[] = t.getClazzData();
return new ByteArrayInputStream( data );
}

is = super.getResourceAsStream( fileName );
if( is != null ){
return is;
}

return null;
}

public URL getResource( String path )
{
if( path == null ){
return null;
        }

        path = normalize(path);
        if (path == null){
            return null;
        }
       
        FreezeResource t = fileList.get( path );
        if( t != null ){
        try{
        TestURLStreamHandler u = new TestURLStreamHandler();
        u.setData( t.getClazzData() );
        return new URL( null, null, -1, path, u );
        }
        catch( Throwable e ){
       
        }
        }
       
URL url = super.getResource( path );
if( url != null ){
return url;
}

        if( path.charAt(0) != '/' ){
        path = "/WEB-INF/classes/" + path;
        }
       
        File file = new File( webPath + path );
        if(file.exists() == false){
            return null;
        }
       
        try{
        // log.info( path + " ==> " + file.toURL() );
        return file.toURL();
        }
        catch( Exception e ){
        return null;
        }
}

    private String normalize(String path) {

        String normalized = path;
       
        if (normalized.indexOf('\\') >= 0){
            normalized = normalized.replace('\\', '/');
        }
       
        // Resolve occurrences of "/../" in the normalized path
        while (true) {
            int index = normalized.indexOf("/../");
            if (index < 0)
                break;
            if (index == 0)
                return (null);  // Trying to go outside our context
            int index2 = normalized.lastIndexOf('/', index - 1);
            normalized = normalized.substring(0, index2) +
                normalized.substring(index + 3);
        }
       
        // Return the normalized path that we have completed
        return (normalized);
    }
   
    /**
     * 初始化关联的Project
     */
    public void linkProjSource( String output )
    {
    if( output.indexOf("freeze-unit") >= 0 ){
    return;
    }
   
if( output.endsWith("/") == false ){
output = output + "/";
}

    // 检查是否已经存在
    for( ProjLoaderInfo j : jarLoaderList ){
    String path = j.getClassPath();
    if( path.compareTo(output) == 0 ){
    return;
    }
    }
   
try{
ProjLoaderInfo l = new ProjLoaderInfo( parent, this, webPath, output );
jarLoaderList.add( l );
}
catch( Throwable e ){

}
    }

public String getWebPath()
{
return webPath;
}

public String getWebClazzPath()
{
return webClazzPath;
}
   
}
2012年11月22日 11:24

2个答案 按时间排序 按投票排序

0 0

java.lang.LinkageError: duplicate class definition  :  表示重复加载了类定义,即多次load同一个类.


// 加载关联项目的类
for( ProjLoaderInfo l : jarLoaderList ){
clazz = l.loadClass( clazzName, resolve );
if( clazz != null ){
return clazz;
}
}

// 如果不需要刷新,从缓存中装入
clazz = findLoadedClass(clazzName);
if( clazz != null ){
return clazz;
}

初步怀疑可能是 ProjLoaderInfo(clazz = l.loadClass( clazzName, resolve );  ) 即loadClass实现时是否调用clazz = findLoadedClass(clazzName);

看你给出的代码也不是很全,如果方便请提供完整的例子方便找原因.

2012年11月25日 12:20
0 0

准确的说Java中类名是包含包名的,所以你说的所谓同名类对JVM来说不是同一个类,不会出现同名类的问题。
至于你自定以的ClassLoader,可以把你的详细代码贴出来吗?可能是你写的代码的问题

2012年11月22日 11:37

相关推荐

    ClassLoader类加载器

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。了解和掌握ClassLoader的工作原理以及如何自定义ClassLoader对于深入理解Java应用程序的运行机制非常有帮助。以下是对...

    Tomcat加载顺序

    与Common ClassLoader类似,但它的优先级更高,这意味着如果在两个目录下有同名的类,Shared ClassLoader加载的类将被优先考虑。 #### 6. WebApp ClassLoader WebApp ClassLoader是最底层的类加载器,它负责加载每...

    classloader的简单实现

    2. 类的唯一性:同一个类加载器实例加载的类具有唯一性,即使两个类加载器加载了相同的类路径下的同名类,只要加载它们的类加载器不同,那么这两个类在JVM中就被视为不同的类。 3. 安全性:通过类加载器的双亲委派...

    java classLoader 的全面讲解

    1. 类的唯一标识:类在JVM中的唯一标识是由全限定名(包名+类名)和加载它的类加载器共同决定的,这允许同名类在不同类加载器下不冲突。 2. 类的查找与加载:类加载器根据类的全限定名在指定的目录或jar文件中查找...

    自定义类加载器实现自定义加载

    - 类的唯一性:类加载器与类的关系是“一对多”,同一类加载器下的类名必须唯一,不同类加载器加载的同名类不冲突。 - 双亲委派模型的遵守:自定义类加载器通常应遵循双亲委派模型,除非有特殊需求,否则不应轻易...

    Java的类加载器

    Extension ClassLoader加载`&lt;JAVA_HOME&gt;/jre/lib/ext`目录下的扩展类库。AppClassLoader(也叫System ClassLoader)负责加载用户类路径(ClassPath)中的类。 3. **双亲委派模型** 类加载器遵循双亲委派模型,即当...

    自定义类加载器

    3. **隔离加载**:在不同加载器实例之间创建类的隔离,使得不同版本的同名类可以共存。 ### 总结 自定义类加载器是Java平台灵活性的体现,它允许开发者对类加载过程进行扩展和定制,满足特殊需求。理解类加载器的...

    Java类加载机制学习1

    这意味着不同类加载器加载的同名类在JVM中被视为不同的类,这为实现模块化和版本控制提供了基础。 在某些场景下,比如版本更新时,我们可能希望新旧代码能够共存。这时,自定义类加载器如`ParentLastClassLoader....

    详解JVM类加载机制及类缓存问题的处理方法

    解决类缓存问题的方法有多种,例如使用不同的类加载器加载同名的类,或者使用不同的命名空间来避免类冲突。 在JVM中,类加载器会将类加载到内存中,并将其存储在方法区中。方法区是JVM用于存储类的运行时数据结构的...

    Java classloader和namespace详细介绍

    这种方式保证了核心类库的优先级,避免了不同ClassLoader加载相同类导致的冲突问题。 命名空间是每个ClassLoader特有的,由它装载的类拥有独特的标识。即使两个类具有相同的全限定名,只要它们是由不同的...

    使用自定义ClassLoader解决反序列化serialVesionUID不一致问题 _ 回忆飘如雪1

    但是,它存在三个挑战:一是不便于隔离不一致的JAR,因为可能存在父`ClassLoader`中的同名类覆盖;二是不便于共享无冲突的依赖;三是添加类到`ClassLoader`时,只能添加JAR,不能直接添加类。 为了解决这些问题,...

    [浅析J2EE应用服务器的JAVA类装载器]python回朔异常的模块.docx

    这意味着不同类加载器加载的同名类是相互独立的,不能直接交互。 2. 委托规则:在加载类之前,类加载器总是先询问其父加载器是否已经加载过该类。这保证了核心API的优先加载,也有助于维持系统的安全性和稳定性。 ...

    深入探索 Java 热部署

    然而,JVM 不允许同一 ClassLoader 加载同名的不同类,因此需要借助 ASM(Abstract Syntax Tree Manipulation)这样的字节码操作库。 ASM 是一个强大的 Java 字节码操控和分析框架,允许开发者动态生成类或修改已...

    JSP参数接收工具类.rar

    - 自定义类加载器:可能涉及如何继承`java.lang.ClassLoader`,实现自己的类加载策略,比如从特定目录或数据库加载类。 - 动态加载:在运行时根据需求加载类,实现代码的热替换或模块化。 - 类加载的生命周期:...

    Spring中使用classpath加载配置文件浅析

    如果存在同名的配置文件,但位于不同的位置,根据Spring加载类路径资源的策略,可能会加载到一个错误的文件。为了避免这种情况,开发者需要仔细设计自己的目录结构,并确保配置文件的命名和位置唯一,以避免冲突。 ...

    Java常问到的几个中级以上问题

    4. 用户自定义类加载器:开发者可以通过继承ClassLoader类来创建自己的类加载器,以满足特定的应用场景,例如加载网络上的类或者加密后的类。 类加载器之间存在层次关系,BootStrapClassLoader加载的类对于其他类...

    java类重载,可以用于热更新

    在Java中,类是由ClassLoader加载的,当一个类被加载后,如果该类的.class文件被修改,ClassLoader可以重新加载这个修改后的类,从而实现运行时的代码更新。但是,需要注意的是,热更新并非总是可行的,有些复杂的...

    记录我的研究的代表,可能从 0 到 0.1.zip

    基础篇Java反射补充:通过反射修改用final static修饰的变量Java动态代理JNDI注入反序列化类加载器与双亲委派模型两种实现Java类隔离加载的方法(当然同名目录下也有pdf,防止以后站不在了)ClassLoader(类加载机制)SPI...

Global site tag (gtag.js) - Google Analytics