Spring 3.0中FactoryBeans和基于注解的配置

发布于 2024-10-30 06:03:27 字数 996 浏览 6 评论 0原文

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 特定的回调接口,例如 InitializingBean,这将会失败>、ApplicationContextAwareBeanClassLoaderAware@PostConstruct 例如。我还需要检查 FactoryBean,找出它实现的回调接口,然后通过调用 setApplicationContext、afterPropertiesSet() 等来自己实现此功能。

这感觉很尴尬和后退 -对我来说,应用程序开发人员不必实现 IOC 容器的回调。

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

Spring provides the FactoryBean interface to allow non-trivial initialisation of beans. The framework provides many implementations of factory beans and -- when using Spring's XML config -- factory beans are easy to use.

However, in Spring 3.0, I can't find a satisfactory way of using factory beans with the annotation-based configuration (née JavaConfig).

Obviously, I could manually instantiate the factory bean and set any required properties myself, like so:

@Configuration
public class AppConfig {

...

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

        return factory.getObject();
    }

However, this would fail if the FactoryBean implemented any Spring-specific callback interfaces, like InitializingBean, ApplicationContextAware, BeanClassLoaderAware, or @PostConstruct for example. I also need to inspect the FactoryBean, find out what callback interfaces it implements, then implement this functionality myself by calling setApplicationContext, afterPropertiesSet() etc.

This feels awkward and back-to-front to me: application-developers should not have to implement the callbacks of the IOC container.

Does anyone know of a better solution to using FactoryBeans from Spring Annotation configs?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

回忆那么伤 2024-11-06 06:03:27

我认为当您依靠自动装配时最好解决这个问题。如果您对 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 ,这也应该有效。

I think that this is best solved when you rely on auto-wiring. If you are using Java configuration for the beans, this would like:

@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);
}

So Spring will inject for us the MyBean instance returned by the myFactory().getObject() as it does with XML configuration.

This should also work if you are using @Inject/@Autowire in your @Component/@Service etc classes.

悍妇囚夫 2024-11-06 06:03:27

据我了解,您的问题是您希望 sqlSessionFactory() 的结果成为 SqlSessionFactory (用于其他方法),但您必须返回 SqlSessionFactoryBean 来自 @Bean 注解的方法,以触发 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 注释方法的调用会被执行返回 bean 初始化的方面拦截(FactoryBean in您的情况),以便在 sqlSessionFactory() 中调用 sqlSessionFactoryBean() 返回完全初始化的 FactoryBean

As far as I understand your problem is what you want a result of sqlSessionFactory() to be a SqlSessionFactory (for use in other methods), but you have to return SqlSessionFactoryBean from a @Bean-annotated method in order to trigger Spring callbacks.

It can be solved with the following workaround:

@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());
    }
}

The point is that calls to @Bean-annotated methods are intercepted by an aspect which performs initialization of the beans being returned (FactoryBean in your case), so that call to sqlSessionFactoryBean() in sqlSessionFactory() returns a fully initialized FactoryBean.

晚风撩人 2024-11-06 06:03:27

Spring JavaConfig 有一个 ConfigurationSupport 类,具有与 FactoryBean 一起使用的 getObject() 方法。

你可以使用它来扩展

@Configuration
public class MyConfig extends ConfigurationSupport {

    @Bean
    public MyBean getMyBean() {
       MyFactoryBean factory = new MyFactoryBean();
       return (MyBean) getObject(factory);
    }
}

jira问题有一些背景

,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();
    }
}

一些背景此处

Spring JavaConfig had a ConfigurationSupport class that had a getObject() method for use with FactoryBean's.

You would use it be extending

@Configuration
public class MyConfig extends ConfigurationSupport {

    @Bean
    public MyBean getMyBean() {
       MyFactoryBean factory = new MyFactoryBean();
       return (MyBean) getObject(factory);
    }
}

There is some background in this jira issue

With Spring 3.0 JavaConfig was moved into Spring core and it was decided to get rid of the ConfigurationSupport class. Suggested approach is to now use builder pattern instead of factories.

An example taken from the new SessionFactoryBuilder

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

Some background here

孤君无依 2024-11-06 06:03:27

这就是我正在做的事情,并且它有效:

@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;

Anywhere,它也会起作用。

This is what I'm doing, and it works:

@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);
}

Any bean you have declared can be passed as an argument to any other @Bean method (invoking the same method again will create a new instance which is not processed by spring).
If you declare a FactoryBean, you can use the bean type it creates as an argument for another @Bean method, and it will receive the right instance.
You could also use

@Autowired
private SqlSessionFactory sqlSessionFactory;

Anywhere and it will work too.

盛夏已如深秋| 2024-11-06 06:03:27

为什么不在 AppConfiguration 中注入 Factory?

@Configuration
public class AppConfig {

    @Resource
    private SqlSessionFactoryBean factory;

    @Bean 
    public SqlSessionFactory sqlSessionFactory() throws Exception {
       return factory.getObjectfactory();    
    }    
}

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

Why do you not inject the Factory in your AppConfiguration?

@Configuration
public class AppConfig {

    @Resource
    private SqlSessionFactoryBean factory;

    @Bean 
    public SqlSessionFactory sqlSessionFactory() throws Exception {
       return factory.getObjectfactory();    
    }    
}

But may I did not understand your question correct. Because it looks to me that you are trying something strange - go a step back and rethink what are you really need.

魔法少女 2024-11-06 06:03:27

我是这样做的:

@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 中。

Here is how I am doing it:

@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
}

The sessionFactoryBean gets created and the proper post-create lifecycle stuff happens to it (afterPropertiesSet, etc).

Note that I do not reference the sessionFactoryBean as a bean directly. I autowire the sessionFactory into my other beans.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文