【问题标题】:FactoryBeans and the annotation-based configuration in Spring 3.0Spring 3.0 中的 FactoryBeans 和基于注解的配置
【发布时间】:2011-07-29 07:19:47
【问题描述】:

Spring 提供了FactoryBean 接口以允许对bean 进行非平凡的初始化。该框架提供了许多工厂 bean 的实现,并且——当使用 Spring 的 XML 配置时——工厂 bean 很容易使用。

但是,在 Spring 3.0 中,我找不到一种令人满意的方式来使用工厂 bean 和基于注释的配置(née JavaConfig)。

显然,我可以手动实例化工厂 bean 并自己设置任何所需的属性,如下所示:

@Configuration
public class AppConfig {

...

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource());
        factory.setAnotherProperty(anotherProperty());

        return factory.getObject();
    }

但是,如果 FactoryBean 实现了任何 Spring 特定的回调接口,例如 InitializingBeanApplicationContextAwareBeanClassLoaderAware@PostConstruct,这将失败。我还需要检查FactoryBean,找出它实现了哪些回调接口,然后通过调用setApplicationContextafterPropertiesSet()等自己实现这个功能。

这让我感觉很尴尬和背对前:应用程序开发人员不应该实现 IOC 容器的回调。

有谁知道从 Spring Annotation 配置中使用 FactoryBeans 的更好解决方案?

【问题讨论】:

    标签: java spring inversion-of-control spring-annotations


    【解决方案1】:

    我认为当您依赖自动布线时,这个问题得到了最好的解决。如果您对 bean 使用 Java 配置,则如下:

    @Bean
    MyFactoryBean myFactory()
    { 
        // this is a spring FactoryBean for MyBean
        // i.e. something that implements FactoryBean<MyBean>
        return new MyFactoryBean();
    }
    
    @Bean
    MyOtherBean myOther(final MyBean myBean)
    {
        return new MyOtherBean(myBean);
    }
    

    因此 Spring 将为我们注入由 myFactory().getObject() 返回的 MyBean 实例,就像它对 XML 配置所做的那样。

    如果您在@Component/@Service 等类中使用@Inject/@Autowire,这也应该有效。

    【讨论】:

    • “MyBean”实例是否每次我们在某个地方注入它时都相同,或者每个注入工厂都会创建新实例?
    • @KonstantinZyubin 这取决于你如何实现isSingleton 方法。
    【解决方案2】:

    据我了解,您的问题是您希望 sqlSessionFactory() 的结果为 SqlSessionFactory(用于其他方法),但您必须从 @Bean-annotated 方法返回 SqlSessionFactoryBean为了触发 Spring 回调。

    可以通过以下解决方法解决:

    @Configuration 
    public class AppConfig { 
        @Bean(name = "sqlSessionFactory")
        public SqlSessionFactoryBean sqlSessionFactoryBean() { ... }
    
        // FactoryBean is hidden behind this method
        public SqlSessionFactory sqlSessionFactory() {
            try {
                return sqlSessionFactoryBean().getObject();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    
        @Bean
        public AnotherBean anotherBean() {
            return new AnotherBean(sqlSessionFactory());
        }
    }
    

    关键是对@Bean-annotated 方法的调用被执行返回的bean 的初始化的方面拦截(在您的情况下为FactoryBean),因此在sqlSessionFactory() 中对sqlSessionFactoryBean() 的调用返回一个完全初始化FactoryBean

    【讨论】:

    • 但我不明白使用FactoryBean 的利润在哪里。因为在这种情况下,我们可以简单地使用静态工厂创建常规 bean,它将返回一个 bean,结果将是相同的。
    【解决方案3】:

    Spring JavaConfig 有一个 ConfigurationSupport 类,它有一个用于 FactoryBean 的 getObject() 方法。

    你会用它来扩展

    @Configuration
    public class MyConfig extends ConfigurationSupport {
    
        @Bean
        public MyBean getMyBean() {
           MyFactoryBean factory = new MyFactoryBean();
           return (MyBean) getObject(factory);
        }
    }
    

    这个jira issue有一些背景

    在 Spring 3.0 中,JavaConfig 被移入 Spring 核心,并决定摆脱 ConfigurationSupport 类。建议的方法是现在使用构建器模式而不是工厂。

    取自新SessionFactoryBuilder的示例

    @Configuration
    public class DataConfig {
        @Bean
        public SessionFactory sessionFactory() {
            return new SessionFactoryBean()
               .setDataSource(dataSource())
               .setMappingLocations("classpath:com/myco/*.hbm.xml"})
               .buildSessionFactory();
        }
    }
    

    一些背景here

    【讨论】:

      【解决方案4】:

      这就是我正在做的,而且它有效:

      @Bean
      @ConfigurationProperties("dataSource")
      public DataSource dataSource() { // Automatically configured from a properties file
          return new BasicDataSource();
      }
      
      @Bean
      public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
          SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
          factory.setDataSource(dataSource); // Invoking dataSource() would get a new instance which won't be initialized
          factory.setAnotherProperty(anotherProperty());
          return factory;
      }
      
      
      @Bean
      public AnotherBean anotherBean(SqlSessionFactory sqlSessionFactory) { // This method receives the SqlSessionFactory created by the factory above
          return new AnotherBean(sqlSessionFactory);
      }
      

      你声明的任何bean都可以作为参数传递给任何其他@Bean方法(再次调用相同的方法将创建一个不被spring处理的新实例)。 如果您声明一个 FactoryBean,您可以使用它创建的 bean 类型作为另一个 @Bean 方法的参数,并且它将接收正确的实例。 你也可以使用

      @Autowired
      private SqlSessionFactory sqlSessionFactory;
      

      任何地方,它也可以工作。

      【讨论】:

      • 您想在每次启动 sqlSessionFactory 时创建一个新的 SQl 会话实例吗?
      【解决方案5】:

      为什么不在 AppConfiguration 中注入 Factory?

      @Configuration
      public class AppConfig {
      
          @Resource
          private SqlSessionFactoryBean factory;
      
          @Bean 
          public SqlSessionFactory sqlSessionFactory() throws Exception {
             return factory.getObjectfactory();    
          }    
      }
      

      但是我可能没有正确理解你的问题。因为在我看来你正在尝试一些奇怪的东西——退后一步,重新考虑你真正需要的是什么。

      【讨论】:

      • Ralph,感谢您的回复,但它不起作用,因为 FactoryBean(在本例中为 SqlSessionFactoryBean,但它可能是实现org.springframework.beans.factory.FactoryBean任何东西 -例如org.springframework.orm.hibernate3.LocalSessionFactoryBean) 不受我的控制(或在我的源代码中),并且没有使用@Value/@Resource/@Autowired/@Inject 注释进行注释。因此,在我调用 .getObject() 之前,Spring 不会正确地注入它的依赖项。
      • @Andrew Newdigate -- 工厂本身是 XML 配置的候选者,或者如果您不喜欢 XML,则在其他 @Bean 注释方法中创建它。 (然后你可以使用@Value)
      【解决方案6】:

      这是我的做法:

      @Bean
      def sessionFactoryBean: AnnotationSessionFactoryBean = {
        val sfb = new AnnotationSessionFactoryBean
      
        sfb.setDataSource(dataSource)
        sfb.setPackagesToScan(Array("com.foo.domain"))
      
        // Other configuration of session factory bean
        // ...
      
        return sfb
      }
      
      @Bean
      def sessionFactory: SessionFactory = {
         return sessionFactoryBean.getObject
      }
      

      sessionFactoryBean 被创建,并发生了适当的创建后生命周期内容(afterPropertiesSet 等)。

      请注意,我没有直接将 sessionFactoryBean 引用为 bean。我将 sessionFactory 自动装配到我的其他 bean 中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-02-13
        • 1970-01-01
        • 1970-01-01
        • 2012-05-25
        • 2014-07-08
        • 2014-09-02
        • 2016-11-03
        • 2012-07-18
        相关资源
        最近更新 更多