【问题标题】:Spring @Autowiring with generic factory-built beansSpring @Autowireing 与通用工厂构建的 bean
【发布时间】:2011-06-18 07:03:09
【问题描述】:

我有一组具有复杂初始化方案的类。基本上,我从需要获取的接口开始,然后进行大量调用,最后得到一个实现该接口的对象。

为了处理这个问题,我创建了一个工厂类,它可以在给定接口的情况下生成最终对象。我把这个工厂变成了一个 bean,在 XML 中我指定我的各种服务 bean 通过这个工厂对象实例化,并带有它们将实现的接口的参数。

这很好用,我完全得到了我需要的豆子。不幸的是,我想从我的控制器类中访问它们,这些是通过组件扫描发现的。我在这里使用@Autowired,似乎Spring不知道这些是什么类型的对象,并且由于@Autowired按类型工作,所以我是SOL。

在这里使用@Resource(name="beanName") 会很完美,但是对某些bean 使用@Resource 而对其他bean 使用@Autowired 似乎很奇怪。

有没有办法让 Spring 知道工厂将为这些 bean 中的每一个创建什么接口,而无需为每种类型设置不同的工厂方法?

顺便说一下,我使用的是 Spring 2.5.6,否则我只会 JavaConfig 整个事情并忘记它。

工厂类:

<T extends Client> T buildService(Class<T> clientClass) {
  //Do lots of stuff with client class and return an object of clientClass.
}

应用上下文:

<bean id="serviceFactoryBean" class="com.captainAwesomePants.FancyFactory" />
<bean id="userService" factory-bean="serviceFactoryBean" factory-method="buildService">
   <constructor-arg value="com.captain.services.UserServiceInterface" />
</bean>
<bean id="scoreService" factory-bean="serviceFactoryBean" factory-method="buildService">
   <constructor-arg value="com.captain.services.ScoreServiceInterface" />
</bean>  

我的控制器:

public class HomepageController {

   //This doesn't work
   @Autowired @Qualifier("userService") UserServiceInterface userService;

   //This does
   @Resource(name="scoreService") ScoreServiceInterface scoreService;
}

【问题讨论】:

    标签: spring spring-mvc autowired


    【解决方案1】:

    我建议你将工厂模式更进一步,implement your factories as Spring FactoryBean classesFactoryBean 接口有一个 getObjectType() 方法,包含调用该方法以发现工厂将返回的类型。只要您的工厂返回一个合理的值,这就会让您的自动装配有所帮助。

    【讨论】:

    • 我喜欢。起初我不喜欢 FactoryBeans,但老实说,他们似乎会做我想让他们做的事。
    • @CaptainAwesomePants:我倾向于让我的FactoryBean 实现尽可能精简,让它们委托给另一个不使用 Spring API 的类。您也可以这样做 - 只需委托给您现有的 serviceFactoryBean
    • @skaffman:所以在这种情况下,FactoryBean 实现的数量应该与返回类型变化的数量一样多。如果向工厂 UserServiceInterface buildUserService() { return buildService(UserServiceInterface.class); }ScoreServiceInterface buildScoreService() { return buildService(ScoreServiceInterface.class); } 添加两个额外的方法(并且重新调整上下文以使用它们),Spring 会执行正确的类型发现吗?
    【解决方案2】:

    您应该能够使用:

    <bean id="myCreatedObjectBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetClass">
            <value>com.mycompany.MyFactoryClass</value>
        </property>
        <property name="targetMethod">
            <value>myFactoryMethod</value>
        </property>
    </bean>
    

    然后您可以使用@Resource 或@Autowired + @Qualifier 直接注入您的对象。

    【讨论】:

      【解决方案3】:

      我遇到了类似的问题,但对我来说,我想使用一个工厂来使用 JMockit(我需要使用的测试框架)创建我的自动连接依赖项的模拟实现。

      在网上找不到令人满意的解决方案后,我整理了一个对我来说非常有效的简单解决方案。

      我的解决方案也使用了 Spring FactoryBean,但它只使用一个工厂 bean 来创建我所有的 bean(最初的提问者似乎希望这样做)。

      我的解决方案是实现一个工厂工厂元工厂,为真实的单一工厂提供 FactoryBean 包装器。

      这是我的 JMockit 模拟 bean 工厂的 Java:

      public class MockBeanFactory<C> implements FactoryBean<C> {
      
          private Class<C> mockBeanType;
          protected MockBeanFactory(){}
      
          protected  <C> C create(Class<C> mockClass) {
              return Mockit.newEmptyProxy(mockClass);
          }
      
          @Override
          public C getObject() throws Exception {
              return create(mockBeanType);
          }
      
          @Override
          public Class<C> getObjectType() {
              return mockBeanType;
          }
      
          @Override
          public boolean isSingleton() {
              return true;
          }
      
          public static class MetaFactory {
              public <C> MockBeanFactory<C> createFactory(Class<C> mockBeanType) {
                  MockBeanFactory<C> factory = new MockBeanFactory<C>();
                  factory.mockBeanType = mockBeanType;
                  return factory;
              }
          }
      }
      

      然后在 Spring 上下文 XML 文件中,您可以简单地创建用于创建特定 bean 类型工厂的元工厂:

      <bean id="metaFactory" class="com.stackoverflow.MockBeanFactory$MetaFactory"/>
      
      <bean factory-bean="metaFactory" factory-method="createFactory">
          <constructor-arg name="mockBeanType" value="com.stackoverflow.YourService"/>
      </bean>
      

      为了使这项工作适用于原始提问者的情况,可以对其进行调整以使 FactoryBeans 成为 serviceFactoryBean 的包装器/适配器:

      public class FancyFactoryAdapter<C> implements FactoryBean<C> {
      
          private Class<C> clientClass;
          private FancyFactory serviceFactoryBean;
      
          protected FancyFactoryAdapter(){}
      
          @Override
          public C getObject() throws Exception {
              return serviceFactoryBean.buildService(clientClass);
          }
      
          @Override
          public Class<C> getObjectType() {
              return clientClass;
          }
      
          @Override
          public boolean isSingleton() {
              return true;
          }
      
          public static class MetaFactory {
      
              @Autowired FancyFactory serviceFactoryBean;
      
              public <C> FancyFactoryAdapter<C> createFactory(Class<C> clientClass) {
                  FancyFactoryAdapter<C> factory = new FancyFactoryAdapter<C>();
                  factory.clientClass = clientClass;
                  factory.serviceFactoryBean = serviceFactoryBean;
                  return factory;
              }
          }
      }
      

      然后在 XML 中(注意 userServiceFactory id 和 userService bean id 仅适用于 @Qualifier 注释):

      <bean id="metaFactory" class="com.stackoverflow.FancyFactoryAdapter$MetaFactory"/>
      
      <bean id="userServiceFactory" factory-bean="metaFactory" factory-method="createFactory">
          <constructor-arg name="clientClass" value="com.captain.services.UserServiceInterface"/>
      </bean>
      
      <bean id="userService" factory-bean="userServiceFactory"/>
      
      <bean id="scoreServiceFactory" factory-bean="metaFactory" factory-method="createFactory">
          <constructor-arg name="clientClass" value="com.captain.services.ScoreServiceInterface"/>
      </bean>
      
      <bean id="scoreService" factory-bean="scoreServiceFactory"/>
      

      就是这样,只需一个小的 Java 类和一点样板配置以及您的自定义 bean 工厂就可以创建所有 bean 并让 Spring 成功解析它们。

      【讨论】:

      • 我赞成这个主要是因为这是 Java,因此 FactoryFactory 对象总是正确的!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      • 2019-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      相关资源
      最近更新 更多