问题现象

项目在本地Tomcat下一切正常,但是部署到websphere(was)时启动报出如下错误:

Caused by: org.neo4j.ogm.exception.core.MappingException: Unable to load class with FQN: com.ktanx.model.UserNode
        at org.neo4j.ogm.metadata.reflect.EntityFactory.instantiateObjectFromTaxa(EntityFactory.java:109)
        at org.neo4j.ogm.metadata.reflect.EntityFactory.newObject(EntityFactory.java:58)
        at org.neo4j.ogm.context.GraphEntityMapper.mapNodes(GraphEntityMapper.java:217)
        at org.neo4j.ogm.context.GraphEntityMapper.mapEntities(GraphEntityMapper.java:203)
        at org.neo4j.ogm.context.GraphEntityMapper.map(GraphEntityMapper.java:135)
        at org.neo4j.ogm.context.GraphEntityMapper.map(GraphEntityMapper.java:89)
        at org.neo4j.ogm.session.delegates.LoadOneDelegate.load(LoadOneDelegate.java:70)
        at org.neo4j.ogm.session.delegates.LoadOneDelegate.load(LoadOneDelegate.java:46)
        at org.neo4j.ogm.session.Neo4jSession.load(Neo4jSession.java:155)
        at controllers.UsersController.getUserMetrics(UsersController.java:372)
Caused by: java.lang.ClassNotFoundException: com.ktanx.model.UserNode
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:348)
        at org.neo4j.ogm.metadata.reflect.EntityFactory.instantiateObjectFromTaxa(EntityFactory.java:106)
        at org.neo4j.ogm.metadata.reflect.EntityFactory.newObject(EntityFactory.java:58)
        at org.neo4j.ogm.context.GraphEntityMapper.mapNodes(GraphEntityMapper.java:217)
        at org.neo4j.ogm.context.GraphEntityMapper.mapEntities(GraphEntityMapper.java:203)
        at org.neo4j.ogm.context.GraphEntityMapper.map(GraphEntityMapper.java:135)

主要信息是ClassNotFoundException,找不到class,但是查看war包里面都是存在的,本地Tomcat下部署war包也都一切正常,那么肯定是环境问题导致的了。

分析

通过查看异常信息和远程调试,最终定位到具体出错代码位置是在org.neo4j.ogm.classloader.MetaDataClassLoader的loadClass方法,

private static final ClassLoader classLoader = ClassLoaderResolver.resolve();

public static Class loadClass(final String name) throws ClassNotFoundException {
    return Class.forName(name, false, classLoader);
}

该方法在was下获取到的ClassLoader是AppClassLoader,而加载的时候确实加载不到上面的类,应该是was的隔离机制和Tomcat等不同导致错误的发生。

将ClassLoader改成Thread.currentThread().getContextClassLoader()后则加载成功,看来问题的根源就在这里了。

解决

因为这是个静态方法,修改的话就要直接动jar包,显然这种修改方式并不好。

查看在spring中声明使用的各个bean(项目和spring整合),发现org.springframework.data.neo4j.mapping.Neo4jMappingContext类中有如下代码:

public Neo4jMappingContext(MetaData metaData) {
    this.metaData = metaData;

    for (ClassInfo classInfo : metaData.persistentEntities()) {
        try {
            addPersistentEntity( MetaDataClassLoader.loadClass( classInfo.name() ));
        } catch (ClassNotFoundException e) {
            logger.error("Failed to load class: " + classInfo.name() + " named in ClassInfo due to exception", e);
        }
    }

    logger.info("Neo4jMappingContext initialisation completed");
}

addPersistentEntity( MetaDataClassLoader.loadClass( classInfo.name() ));这行改为

addPersistentEntity( Thread.currentThread().getContextClassLoader().loadClass( classInfo.name() ));

上面的错误信息是没了,但是又报出其它的错误信息,调试后发现neo4j的包中好多地方都用到了MetaDataClassLoader.loadClass( classInfo.name() )这样的代码,例如org.neo4j.ogm.annotations.EntityFactory中:

private <T> T instantiateObjectFromTaxa(String... taxa) {
    if (taxa == null || taxa.length == 0) {
        throw new BaseClassNotFoundException("<null>");
    }

    String fqn = resolve(taxa);

    try {
        @SuppressWarnings("unchecked")
        Class<T> loadedClass = (Class<T>) MetaDataClassLoader.loadClass(fqn); //Class.forName(fqn);
        return instantiate(loadedClass);
    } catch (ClassNotFoundException e) {
        throw new MappingException("Unable to load class with FQN: " + fqn, e);
    }
}

都是直接调用的静态方法且该类都是neo4j的jar包内部使用根本无法扩展替换,所以只能修改MetaDataClassLoader并替换原jar包中的class了。

替换后运行使用都一切正常!

最后

was下的坑总是特别多,部署项目经常就是一个填坑的过程,唉!

另外感觉neo4j相关包封装的扩展性有待提高,像上面的问题完全无法通过自定义class来解决只能hack jar包,Spring目前为止就没碰到过该问题。

参考:https://github.com/neo4j/neo4j-ogm/issues/429

你可能感兴趣的内容
深入浅出ClassLoader 收藏,1998 浏览
深入分析Java ClassLoader原理 收藏,2197 浏览
0条评论

selfly

交流QQ群:32261424
Owner