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
pull/1118/merge
Dave Syer 11 years ago
parent 7fc1f19389
commit f8e847a6fc

@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.Registrar;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@ -52,6 +53,7 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@Configuration
@ConditionalOnClass(EmbeddedDatabaseType.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@Import(Registrar.class)
public class DataSourceAutoConfiguration {
public static final String CONFIGURATION_PREFIX = "spring.datasource";

@ -82,8 +82,8 @@ class DataSourceInitializer implements ApplicationListener<DataSourceInitialized
@Override
public void onApplicationEvent(DataSourceInitializedEvent event) {
// NOTE the even can happen more than once and
// the event datasource if not used here
// NOTE the event can happen more than once and
// the event datasource is not used here
if (!this.initialized) {
runDataScripts();
this.initialized = true;

@ -0,0 +1,83 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jdbc;
import javax.sql.DataSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* {@link BeanPostProcessor} used to fire {@link DataSourceInitializedEvent}s. Should only
* be registered via the inner {@link Registrar} class.
*
* @author Dave Syer
* @since 1.1.0
*/
class DataSourceInitializerPostProcessor implements BeanPostProcessor {
@Autowired
private BeanFactory beanFactory;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof DataSource) {
// force initialization of this bean as soon as we see a DataSource
this.beanFactory.getBean(DataSourceInitializer.class);
}
return bean;
}
/**
* {@link ImportBeanDefinitionRegistrar} to register the
* {@link DataSourceInitializerPostProcessor} without causing early bean instantiation
* issues.
*/
static class Registrar implements ImportBeanDefinitionRegistrar {
private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BEAN_NAME)) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DataSourceInitializerPostProcessor.class);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// We don't need this one to be post processed otherwise it can cause a
// cascade of bean instantiation that we would rather avoid.
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
}
}
}
}

@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.orm.jpa;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils;
@ -57,7 +58,9 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura
"SELECT COUNT(*) from CITY", Integer.class));
}
@Test
// This can't succeed because the data SQL is executed immediately after the schema
// and Hibernate hasn't initialized yet at that point
@Test(expected = BeanCreationException.class)
public void testDataScript() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.data:classpath:/city.sql");

@ -27,7 +27,6 @@ import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@ -72,7 +71,6 @@ public class SampleWebSecureApplication extends WebMvcConfigurerAdapter {
}
@Bean
@DependsOn("dataSourceAutoConfigurationInitializer")
public AuthenticationSecurity authenticationSecurity() {
return new AuthenticationSecurity();
}

Loading…
Cancel
Save