无法使用“工厂方法”实例化单例

Posted

技术标签:

【中文标题】无法使用“工厂方法”实例化单例【英文标题】:can't instantiate singleton with `factory-method` 【发布时间】:2012-12-22 10:40:14 【问题描述】:

我有一段代码集成了 Hibernate 3.3.2.GA 和 Spring 2.5.x,而这些版本很旧,所以我决定升级。我首先将 Spring 升级到了最新的可用版本 3.2.0.RELEASE,这很有效。实际上,可以使用svn co https://perfectjpattern.svn.sourceforge.net/svnroot/perfectjpattern/trunk perfectjpattern 签出具有最新 Spring 版本的工作项目。在尝试升级到 Hibernate 4 时,我得到了一个不同的 Spring bean 行为,这让我无法理解,它显然与 Hibernate 4 无关。

失败的 bean 配置如下。使用 Hibernate 3 的完整工作配置是 here(我还没有检查使用 Hibernate 4 ofc 失败的配置):

<bean id="localDaoFactory" class="org.perfectjpattern.jee.integration.dao.LocalDaoFactory"  
    factory-method="getInstance">
    <property name="sessionStrategy">
        <ref bean="daoSessionStrategy" />
    </property>     
    <property name="transactionStrategy">
        <ref bean="daoTransactionStrategy" />
    </property>         
    <property name="personDao">
        <ref bean="personDao" />
    </property>     
</bean>

并且异常的堆栈跟踪如下所示,这意味着它无法初始化org.perfectjpattern.jee.integration.dao.LocalDaoFactory,显然它在寻找可访问的构造函数时失败但在此配置中我指定使用工厂方法而不是直接实例化bean。这在升级 Hibernate 之前有效,但在同一 Spring(最新)版本之后和之后都无效,这非常令人困惑。

java.lang.IllegalStateException: Failed to load ApplicationContext
        at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
        at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:313)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
        at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
        at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
        at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:53)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
        at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
        at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'localDaoFactory' defined in URL [file:src/test/resources/test-applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.perfectjpattern.jee.integration.dao.LocalDaoFactory
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:581)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1029)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:925)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:490)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:607)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
        at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:106)
        at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:57)
        at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
        at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:248)
        at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
        at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
        ... 30 more
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.perfectjpattern.jee.integration.dao.LocalDaoFactory
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:160)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:570)
        ... 47 more

更新:在加载 Spring 配置时,Spring 找不到 LocalDaoFactory 类,但它之前确实...

更新:这是运行mvn dependency:tree -Dverbose 的结果,并且没有未解决的冲突

[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ perfectjpattern-spring ---
[INFO] perfectjpattern:perfectjpattern-spring:jar:1.0.3-SNAPSHOT
[INFO] +- perfectjpattern:perfectjpattern-api:jar:1.0.3-SNAPSHOT:compile
[INFO] +- commons-lang:commons-lang:jar:2.5:compile
[INFO] +- org.springframework:spring-core:jar:3.2.0.RELEASE:compile
[INFO] +- org.springframework:spring-jdbc:jar:3.2.0.RELEASE:compile
[INFO] |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-tx:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] +- org.springframework:spring-orm:jar:3.2.0.RELEASE:compile
[INFO] |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-jdbc:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-tx:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- aopalliance:aopalliance:jar:1.0:compile
[INFO] |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] +- org.springframework:spring-tx:jar:3.2.0.RELEASE:compile
[INFO] |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (aopalliance:aopalliance:jar:1.0:compile - omitted for duplicate)
[INFO] |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] +- org.springframework:spring-aop:jar:3.2.0.RELEASE:compile
[INFO] |  +- (aopalliance:aopalliance:jar:1.0:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] +- org.springframework:spring-context:jar:3.2.0.RELEASE:compile
[INFO] |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-aop:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- org.springframework:spring-expression:jar:3.2.0.RELEASE:compile
[INFO] |  |  \- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] +- org.springframework:spring-context-support:jar:3.2.0.RELEASE:compile
[INFO] |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-context:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] +- org.springframework:spring-beans:jar:3.2.0.RELEASE:compile
[INFO] |  \- (org.springframework:spring-core:jar:3.2.0.RELEASE:compile - omitted for duplicate)
[INFO] +- commons-logging:commons-logging:jar:1.1.1:test
[INFO] +- org.springframework:spring-test:jar:3.2.0.RELEASE:test
[INFO] |  +- org.springframework:spring-webmvc:jar:3.2.0.RELEASE:test
[INFO] |  |  +- (org.springframework:spring-context:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  |  +- org.springframework:spring-web:jar:3.2.0.RELEASE:test
[INFO] |  |  |  +- (org.springframework:spring-context:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  |  |  +- (org.springframework:spring-core:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  |  |  +- (org.springframework:spring-aop:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  |  |  +- (aopalliance:aopalliance:jar:1.0:test - omitted for duplicate)
[INFO] |  |  |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  |  +- (org.springframework:spring-expression:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  |  \- (org.springframework:spring-beans:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] |  \- (org.springframework:spring-core:jar:3.2.0.RELEASE:test - omitted for duplicate)
[INFO] +- perfectjpattern:perfectjpattern-testcommon:jar:1.0.3-SNAPSHOT:test
[INFO] |  +- junit:junit:jar:4.8.1:test
[INFO] |  +- org.slf4j:slf4j-log4j12:jar:1.7.2:test
[INFO] |  |  +- org.slf4j:slf4j-api:jar:1.7.2:test
[INFO] |  |  \- log4j:log4j:jar:1.2.17:test
[INFO] |  \- org.easymock:easymock:jar:2.5.2:test
[INFO] +- org.hsqldb:hsqldb:jar:2.2.9:test
[INFO] \- perfectjpattern:perfectjpattern-hibernate:jar:1.0.3-SNAPSHOT:test
[INFO]    +- perfectjpattern:perfectjpattern-jee:jar:1.0.3-SNAPSHOT:test
[INFO]    |  +- perfectjpattern:perfectjpattern-core:jar:1.0.3-SNAPSHOT:test
[INFO]    |  |  +- (perfectjpattern:perfectjpattern-api:jar:1.0.3-SNAPSHOT:test - omitted for duplicate)
[INFO]    |  |  +- (org.slf4j:slf4j-api:jar:1.7.2:test - omitted for duplicate)
[INFO]    |  |  \- (commons-lang:commons-lang:jar:2.5:test - omitted for duplicate)
[INFO]    |  +- org.apache.geronimo.specs:geronimo-ejb_3.0_spec:jar:1.0.1:test
[INFO]    |  +- org.apache.geronimo.specs:geronimo-jpa_3.0_spec:jar:1.1.1:test
[INFO]    |  \- org.apache.geronimo.specs:geronimo-jta_1.1_spec:jar:1.1.1:test
[INFO]    +- javassist:javassist:jar:3.8.0.GA:test
[INFO]    +- commons-collections:commons-collections:jar:3.2.1:test
[INFO]    \- org.hibernate:hibernate-core:jar:4.1.9.Final:test
[INFO]       +- antlr:antlr:jar:2.7.7:test
[INFO]       +- org.jboss.logging:jboss-logging:jar:3.1.0.GA:test
[INFO]       +- org.javassist:javassist:jar:3.17.1-GA:test
[INFO]       +- org.jboss.spec.javax.transaction:jboss-transaction-api_1.1_spec:jar:1.0.0.Final:test
[INFO]       +- dom4j:dom4j:jar:1.6.1:test
[INFO]       +- org.hibernate.javax.persistence:hibernate-jpa-2.0-api:jar:1.0.1.Final:test
[INFO]       \- org.hibernate.common:hibernate-commons-annotations:jar:4.0.1.Final:test
[INFO]          \- (org.jboss.logging:jboss-logging:jar:3.1.0.CR2:test - omitted for conflict with 3.1.0.GA)

【问题讨论】:

请检查您的 LocalDaoFactory 在其静态初始化程序中没有做任何事情。 【参考方案1】:

消息Could not initialize class org.perfectjpattern.jee.integration.dao.LocalDaoFactory表示JVM已经无法静态初始化这个名字的类。

我查看了source of this class 及其超类HibernateDaoFactory 和AbstractDaoFactory。 LocalDaoFactory 和 HibernateDaoFactory 完成的唯一静态初始化是创建每个类的单个实例并将其存储在名为 INSTANCEprivate static final 字段中。所以,实例化这些单例肯定有问题。

LocalDaoFactory 没有自己的构造函数,AbstractDaoFactory 的构造函数只使用标准 Java 类和与它在同一个包中的类。 HibernateDaoFactory 的构造函数更有趣一点,它创建了一个HibernateCurrentSessionStrategy 和一个HibernateConfiguredTransactionStrategy。代码本身看起来并没有什么特别的问题,因此该代码尝试实例化的某些类似乎丢失了。

我可以在 PerfectJPattern 之外的代码中看到的唯一依赖项是:

休眠核心, Apache Commons Lang.

我将假设您确实拥有相关的 Hibernate 核心 JAR,因此将责任归咎于 Apache Commons Lang。

Apache Commons Lang website 提到 commons-lang 版本 3 使用与早期版本的 commons-lang 不同的包。鉴于您的项目在升级到 Hibernate 4 之前工作并且现在不工作,我猜测升级到 Hibernate 4 会用 commons-lang 3 或更高版本替换 commons-lang 2.6 或更早版本,问题出现是因为 PerfectJPattern 有对 commons-lang 2 的依赖。

编辑:看起来您的项目确实包含 commons-lang v2.5,因此缺少它似乎不是问题。带有消息 Could not initialize class ...NoClassDefFoundError 表示 JVM 已尝试加载类至少两次失败 - 如果您可以在第一次失败时获取异常消息,那可能会帮助你更多。恐怕我真正能建议的只是在已设置为中断异常的调试器下启动您的项目。

【讨论】:

谢谢。我目前正在调查,但看起来这不是问题所在。在主干中,我为所有依赖项和公共集合全局定义版本,我使用与休眠相同的 3.x。 @GiovanniAzua:我不确定我是否理解您的评论。首先,这个问题与 commons-lang 相关,而不是 commons-collections。其次,我可以看到您的代码依赖于 commons-lang v2,因为它导入了org.apache.commons.lang.*;在 commons-lang v3 中,包变成了org.apache.commons.lang3.*。我刚刚下载了 commons-lang v3 JAR 并验证它不包含任何 org.apache.commons.lang.* 类。 它适用于使用 Maven 创建的 Eclipse IDE 项目,同时从命令行运行它会产生相同的问题 ... @GiovanniAzua:那么,尝试使用命令行调试器,例如jdb。我认为jdb中的catch命令可能是你需要使用的。 OK 解决了。这个问题与有几个非抽象基类有关,它们会贪婪地初始化单例实例,我转向惰性单例初始化,现在可以工作了。基本上,当 HibernateDaoFactory 试图获取纯 Hibernate 配置时,我是在 Spring 中设置 dataSource 和 SessionFactory。

以上是关于无法使用“工厂方法”实例化单例的主要内容,如果未能解决你的问题,请参考以下文章

单例模式和工厂方法模式

设计模式:对象生成(单例工厂抽象工厂)

面向对象编程模式之“简单工厂和单例“

第四十篇 Python之设计模式总结-简单工厂工厂方法抽象工厂单例模式

创建型设计模式总结

Java设计模式图文代码案例详解Java五大创建者模式 建造者原型(抽象)工厂单例模式