Skip to content

Commit dcc8393

Browse files
committed
#41 fix early initialization of factory beans
1 parent 2348193 commit dcc8393

File tree

4 files changed

+99
-25
lines changed

4 files changed

+99
-25
lines changed

embedded-database-spring-test/src/main/java/io/zonky/test/db/postgres/EmbeddedPostgresContextCustomizerFactory.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import org.springframework.beans.factory.config.BeanDefinition;
4141
import org.springframework.beans.factory.config.BeanDefinitionHolder;
4242
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
43-
import org.springframework.beans.factory.config.RuntimeBeanReference;
4443
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
4544
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
4645
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -235,7 +234,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t
235234
dataSourceDefinition.getConstructorArgumentValues()
236235
.addIndexedArgumentValue(0, flywayInfo.getBeanName());
237236
dataSourceDefinition.getConstructorArgumentValues()
238-
.addIndexedArgumentValue(1, new RuntimeBeanReference(contextInfo.getBeanName()));
237+
.addIndexedArgumentValue(1, contextInfo.getBeanName());
239238
}
240239

241240
String dataSourceBeanName = dataSourceInfo.getBeanName();

embedded-database-spring-test/src/main/java/io/zonky/test/db/postgres/EmptyEmbeddedPostgresDataSourceFactoryBean.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,33 @@
2020
import io.zonky.test.db.provider.DatabaseDescriptor;
2121
import io.zonky.test.db.provider.DatabasePreparer;
2222
import io.zonky.test.db.provider.GenericDatabaseProvider;
23+
import org.springframework.beans.BeansException;
24+
import org.springframework.beans.factory.BeanFactory;
25+
import org.springframework.beans.factory.BeanFactoryAware;
2326
import org.springframework.beans.factory.FactoryBean;
24-
import org.springframework.beans.factory.InitializingBean;
25-
import org.springframework.beans.factory.annotation.Autowired;
2627

2728
import javax.sql.DataSource;
2829

2930
/**
3031
* Implementation of the {@link org.springframework.beans.factory.FactoryBean} interface
3132
* that provides empty instances of the embedded postgres database.
3233
*/
33-
public class EmptyEmbeddedPostgresDataSourceFactoryBean implements FactoryBean<DataSource>, InitializingBean {
34+
public class EmptyEmbeddedPostgresDataSourceFactoryBean implements FactoryBean<DataSource>, BeanFactoryAware {
3435

3536
protected final DatabaseDescriptor databaseDescriptor;
3637

37-
@Autowired
38-
protected GenericDatabaseProvider databaseProvider;
39-
38+
private BeanFactory beanFactory;
4039
private DataSource dataSource;
4140

4241
public EmptyEmbeddedPostgresDataSourceFactoryBean(DatabaseDescriptor databaseDescriptor) {
4342
this.databaseDescriptor = databaseDescriptor;
4443
}
4544

45+
@Override
46+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
47+
this.beanFactory = beanFactory;
48+
}
49+
4650
@Override
4751
public boolean isSingleton() {
4852
return true;
@@ -54,16 +58,15 @@ public Class<?> getObjectType() {
5458
}
5559

5660
@Override
57-
public DataSource getObject() {
61+
public DataSource getObject() throws Exception {
62+
if (dataSource == null) {
63+
GenericDatabaseProvider databaseProvider = beanFactory.getBean(GenericDatabaseProvider.class);
64+
dataSource = databaseProvider.getDatabase(EmptyDatabasePreparer.INSTANCE, databaseDescriptor);
65+
EmbeddedDatabaseReporter.reportDataSource(dataSource);
66+
}
5867
return dataSource;
5968
}
6069

61-
@Override
62-
public void afterPropertiesSet() throws Exception {
63-
dataSource = databaseProvider.getDatabase(EmptyDatabasePreparer.INSTANCE, databaseDescriptor);
64-
EmbeddedDatabaseReporter.reportDataSource(dataSource);
65-
}
66-
6770
private static class EmptyDatabasePreparer implements DatabasePreparer {
6871

6972
public static final EmptyDatabasePreparer INSTANCE = new EmptyDatabasePreparer();

embedded-database-spring-test/src/main/java/io/zonky/test/db/postgres/FlywayEmbeddedPostgresDataSourceFactoryBean.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
import org.slf4j.LoggerFactory;
2525
import org.springframework.aop.framework.ProxyFactory;
2626
import org.springframework.beans.BeansException;
27+
import org.springframework.beans.factory.BeanFactory;
28+
import org.springframework.beans.factory.BeanFactoryAware;
2729
import org.springframework.beans.factory.FactoryBean;
28-
import org.springframework.beans.factory.InitializingBean;
2930
import org.springframework.beans.factory.config.BeanPostProcessor;
3031
import org.springframework.core.Ordered;
3132
import org.springframework.util.concurrent.ListenableFuture;
@@ -37,25 +38,31 @@
3738
* that provides fully cacheable instances of the embedded postgres database.
3839
* Each instance is backed by a flyway bean that is used for initializing the target database.
3940
*/
40-
public class FlywayEmbeddedPostgresDataSourceFactoryBean implements FactoryBean<DataSource>, BeanPostProcessor, InitializingBean, Ordered {
41+
public class FlywayEmbeddedPostgresDataSourceFactoryBean implements FactoryBean<DataSource>, BeanPostProcessor, BeanFactoryAware, Ordered {
4142

4243
private static final Logger logger = LoggerFactory.getLogger(FlywayEmbeddedPostgresDataSourceFactoryBean.class);
4344

4445
private final String flywayName;
45-
private final FlywayDataSourceContext dataSourceContext;
46+
private final String dataSourceContextName;
4647

48+
private BeanFactory beanFactory;
4749
private DataSource proxyInstance;
4850

49-
public FlywayEmbeddedPostgresDataSourceFactoryBean(String flywayName, FlywayDataSourceContext dataSourceContext) {
51+
public FlywayEmbeddedPostgresDataSourceFactoryBean(String flywayName, String dataSourceContextName) {
5052
this.flywayName = flywayName;
51-
this.dataSourceContext = dataSourceContext;
53+
this.dataSourceContextName = dataSourceContextName;
5254
}
5355

5456
@Override
5557
public int getOrder() {
5658
return Ordered.HIGHEST_PRECEDENCE;
5759
}
5860

61+
@Override
62+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
63+
this.beanFactory = beanFactory;
64+
}
65+
5966
@Override
6067
public boolean isSingleton() {
6168
return true;
@@ -68,17 +75,17 @@ public Class<?> getObjectType() {
6875

6976
@Override
7077
public DataSource getObject() {
78+
if (proxyInstance == null) {
79+
FlywayDataSourceContext dataSourceContext = beanFactory.getBean(dataSourceContextName, FlywayDataSourceContext.class);
80+
proxyInstance = ProxyFactory.getProxy(DataSource.class, dataSourceContext);
81+
}
7182
return proxyInstance;
7283
}
7384

74-
@Override
75-
public void afterPropertiesSet() {
76-
proxyInstance = ProxyFactory.getProxy(DataSource.class, dataSourceContext);
77-
}
78-
7985
@Override
8086
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
8187
if (bean instanceof Flyway && StringUtils.equals(beanName, flywayName)) {
88+
FlywayDataSourceContext dataSourceContext = beanFactory.getBean(dataSourceContextName, FlywayDataSourceContext.class);
8289
ListenableFuture<DataSource> reloadFuture = dataSourceContext.reload((Flyway) bean);
8390
reloadFuture.addCallback(
8491
result -> EmbeddedDatabaseReporter.reportDataSource(result),
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.zonky.test.db;
2+
3+
import io.zonky.test.category.FlywayIntegrationTests;
4+
import org.flywaydb.core.Flyway;
5+
import org.junit.Test;
6+
import org.junit.experimental.categories.Category;
7+
import org.junit.runner.RunWith;
8+
import org.springframework.beans.BeansException;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
11+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
12+
import org.springframework.context.annotation.Bean;
13+
import org.springframework.context.annotation.Configuration;
14+
import org.springframework.core.Ordered;
15+
import org.springframework.test.context.ContextConfiguration;
16+
import org.springframework.test.context.junit4.SpringRunner;
17+
18+
import javax.sql.DataSource;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
22+
@RunWith(SpringRunner.class)
23+
@Category(FlywayIntegrationTests.class)
24+
@AutoConfigureEmbeddedDatabase(beanName = "dataSource")
25+
@ContextConfiguration
26+
public class EarlyInitializationIntegrationTest {
27+
28+
@Configuration
29+
static class Config {
30+
31+
@Bean
32+
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
33+
return new TestBeanFactoryPostProcessor();
34+
}
35+
36+
@Bean(initMethod = "migrate")
37+
public Flyway flyway(DataSource dataSource) {
38+
Flyway flyway = new Flyway();
39+
flyway.setDataSource(dataSource);
40+
flyway.setSchemas("test");
41+
return flyway;
42+
}
43+
}
44+
45+
@Autowired
46+
private DataSource dataSource;
47+
48+
@Test
49+
public void testDataSource() {
50+
assertThat(dataSource).isNotNull();
51+
}
52+
53+
private static class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
54+
55+
@Override
56+
public int getOrder() {
57+
return Ordered.HIGHEST_PRECEDENCE;
58+
}
59+
60+
@Override
61+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
62+
beanFactory.getBeanNamesForType(DataSource.class, true, true);
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)