Skip to content

Commit f8e847a

Browse files
author
Dave Syer
committed
Change ordering of DataSourceInitializer a bit
It needs to run as soon as the DataSource is available really otherwise anything else that depends on the DataSource (like Security JDBC initializers) might fail when it tries to use it. One change from 1.1.1 is that if you have a schema.sql you had better make sure your data.sql talks to the same tables. In 1.1.1 you could sometimes get away with letting Hibernate initialize the tables for your data.sql and *also* have a schema.sql. This was fragile and doomed to fail eventually if the DataSourceInitializer somehow got initialized earlier (e.g. through a @dependsOn), so in the spririt of honesty being the best policy we explicitly disallow it now. Fixes gh-1115
1 parent 7fc1f19 commit f8e847a

File tree

5 files changed

+91
-5
lines changed

5 files changed

+91
-5
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2929
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3030
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
31+
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.Registrar;
3132
import org.springframework.boot.context.properties.ConfigurationProperties;
3233
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3334
import org.springframework.context.annotation.Bean;
@@ -52,6 +53,7 @@
5253
@Configuration
5354
@ConditionalOnClass(EmbeddedDatabaseType.class)
5455
@EnableConfigurationProperties(DataSourceProperties.class)
56+
@Import(Registrar.class)
5557
public class DataSourceAutoConfiguration {
5658

5759
public static final String CONFIGURATION_PREFIX = "spring.datasource";

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ private void runSchemaScripts() {
8282

8383
@Override
8484
public void onApplicationEvent(DataSourceInitializedEvent event) {
85-
// NOTE the even can happen more than once and
86-
// the event datasource if not used here
85+
// NOTE the event can happen more than once and
86+
// the event datasource is not used here
8787
if (!this.initialized) {
8888
runDataScripts();
8989
this.initialized = true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2012-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jdbc;
18+
19+
import javax.sql.DataSource;
20+
21+
import org.springframework.beans.BeansException;
22+
import org.springframework.beans.factory.BeanFactory;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.beans.factory.config.BeanDefinition;
25+
import org.springframework.beans.factory.config.BeanPostProcessor;
26+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
27+
import org.springframework.beans.factory.support.GenericBeanDefinition;
28+
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
29+
import org.springframework.core.type.AnnotationMetadata;
30+
31+
/**
32+
* {@link BeanPostProcessor} used to fire {@link DataSourceInitializedEvent}s. Should only
33+
* be registered via the inner {@link Registrar} class.
34+
*
35+
* @author Dave Syer
36+
* @since 1.1.0
37+
*/
38+
class DataSourceInitializerPostProcessor implements BeanPostProcessor {
39+
40+
@Autowired
41+
private BeanFactory beanFactory;
42+
43+
@Override
44+
public Object postProcessBeforeInitialization(Object bean, String beanName)
45+
throws BeansException {
46+
return bean;
47+
}
48+
49+
@Override
50+
public Object postProcessAfterInitialization(Object bean, String beanName)
51+
throws BeansException {
52+
if (bean instanceof DataSource) {
53+
// force initialization of this bean as soon as we see a DataSource
54+
this.beanFactory.getBean(DataSourceInitializer.class);
55+
}
56+
return bean;
57+
}
58+
59+
/**
60+
* {@link ImportBeanDefinitionRegistrar} to register the
61+
* {@link DataSourceInitializerPostProcessor} without causing early bean instantiation
62+
* issues.
63+
*/
64+
static class Registrar implements ImportBeanDefinitionRegistrar {
65+
66+
private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";
67+
68+
@Override
69+
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
70+
BeanDefinitionRegistry registry) {
71+
if (!registry.containsBeanDefinition(BEAN_NAME)) {
72+
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
73+
beanDefinition.setBeanClass(DataSourceInitializerPostProcessor.class);
74+
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
75+
// We don't need this one to be post processed otherwise it can cause a
76+
// cascade of bean instantiation that we would rather avoid.
77+
beanDefinition.setSynthetic(true);
78+
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
79+
}
80+
}
81+
}
82+
83+
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import javax.sql.DataSource;
2020

2121
import org.junit.Test;
22+
import org.springframework.beans.factory.BeanCreationException;
2223
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
2324
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
2425
import org.springframework.boot.test.EnvironmentTestUtils;
@@ -57,7 +58,9 @@ public void testDataScriptWithMissingDdl() throws Exception {
5758
"SELECT COUNT(*) from CITY", Integer.class));
5859
}
5960

60-
@Test
61+
// This can't succeed because the data SQL is executed immediately after the schema
62+
// and Hibernate hasn't initialized yet at that point
63+
@Test(expected = BeanCreationException.class)
6164
public void testDataScript() throws Exception {
6265
EnvironmentTestUtils.addEnvironment(this.context,
6366
"spring.datasource.data:classpath:/city.sql");

spring-boot-samples/spring-boot-sample-web-secure/src/main/java/sample/ui/secure/SampleWebSecureApplication.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.springframework.boot.builder.SpringApplicationBuilder;
2828
import org.springframework.context.annotation.Bean;
2929
import org.springframework.context.annotation.ComponentScan;
30-
import org.springframework.context.annotation.DependsOn;
3130
import org.springframework.core.Ordered;
3231
import org.springframework.core.annotation.Order;
3332
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -72,7 +71,6 @@ public ApplicationSecurity applicationSecurity() {
7271
}
7372

7473
@Bean
75-
@DependsOn("dataSourceAutoConfigurationInitializer")
7674
public AuthenticationSecurity authenticationSecurity() {
7775
return new AuthenticationSecurity();
7876
}

0 commit comments

Comments
 (0)