【问题标题】:Spring framework - class loader relationshipSpring框架-类加载器关系
【发布时间】:2009-02-16 23:08:59
【问题描述】:

我遇到了一个可能与 Spring / 类加载器关系有关的问题。

我现在为这个问题的冗长道歉。

我有许多最初编写并打算在专用 JVM 中运行的遗留 Java 应用程序。我们在检查资源使用情况后决定,可以通过在单个 JVM 中运行多个应用程序来提高效率。在这种情况下,我编写了一个专门为我们的环境设计的容器管理器,它能够在容器(或沙箱)中运行每个应用程序。这种隔离容器的能力的基础当然是自定义类加载器。

在我们遇到一个使用 Spring 框架的应用程序之前,一切都很好。我有一个带有如下片段的 Spring 配置文件。

<bean id="MDDStructurPackager" class="abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000">
    <property name="logger">
        <ref local="Logger"/>
    </property>
    <property name="realm">
        <value>unpack</value>
    </property>
</bean>

<bean id="Jakarta" class="abc.def.mdd.channel.corba.M2000AlarmChannel">
    <constructor-arg>
        <ref bean="MDDStructurPackager"/>
    </constructor-arg>
    <property name="interactionLayer">
        <ref local="MDDInteractionLayer"/>
    </property>
    <property name="logger">
        <ref local="Logger"/>
    </property>
    <property name="tempFile" value="/opt/app/abcdef/rt_dev/var/cache/dat/Huawei_M2000_Jakarta.dat"/>
    <property name="host" value="M2000Jakarta.ior"/>
    <property name="irpReference" value="clarity"/>
    <property name="name" value="M2000Jakarta"/>
    <property name="realm" value="Jakarta"/>
	<property name="natAddress" value="99.999.99.9" />
</bean>


以下是 Spring 实例化 bean 时日志文件中的片段。

调试 [South Agent 1] (DefaultSingletonBeanRegistry.java:162) - 创建单例 bean 'MDDStructurPackager' 的共享实例

调试 [South Agent 1] (AbstractAutowireCapableBeanFactory.java:378) - 创建 bean 'MDDStructurPackager' 的实例

调试 [South Agent 1] (AbstractAutowireCapableBeanFactory.java:453) - 急切地缓存 bean 'MDDStructurPackager' 以允许解析潜在的循环引用

调试 [South Agent 1] (AbstractBeanFactory.java:213) - 返回单例 bean 'Logger' 的缓存实例

DEBUG [South Agent 1] (CachedIntrospectionResults.java:242) - 获取类 [abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000] 的 BeanInfo

DEBUG [South Agent 1] (CachedIntrospectionResults.java:258) - 缓存类 [abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000] 的 PropertyDescriptors

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.Class] 的 bean 属性“类”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“colectionDate”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型为 [abc.def.mdd.msg.MDDComponent] 的 bean 属性“组件”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“fileType”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型为 [java.util.ArrayList] 的 bean 属性“listCommonWords”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到 [java.util.ArrayList] 类型的 bean 属性“listHeader”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到 [abc.def.mdd.logger.Logger] 类型的 bean 属性“记录器”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.util.Map] 的 bean 属性“mapDelimiter”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到 [java.util.Map] 类型的 bean 属性“mapElement”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“namePackager”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“nameSpaceXMLSchema”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“nodeName”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“packageXMLSchema”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型为 [abc.def.mdd.channel.ProcessingLayerListener] 的 bean 属性“processingLayerListener”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“领域”

调试 [South Agent 1] (CachedIntrospectionResults.java:267) - 找到类型 [java.lang.String] 的 bean 属性“statesObject”

DEBUG [South Agent 1] (AbstractAutowireCapableBeanFactory.java:406) - 完成创建 bean 'MDDStructurPackager' 的实例

调试 [South Agent 1] (DefaultSingletonBeanRegistry.java:162) - 创建单例 bean 'ListAlarmChannel' 的共享实例

调试 [South Agent 1] (AbstractAutowireCapableBeanFactory.java:378) - 创建 bean 'ListAlarmChannel' 的实例

调试 [South Agent 1] (AbstractAutowireCapableBeanFactory.java:453) - 急切地缓存 bean 'ListAlarmChannel' 以允许解析潜在的循环引用

调试 [South Agent 1] (DefaultSingletonBeanRegistry.java:162) - 创建单例 bean 'Jakarta' 的共享实例

调试 [South Agent 1] (AbstractAutowireCapableBeanFactory.java:378) - 创建 bean 'Jakarta' 的实例

调试 [South Agent 1] (AbstractBeanFactory.java:213) - 返回单例 bean 'MDDStructurPackager' 的缓存实例

信息:CORBA_HUAWEI_M2KJKT_ALARM (23447542):在类路径中添加了“/opt/app/abcdef/rt_dev/lib/logkit-1.2.jar”。

信息:CORBA_HUAWEI_M2KJKT_ALARM (23447542):在类路径中添加了“/opt/app/abcdef/rt_dev/lib/avalon-framework-4.1.5.jar”。

信息:CORBA_HUAWEI_M2KJKT_ALARM (23447542):在类路径中添加了“/opt/app/abcdef/rt_dev/lib/concurrent-1.3.2.jar”。

信息:CORBA_HUAWEI_M2KJKT_ALARM (23447542):在类路径中添加了“/opt/app/abcdef/rt_dev/lib/antlr-2.7.2.jar”。

调试 [South Agent 1] (ConstructorResolver.java:195) -
忽略构造函数 [public abc.def.mdd.channel.corba.M2000AlarmChannel(
java.lang.String, 整数, java.lang.String, java.lang.String, com.citycorp.mdd.msg.MDDComponent ) 抛出 java.lang.Exception ]
豆“雅加达”:org.springframework.beans.factory.UnsatisfiedDependencyException:
创建文件 [/opt/app/abcdef/rt_dev/etc/HUAWEI_M2KJKT_ALARM.xml] 中定义的名称为“Jakarta”的 bean 时出错:
通过类型为 [java.lang.String] 的索引 0 的构造函数参数表示的不满足依赖关系:
无法将 [abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000] 类型的构造函数参数值转换为所需类型 [java.lang.String]:
将[abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000]类型的值转换为所需类型[java.lang.String]失败;
嵌套异常是 java.lang.IllegalArgumentException: 无法将类型 [abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000] 的值转换为所需类型 [java.lang.String]:
未找到匹配的编辑器或转换策略


以下是 abc.def.mdd.channel.corba.M2000AlarmChannel 类的构造函数的签名:

公共 M2000AlarmChannel(MDDComponent 组件)

public M2000AlarmChannel(String host, int port, String username, String password, MDDComponent componenet)

abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000类实现了MDDComponent(bean MDDStructurPackager)。



如您所见,Spring 正在实例化一些 bean,即。 MDDStructurPackager 没有问题。

问题在于实例化 'Jakarta' bean。

线条的含义:

在文件 [/opt/app/abcdef/rt_dev/etc/HUAWEI_M2KJKT_ALARM.xml] 中定义名称为“Jakarta”的 bean 创建错误:

通过类型为 [java.lang.String] 的索引 0 的构造函数参数表示的不满足依赖关系:

无法将类型 [abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000] 的构造函数参数值转换为所需类型 [java.lang.String]:

不清楚。在尝试确定适当的构造函数时,我不确定类型混淆的原因。问题可能与接口 MDDComponent 的加载有关。如果它被加载了两次,由在类加载器层次结构中不相关的不同类加载器加载,您可以想象与确定正确的构造函数相关的问题。

感谢您的任何想法,我正在抓住稻草。

谢谢 布莱恩

【问题讨论】:

    标签: java spring classloader


    【解决方案1】:

    从错误消息中,听起来 Spring 正在尝试使用此构造函数实例化对象:

    public M2000AlarmChannel(String host, int port, String username, String password, MDDComponent componenet)
    

    看起来 Spring 对使用哪个构造函数感到困惑,可能是因为两个构造函数都有一个 MDDComponent 参数(在某种程度上,两个构造函数都将这个作为最后一个参数,我很好奇这是否与Spring 在确定使用哪个构造函数时使用的逻辑。无论如何...)。

    根据Spring manual,有一些参数可以添加到&lt;constructor-arg&gt;元素中,帮助容器解析使用哪个构造函数:

    您可以添加“类型”:

    <bean id="exampleBean" class="examples.ExampleBean">
      <constructor-arg type="int" value="7500000"/>
      <constructor-arg type="java.lang.String" value="42"/>
    </bean>
    

    或者你可以添加一个索引:

    <bean id="exampleBean" class="examples.ExampleBean">
      <constructor-arg index="0" value="7500000"/>
      <constructor-arg index="1" value="42"/>
    </bean>
    

    (如果你真的想,你可以同时指定两者)

    添加其中一个或两个应该有助于 Spring 确定要使用的构造函数。

    作为推论,如果这没有帮助,您是否可以简单地更改 XML 定义以传递另一个构造函数所需的参数,即 Spring 正在尝试使用的构造函数?

    顺便说一句,在 WMD 中使用引号标签可能会使您的问题更具可读性。

    【讨论】:

    • 对不起,我忘了说应用程序可以独立运行,即。不在容器内。
    • 您的意思是像使用 Spring 一样“独立”,而不是您自制的应用程序容器?
    • 那么您可能需要检查类路径中是否存在不同版本的 abc.def.mdd.channel.corba.M2000AlarmChannel
    • 您是否尝试过隔离此应用,例如在您的容器中自行运行它?如果您在同一个 JVM 中运行其他应用程序,请关闭所有应用程序并自行尝试此应用程序。
    • 是的,我们只在容器中运行一个应用程序。
    【解决方案2】:

    实际上,我认为是的。您传递给 Jakarta 对象的第一个构造函数参数的类型为 abc.def.mdd.msg.alarm.huawei.MDDPackagerAlarmM2000,但 ctor 需要一个字符串。使类型正确匹配。

    它是一条直截了当的消息。我错过了什么?

    我将从包结构中假设这些是您的类。是吗?

    【讨论】:

    • 但是他的论点do 不匹配构造函数吗?只是Spring似乎不这么认为?
    • 我一定累了。对不起,我错过了。
    • 问题的另一部分当然是它实际考虑的是哪个构造函数。根据日志,它决定忽略其中一个,因为它应该。为什么它认为它需要一个 String 参数?还是字符串要求是选择错误构造函数的产物?
    【解决方案3】:

    您可以控制AlarmChannel 类吗?如果是这样,您能否推出一个在构造函数中包含打印语句的新版本(您也可以在 static 初始化程序中使用 try-catch 从初始化类的位置获取堆栈跟踪)?

    这样,您可能会了解是否涉及重复的类

    此外,在 Spring 配置文件中,您可以使用 MethodInvokingFactoryBean 打印出 ClassLoader - 这也可能对此事有所帮助?

    【讨论】:

    • 不幸的是,我控制的是容器,而不是正在执行的软件。我能够获得构造函数签名和 Spring 片段,但我将无法访问/修改任何正在执行的代码。
    • 你能继承AlarmChannel并添加信息,还是最终的?
    • 我无法直接访问容器中运行的软件。我所做的任何事情都必须在容器管理器中完成。
    • 我有点困惑;对不起。那你怎么知道 AlarmChannel 的构造函数签名是什么样的呢?你能调试 Spring 容器,它实际上是在使用类名并试图找到构造函数吗?或者使用适当的日志语句滚动您自己的应用上下文?
    • 我向供应商询问了构造函数签名,但我无法获得完整的源代码。我正在尝试构建一个环境来复制(至少在逻辑上)环境。
    猜你喜欢
    • 2020-11-06
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多