2

My project has a dependency on another one, and imports beans from it (using @ImportResource("foo.xml")).

foo.xml defines two datasources (datasource1 and datasource2), I would like to make datasource1 a primary (so all auto-configurations of Spring Boot will work).

Is it possible? I found out that there is a DefaultListableBeanFactory that has determinePrimaryCandidate method. So the idea is to create my own ListableBeanFactory, that would extend the DefaultListableBeanFactory, but how to force Spring Boot to use my implementation?

Or maybe there is another, easier way to mark a given bean as primary (without changing the configuration where it is defined).

Krzysztof Krasoń
  • 26,515
  • 16
  • 89
  • 115
  • Can you create a new data source bean in your project and inject your `datasource1` as argument? Then, you can annotate this method with `@Primary` and simply return the `datasource1`. Please, let me know if I understood your problem. – Mike Wojtyna Apr 20 '17 at 14:18
  • @MikeWojtyna I tried that, but it doesn't work. I get message that I have two beans and Spring can't decide which one to use. – Krzysztof Krasoń Apr 20 '17 at 15:50
  • Use @Qualifier to differentiate between your beans and name your primary bean. I'll try to prepare a working example for you later. – Mike Wojtyna Apr 20 '17 at 16:13
  • @MikeWojtyna I tried with `@Qualifier`, but I still have a problem - 2 beans with same type and Spring can't decide which one to use. That's why I was thinking of intercepting the registration of a bean and marking it as `Primary`. – Krzysztof Krasoń Apr 20 '17 at 16:16
  • I've just prepared a working example - everything works as intended. I'll post an answer. – Mike Wojtyna Apr 20 '17 at 23:16

1 Answers1

0

You can create a configuration in your project, which builds a new data source annotated as @Primary bean. This new data source will be the datasource1, which will be injected by spring to the new data source factory method. Here you have the working example.

The config:

@SpringBootApplication
public class BeanSpringExampleApplication
{

    @Bean(name = "dataSource1")
    public FakeDataSource dataSource1()
    {
        return new FakeDataSource("dataSource1");
    }

    @Bean(name = "dataSource2")
    public FakeDataSource dataSource2()
    {
        return new FakeDataSource("dataSource2");
    }

    @Bean
    @Primary
    public FakeDataSource primaryDataSource(
        @Qualifier("dataSource1") FakeDataSource dataSource1)
    {
        return dataSource1;
    }
}

Here you see three beans (using FakeDataSource class), which simulate your situation. The primaryDataSource bean factory method simply returns the dataSource1 (it's just a mere data source selector).

The FakeDataSource is just a placeholder, to make example runnable:

public class FakeDataSource
{
    private final String fakeProperty;

    public FakeDataSource(String id)
    {
        fakeProperty = id;
    }

    /**
     * @return the fakeProperty
     */
    public String getFakeProperty()
    {
        return fakeProperty;
    }
}

Finally, a test which proves everything is working:

@RunWith(SpringRunner.class)
@SpringBootTest
public class BeanSpringExampleApplicationTests
{
    @Autowired
    private FakeDataSource fakeDataSource;

    @Test
    public void should_AutowirePrimaryDataSource() throws Exception
    {
        assertEquals("dataSource1", fakeDataSource.getFakeProperty());
    }

}
Mike Wojtyna
  • 751
  • 5
  • 10