Spring FactoryBean

时间:2013-09-13 12:42:34

标签: spring factory spring-bean

我试图抓住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()

1 个答案:

答案 0 :(得分: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发现digester bean时,它会尝试获取它的依赖关系。 Spring看到依赖项为FactoryBean,因此最终会调用FactoryBeanRegistrySupport.getObjectFromFactoryBean。此方法首先检查bean是否是单例,调用 MessageDigestFactoryBean.isSingleton() 。如果bean是单例,它首先尝试从工厂bean对象缓存中获取对它的引用。由于这是第一次引用此bean,它尚未缓存,因此通过 MessageDigestFactoryBean.getObject() 获取引用,缓存然后返回。由于digester中引用了两个工厂bean,显然每个工艺都会重复此过程。
  3. 初始化完成后,Spring尝试将ContextRefreshedEvent发布到生命周期处理器,因此它会搜索所有bean定义以查找实现Lifecycle接口的实例。基本上Spring循环遍历在上下文中定义的所有bean,检查匹配类型的singleton bean(实际上有更多的检查,但我们只对这些检查感兴趣)。在此过程中,对于工厂bean,调用 MessageDigestFactoryBean.isSingleton() 来确定对象是否为单例,并调用 MessageDigestFactoryBean.getObjectType() 来检查对象的类型是否为可从Lifecycle接口分配。同样,由于您有两个MessageDigestFactoryBean实例,因此每个方法都被调用两次。
  4. 当你拨打ctx.refresh()时会发生这种情况。这只是顶部的外观,显然更多的是由引擎盖完成的,但这就是我能看到的与你的输出有关的东西。希望这能回答你的第一个问题。

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