1
1
/*
2
- * Copyright 2012-2019 the original author or authors.
2
+ * Copyright 2012-2020 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .boot .autoconfigure .orm .jpa ;
18
18
19
19
import java .util .Map ;
20
+ import java .util .concurrent .Future ;
20
21
import java .util .function .Supplier ;
21
22
22
23
import javax .persistence .EntityManager ;
29
30
import org .springframework .beans .factory .annotation .Autowired ;
30
31
import org .springframework .beans .factory .config .BeanDefinition ;
31
32
import org .springframework .beans .factory .config .BeanPostProcessor ;
33
+ import org .springframework .beans .factory .support .AbstractBeanDefinition ;
34
+ import org .springframework .beans .factory .support .BeanDefinitionBuilder ;
32
35
import org .springframework .beans .factory .support .BeanDefinitionRegistry ;
33
- import org .springframework .beans .factory .support .GenericBeanDefinition ;
34
36
import org .springframework .boot .autoconfigure .jdbc .DataSourceSchemaCreatedEvent ;
35
37
import org .springframework .boot .jdbc .EmbeddedDatabaseConnection ;
36
38
import org .springframework .context .ApplicationContext ;
39
+ import org .springframework .context .ApplicationListener ;
37
40
import org .springframework .context .annotation .ImportBeanDefinitionRegistrar ;
41
+ import org .springframework .context .event .ContextRefreshedEvent ;
42
+ import org .springframework .core .Ordered ;
43
+ import org .springframework .core .task .AsyncTaskExecutor ;
38
44
import org .springframework .core .type .AnnotationMetadata ;
39
45
import org .springframework .orm .jpa .JpaDialect ;
40
46
import org .springframework .orm .jpa .JpaVendorAdapter ;
@@ -59,6 +65,12 @@ class DataSourceInitializedPublisher implements BeanPostProcessor {
59
65
60
66
private DataSourceSchemaCreatedPublisher schemaCreatedPublisher ;
61
67
68
+ private DataSourceInitializationCompletionListener initializationCompletionListener ;
69
+
70
+ DataSourceInitializedPublisher (DataSourceInitializationCompletionListener completionListener ) {
71
+ this .initializationCompletionListener = completionListener ;
72
+ }
73
+
62
74
@ Override
63
75
public Object postProcessBeforeInitialization (Object bean , String beanName ) throws BeansException {
64
76
if (bean instanceof LocalContainerEntityManagerFactoryBean ) {
@@ -119,26 +131,68 @@ private boolean isInitializingDatabase(DataSource dataSource) {
119
131
return hibernate .containsKey ("hibernate.hbm2ddl.auto" );
120
132
}
121
133
134
+ /**
135
+ * {@link ApplicationListener} that, upon receiving {@link ContextRefreshedEvent},
136
+ * blocks until any asynchronous DataSource initialization has completed.
137
+ */
138
+ static class DataSourceInitializationCompletionListener
139
+ implements ApplicationListener <ContextRefreshedEvent >, Ordered {
140
+
141
+ private volatile Future <?> dataSourceInitialization ;
142
+
143
+ @ Override
144
+ public void onApplicationEvent (ContextRefreshedEvent event ) {
145
+ Future <?> dataSourceInitialization = this .dataSourceInitialization ;
146
+ if (dataSourceInitialization != null ) {
147
+ try {
148
+ dataSourceInitialization .get ();
149
+ }
150
+ catch (Exception ex ) {
151
+ throw new RuntimeException (ex );
152
+ }
153
+ }
154
+ }
155
+
156
+ @ Override
157
+ public int getOrder () {
158
+ return Ordered .HIGHEST_PRECEDENCE ;
159
+ }
160
+
161
+ }
162
+
122
163
/**
123
164
* {@link ImportBeanDefinitionRegistrar} to register the
124
165
* {@link DataSourceInitializedPublisher} without causing early bean instantiation
125
166
* issues.
126
167
*/
127
168
static class Registrar implements ImportBeanDefinitionRegistrar {
128
169
129
- private static final String BEAN_NAME = "dataSourceInitializedPublisher" ;
170
+ private static final String PUBLISHER_BEAN_NAME = "dataSourceInitializedPublisher" ;
171
+
172
+ private static final String COMPLETION_LISTENER_BEAN_BEAN = DataSourceInitializationCompletionListener .class
173
+ .getName ();
130
174
131
175
@ Override
132
176
public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata ,
133
177
BeanDefinitionRegistry registry ) {
134
- if (!registry .containsBeanDefinition (BEAN_NAME )) {
135
- GenericBeanDefinition beanDefinition = new GenericBeanDefinition ();
136
- beanDefinition .setBeanClass (DataSourceInitializedPublisher .class );
137
- beanDefinition .setRole (BeanDefinition .ROLE_INFRASTRUCTURE );
178
+ if (!registry .containsBeanDefinition (PUBLISHER_BEAN_NAME )) {
179
+ DataSourceInitializationCompletionListener completionListener = new DataSourceInitializationCompletionListener ();
180
+ DataSourceInitializedPublisher publisher = new DataSourceInitializedPublisher (completionListener );
181
+ AbstractBeanDefinition publisherDefinition = BeanDefinitionBuilder
182
+ .genericBeanDefinition (DataSourceInitializedPublisher .class , () -> publisher )
183
+ .getBeanDefinition ();
184
+ publisherDefinition .setRole (BeanDefinition .ROLE_INFRASTRUCTURE );
138
185
// We don't need this one to be post processed otherwise it can cause a
139
186
// cascade of bean instantiation that we would rather avoid.
140
- beanDefinition .setSynthetic (true );
141
- registry .registerBeanDefinition (BEAN_NAME , beanDefinition );
187
+ publisherDefinition .setSynthetic (true );
188
+ registry .registerBeanDefinition (PUBLISHER_BEAN_NAME , publisherDefinition );
189
+ AbstractBeanDefinition listenerDefinition = BeanDefinitionBuilder .genericBeanDefinition (
190
+ DataSourceInitializationCompletionListener .class , () -> completionListener ).getBeanDefinition ();
191
+ listenerDefinition .setRole (BeanDefinition .ROLE_INFRASTRUCTURE );
192
+ // We don't need this one to be post processed otherwise it can cause a
193
+ // cascade of bean instantiation that we would rather avoid.
194
+ listenerDefinition .setSynthetic (true );
195
+ registry .registerBeanDefinition (COMPLETION_LISTENER_BEAN_BEAN , listenerDefinition );
142
196
}
143
197
}
144
198
@@ -193,7 +247,12 @@ public Class<? extends EntityManager> getEntityManagerInterface() {
193
247
@ Override
194
248
public void postProcessEntityManagerFactory (EntityManagerFactory entityManagerFactory ) {
195
249
this .delegate .postProcessEntityManagerFactory (entityManagerFactory );
196
- publishEventIfRequired (this .factoryBean , entityManagerFactory );
250
+ AsyncTaskExecutor bootstrapExecutor = this .factoryBean .getBootstrapExecutor ();
251
+ if (bootstrapExecutor != null ) {
252
+ DataSourceInitializedPublisher .this .initializationCompletionListener .dataSourceInitialization = bootstrapExecutor
253
+ .submit (() -> DataSourceInitializedPublisher .this .publishEventIfRequired (this .factoryBean ,
254
+ entityManagerFactory ));
255
+ }
197
256
}
198
257
199
258
}
0 commit comments