【问题标题】:Spring FactoryBean春天工厂豆
【发布时间】:2013-09-13 12:42:34
【问题描述】:

我正在尝试掌握 Spring 的 FactoryBean,但我遇到了问题。您能否在下面查看我的来源并回答。这是我的应用上下文:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:annotation-config/>

    <bean id="SHADigest" class="com.dtoryanik.spring.factorybean.MessageDigestFactoryBean">
        <property name="algorithmName">
            <value>SHA1</value>
        </property>
    </bean>

    <bean id="defaultDigest" class="com.dtoryanik.spring.factorybean.MessageDigestFactoryBean"/>

    <bean id="digester" class="com.dtoryanik.spring.factorybean.MessageDigester">
        <property name="messageDigest1">
            <ref local="SHADigest"/>
        </property>
        <property name="messageDigest2">
            <ref local="defaultDigest"/>
        </property>
    </bean>
</beans>

它实际上是一个工厂 bean:

public class MessageDigestFactoryBean implements FactoryBean<MessageDigest>{

    private String algorithmName = "MD5";
    private MessageDigest messageDigest = null;

    @Override
    public MessageDigest getObject() throws Exception {
        System.out.println("<> MessageDigestFactoryBean.getObject()");
        return messageDigest;
    }

    @Override
    public Class<?> getObjectType() {
        System.out.println("<> MessageDigestFactoryBean.getObjectType()");
        return MessageDigest.class;
    }

    @Override
    public boolean isSingleton() {
        System.out.println("<> MessageDigestFactoryBean.isSingleton()");
        return true;
    }

    @PostConstruct
    public void postConstructHandler() throws NoSuchAlgorithmException {
        System.out.println("<> MessageDigestFactoryBean.postConstructHandler()");
        messageDigest = MessageDigest.getInstance(algorithmName);
    }

    public void setAlgorithmName(String algorithmName) {
        this.algorithmName = algorithmName;
    }
}

还有另一个类——MessageDigester,但它对主题没有任何帮助。我有一个主方法类:

public class MessageDigestDemo {

public static void main(String[] args) {
    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
    ctx.load("classpath:app-ctx.xml");
    ctx.refresh();

    MessageDigester messageDigester = (MessageDigester) ctx.getBean("digester");
    messageDigester.digest("Hello World!");
}

}

问题出在我的输出中。看来我有一个双重实例化。方法 isSingleton(), getObject() 为每个 bean 调用两次(尽管我只从工厂检索 2 个实例)。为什么会发生?也许我做错了什么?

<> MessageDigestFactoryBean.postConstructHandler()
<> MessageDigestFactoryBean.postConstructHandler()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObject()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObject()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObjectType()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObjectType()

【问题讨论】:

标签: spring factory spring-bean


【解决方案1】:

我已修改您的 MessageDigestFactoryBean 类,使其也输出 algorithmName,这将有助于清除此案。更改后,输出为:

<> MessageDigestFactoryBean.postConstructHandler() SHA1
<> MessageDigestFactoryBean.postConstructHandler() MD5
<> MessageDigestFactoryBean.isSingleton() SHA1
<> MessageDigestFactoryBean.getObject() SHA1
<> MessageDigestFactoryBean.isSingleton() MD5
<> MessageDigestFactoryBean.getObject() MD5
<> MessageDigestFactoryBean.isSingleton() SHA1
<> MessageDigestFactoryBean.getObjectType() SHA1
<> MessageDigestFactoryBean.isSingleton() MD5
<> MessageDigestFactoryBean.getObjectType() MD5

让我们尝试分析这个输出。

  1. 您已经声明了两个 MessageDigestFactoryBean 实例,因此当 Spring 在上下文中发现它们时,它会对其进行初始化,并在进程中调用带有 @PostConstruct - MessageDigestFactoryBean.postConstructHandler() 注释的方法。
  2. 然后当 Spring 发现 digester bean 时,它会尝试获取它的依赖项。 Spring 看到依赖是FactoryBean,所以它最终调用FactoryBeanRegistrySupport.getObjectFromFactoryBean。此方法首先检查 bean 是否为单例,调用 MessageDigestFactoryBean.isSingleton()。如果 bean 是单例的,它首先尝试从工厂 bean 对象缓存中获取对它的引用。由于是第一次引用这个bean,所以还没有缓存,所以通过MessageDigestFactoryBean.getObject()获取引用,缓存后返回。由于您在 digester 中引用了两个工厂 bean,显然这个过程对每个都重复。
  3. 初始化完成后,Spring 尝试将ContextRefreshedEvent 发布到生命周期处理器,因此它会在所有bean 定义中搜索实现Lifecycle 接口的实例。基本上 Spring 循环遍历上下文中定义的所有 bean,检查匹配类型的单例 bean(实际上还有更多检查,但我们只对这些感兴趣)。在此过程中,工厂bean调用MessageDigestFactoryBean.isSingleton()判断对象是否为单例,调用MessageDigestFactoryBean.getObjectType()判断对象的类型是否可从@987654338赋值@ 界面。同样,由于您有两个 MessageDigestFactoryBean 实例,因此每个方法都会被调用两次。

当您致电ctx.refresh() 时会发生这种情况。这只是顶级外观,显然 Spring 在幕后完成了更多工作,但这就是我能看到的与您的输出相关的所有内容。希望这能回答您的第一个问题。

现在是第二个问题 - 不,你没有做错任何事,你看到的输出只是反映了 Spring 内部的功能。

【讨论】:

  • 非常感谢 Dmitry 的澄清!但是当我尝试评论ctx.refresh() 行时,我会在一个我想获得已经初始化的MessageDigest 的地方得到NullPointerException。实际上,它在 MessageDigester 类中,我没有在第一条消息中写到。
  • 发生这种情况是因为依赖关系实际上是在ctx.refresh() 中解决的。 ctx.load() 只加载 bean 定义。所以两个 messageDigest 实例都是空的,因此是 NPE。当使用带有默认构造函数的GenericXmlApplicationContext 时,您必须调用 refresh() 来获取正常运行的应用程序上下文,这反映在 javadoc 中。或者,您可以将"classpath:context.xml" 作为参数传递给构造函数,这样refresh 将被自动调用。
  • 事情马上就到位了!非常感谢您的解释。
  • 没问题 :) 另外,如果您对答案感到满意,请将其标记为已接受。干杯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-28
  • 1970-01-01
相关资源
最近更新 更多