views:

128

answers:

3

Assume we have a simple @Configuration:

@Configuration
public class FooBarConfiguration {

    @Bean
    public Foo createFoo() {
        return new FooImpl();
    }

    @Bean
    @Autowired
    public Bar createBar(Foo foo) {
        return new BarImpl(foo);
    }
}

This class can be used with AnnotationConfigApplicationContext to produce Foo and Bar instances:

final ApplicationContext applicationContext =
    new AnnotationConfigApplicationContext(FooBarConfiguration.class);

final Foo foo = applicationContext.getBean(Foo.class);
final Bar bar = applicationContext.getBean(Bar.class);
assertSame(foo, bar.getFoo());

In the example above Spring will create a new instance of FooBarConfiguration and use it to produce Foos and Bars.

Now assume we already have an instance of FooBarConfiguration and we want to create Foos and Bars through Spring with this very instance. Is there a way to accomplish this?

How to create annotation-configured beans with an existing instance of configuration object?


ps. With Google Guice the solution is trivial:

public class FooBarConfiguration implements Module {

    @Provides
    @Singleton
    public Foo createFoo() {
        return new FooImpl();
    }

    @Override
    public void configure(Binder binder) {
    }

    @Provides
    @Inject
    @Singleton
    public Bar createBar(Foo foo) {
        return new BarImpl(foo);
    }
}

final FooBarConfiguration fooBarConfiguration = ...; // Our instance

final Injector injector = Guice.createInjector(fooBarConfiguration);
final Foo foo = injector.getInstance(Foo.class);
final Bar bar = injector.getInstance(Bar.class);
assertSame(foo, bar.getFoo());
+3  A: 

It's impossible, because some features of Spring's @Configuration require class enhancement, that can't be done on the existing instance.

For example, you can rewrite your configuration as follows:

@Configuration 
public class FooBarConfiguration { 

    @Bean 
    public Foo createFoo() { 
        return new FooImpl(); 
    } 

    @Bean 
    public Bar createBar() { 
        return new BarImpl(createFoo()); 
    } 
} 

and you'll still get a fully initialized instance of Foo, because call to createFoo() will be intercepted.

axtavt
I really don't see why is it impossible if the direct analog works in Guice. I'm fine with certain features not working, but the general thing "here's instances with annotated classes, make an application context out of them" must be possible.
lexicore
+2  A: 

Here's my own variant:

    @Test
    public void createFromConfigurationInstance() {

        final FooBarConfiguration fooBarConfiguration = new FooBarConfiguration();

        final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        final AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(
                beanFactory);

        annotatedBeanDefinitionReader.register(FooBarConfiguration.class);

        beanFactory.registerSingleton("fooBarConfiguration",
                fooBarConfiguration);

        final ConfigurableApplicationContext applicationContext = new GenericApplicationContext(
                beanFactory);

        applicationContext.refresh();

        final Foo foo = applicationContext.getBean(Foo.class);
        final Bar bar = applicationContext.getBean(Bar.class);
        assertSame(foo, bar.getFoo());

    }

However, I'm not sure if it is a "good" or "official" way.

lexicore
A: 

In Spring you basically have to either:

  1. Force your FooBarConfiguration to be a Singleton (not relying on the IoC container to enforce this - e.g. implement using enum, or another Singleton pattern)

  2. Wrap your FooBarConfiguration in a FooBarConfigurationFactory and use something like MethodInvokingFactoryBean to call it.

  3. Expose a static factory method, directly on FooBarConfiguration, and use the same technique as #2.

jayshao
1. doesn't work as Spring subclasses the configuration class when using java-based-config. 2. How would your method invoking factory bean know which factory to use? How would you cause spring to create the beans in the Config the factory created?
meriton