1

我正在尝试掌握 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()
4

1 回答 1

2

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

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

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

于 2013-09-16T09:01:11.087 回答