【问题标题】:How to get beans created by FactoryBean spring managed?如何让 FactoryBean spring 创建的 bean 得到管理?
【发布时间】:2011-02-11 14:44:19
【问题描述】:

FactoryBean 可用于以编程方式创建可能需要复杂实例化逻辑的对象。

但是,FactoryBean 创建的 bean 似乎没有成为 Spring 管理的。这种解释正确吗?如果是这样,有什么好的解决方法吗?包含一个简短的代码示例来说明我的问题。

应用程序上下文:

<bean id="searcher" class="some.package.SearcherFactory" /> 
<bean id="service" class="some.package.Service" /> 

工厂实现:

public class SearcherFactory implements FactoryBean<Searcher> {

    @Override
    public Searcher getObject() throws Exception {
        return new Searcher(); // not so complex after all ;)
    }

    @Override
    public Class<Searcher> getObjectType() {
        return Searcher.class;
    }
    .... 
}

工厂创建的类:

public class Searcher() {
      private Service service;

      @Autowired
      public void setService(Service service) {
           // never invoked
           this.service=service;
      } 
}

【问题讨论】:

    标签: java spring autowired


    【解决方案1】:

    这是一个抽象的 FactoryBean 实现,它为您自动装配:

    public abstract class AbstractAutowiringFactoryBean<T> extends
        AbstractFactoryBean<T> implements ApplicationContextAware{
    
        private ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(
            final ApplicationContext applicationContext){
            this.applicationContext = applicationContext;
        }
    
        @Override
        protected final T createInstance() throws Exception{
            final T instance = doCreateInstance();
            if(instance != null){
                applicationContext
                  .getAutowireCapableBeanFactory()
                  .autowireBean(instance);
            }
            return instance;
        }
    
        /**
         * Create the bean instance.
         * 
         * @see #createInstance()
         */
        protected abstract T doCreateInstance();
    
    }
    

    扩展它,实现getObjectType()doCreateInstance() 方法,你就可以开始使用自动装配了。

    注意:没有应用 BeanPostProcessors,这需要额外的代码。

    【讨论】:

    • 这也不是一个坏主意,提高了可重用性。虽然我想知道为什么我们的朋友在 springsource 上没有将它作为模板提供。
    【解决方案2】:

    FactoryBean 创建的对象由 Spring 管理,但不是由 Spring 实例化或配置的。通过使用FactoryBean,您自己对此负责。所有的注入和配置都必须由FactoryBean处理

    还有一种可能更适合您的替代方法 - 使用 annotation-based config instead of XML-based config。这意味着您可以在 Java 中拥有复杂的实例化逻辑,同时仍然在对象本身上使用 @Autowired 之类的东西。

    我现在倾向于为所有非平凡的 Spring 应用程序使用注解样式的配置,它使许多事情变得容易得多。

    【讨论】:

    • +1。我刚刚开始使用基于注释的配置来处理这些类型的场景。
    • 那么,您将如何解决作者的问题?你能用适当的 java 配置更新答案吗?
    【解决方案3】:

    这个呢?

    <bean id="serviceFactory"
          class="some.package.SearcherFactory" />
    
    
    <bean id="service"
          factory-bean="serviceFactory"
          factory-method="getObject"/>
    

    ...然后只注入 bean '服务',而不关心代码中的工厂

    【讨论】:

    • 试过这个,导致堆栈跟踪存在问题,即 getObject() 方法不存在。根据此链接:goo.gl/V6NyLv 将调用 service.getObject().getObject()。
    • 评论者并不意味着按原样复制上述内容。在这种情况下,您的 SearchFactory 类型需要实现像 getObject() 这样的方法,该方法将返回所需类型的对象,或者您可以随意命名该方法。
    【解决方案4】:

    手动方式是:

    1. 在工厂 bean 中注入依赖项
    2. 在目标对象上手动设置它们。

    你也可以在工厂bean中注入ApplicationContext(或者通过实现ApplicationContextAware得到它),然后做ctx.getAutowireCapableBeanFactory().autowireBean(bean)

    不过,我承认两人都觉得很奇怪。

    但实际上,如果逻辑这么简单(仅实例化),请使用prototype 作用域。

    【讨论】:

    • 它可以工作,并且至少可以将弹簧联轴器简化为单个工厂类。
    【解决方案5】:

    FactoryBean 是您作为开发人员在编写工厂类时实现的接口,并且您希望工厂类创建的对象由 Spring 作为 bean 进行管理,而 BeanFactory 则代表 Spring IoC 容器,它包含托管 bean 并提供检索它们的访问权限。它是实现控制反转容器的基本功能的框架核心的一部分。

    在大多数情况下,您不会发现自己直接使用或实现 BeanFactory 接口,除非您正在扩展框架的核心功能。当您拥有由工厂创建的需要由 Spring 管理的对象时,您确实会实现 FactoryBean。

    更简洁地说,BeanFactory 代表 Spring 容器,而 FactoryBean 代表工厂类,其创建的对象被拾取并注册为容器中的 bean。

    File: context.xml
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
                    http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="sha" class="MessageDigestFactoryBean">
            <property name="algorithm" value="SHA1"/>
        </bean>
    
        <bean id="md5" class="MessageDigestFactoryBean"/>
    
    </beans>
    
    
    File: Main.java
    
    import java.security.MessageDigest;
    
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    public class Main {
      public static void main(String[] args) {
        XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("context.xml"));
        String d1 = (String) factory.getBean("sha");
        String d2 = (String) factory.getBean("md5");
        System.out.println(d1);
        System.out.println(d2);
      }
    
    }
    
    class MessageDigestFactoryBean implements FactoryBean, InitializingBean {
      private static final String DEFAULT_ALGORITHM = "MD5";
    
      private String algorithm = DEFAULT_ALGORITHM;
    
      public Object getObject() throws Exception {
        return this.algorithm;
      }
    
      public Class getObjectType() {
        return MessageDigest.class;
      }
    
      public boolean isSingleton() {
        return true;
      }
    
      public void setAlgorithm(String algorithm) {
        this.algorithm = algorithm;
      }
    
      public void afterPropertiesSet() throws Exception {
        this.algorithm += " after setting";
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多