DATACMNS-1368 - Add support for deferred repository initialization.

Both the XML and annotation based configuration sources now support a bootstrap mode configuration property that allow to configure whether repositories are eagerly initialized unless declared as lazy, initialized in a deferred way (just before the application context finishs bootstrapping) or entirely lazy (upon first usage, i.e. a method invocation).

We now register a custom AutowireCandidateResolver that will consider all injection points of lazy repositories lazy too. This will prevent them to accidentally trigger downstream infrastructure initialization.
This commit is contained in:
Oliver Gierke
2018-08-09 08:15:11 +02:00
parent 60bbc59054
commit 5da6cf20a2
17 changed files with 728 additions and 6 deletions

View File

@@ -23,6 +23,7 @@ import lombok.Value;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -45,6 +46,11 @@ public class DefaultRepositoryConfigurationUnitTests {
RepositoryConfigurationExtension extension = new SimplerRepositoryConfigurationExtension("factory", "module");
@Before
public void before() {
when(source.getBootstrapMode()).thenReturn(BootstrapMode.DEFAULT);
}
@Test
public void supportsBasicConfiguration() {

View File

@@ -51,4 +51,6 @@ public @interface EnableRepositories {
boolean considerNestedRepositories() default false;
boolean limitImplementationBasePackages() default true;
BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT;
}

View File

@@ -49,7 +49,7 @@ public class RepositoryComponentProviderUnitTests {
RepositoryComponentProvider provider = new RepositoryComponentProvider(Collections.emptyList(), registry);
Set<BeanDefinition> components = provider.findCandidateComponents("org.springframework.data.repository.sample");
assertThat(components).hasSize(3);
assertThat(components).hasSize(4);
assertThat(components).extracting(BeanDefinition::getBeanClassName)
.contains(SampleAnnotatedRepository.class.getName());
}

View File

@@ -15,18 +15,29 @@
*/
package org.springframework.data.repository.config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.repository.config.RepositoryConfigurationDelegate.LazyRepositoryInjectionPointResolver;
import org.springframework.data.repository.sample.AddressRepository;
import org.springframework.data.repository.sample.AddressRepositoryClient;
import org.springframework.data.repository.sample.ProductRepository;
/**
@@ -35,7 +46,7 @@ import org.springframework.data.repository.sample.ProductRepository;
* @author Oliver Gierke
* @soundtrack Richard Spaven - Tribute (Whole Other*)
*/
@RunWith(MockitoJUnitRunner.class)
@RunWith(MockitoJUnitRunner.Silent.class)
public class RepositoryConfigurationDelegateUnitTests {
@Mock RepositoryConfigurationExtension extension;
@@ -61,6 +72,51 @@ public class RepositoryConfigurationDelegateUnitTests {
}
}
@Test // DATACMNS-1368
public void registersLazyAutowireCandidateResolver() {
assertLazyRepositoryBeanSetup(LazyConfig.class);
}
@Test // DATACMNS-1368
public void registersDeferredRepositoryInitializationListener() {
ListableBeanFactory beanFactory = assertLazyRepositoryBeanSetup(DeferredConfig.class);
assertThat(beanFactory.getBeanNamesForType(DeferredRepositoryInitializationListener.class)).isNotEmpty();
}
private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class<?> configClass) {
StandardEnvironment environment = new StandardEnvironment();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configClass);
assertThat(context.getDefaultListableBeanFactory().getAutowireCandidateResolver())
.isInstanceOf(LazyRepositoryInjectionPointResolver.class);
AddressRepositoryClient client = context.getBean(AddressRepositoryClient.class);
AddressRepository repository = client.getRepository();
assertThat(Advised.class.isInstance(repository)).isTrue();
TargetSource targetSource = Advised.class.cast(repository).getTargetSource();
assertThat(targetSource).isNotNull();
return context.getDefaultListableBeanFactory();
}
@EnableRepositories(basePackageClasses = ProductRepository.class)
static class TestConfig {}
@ComponentScan(basePackageClasses = AddressRepository.class)
@EnableRepositories(basePackageClasses = AddressRepository.class,
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = AddressRepository.class),
bootstrapMode = BootstrapMode.LAZY)
static class LazyConfig {}
@ComponentScan(basePackageClasses = AddressRepository.class)
@EnableRepositories(basePackageClasses = AddressRepository.class,
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = AddressRepository.class),
bootstrapMode = BootstrapMode.DEFERRED)
static class DeferredConfig {}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2018 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.data.repository.sample;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.sample.AddressRepository.Address;
/**
* @author Oliver Gierke
*/
public interface AddressRepository extends Repository<Address, Long> {
static class Address {}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2018 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.data.repository.sample;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor
public class AddressRepositoryClient {
private final @Getter AddressRepository repository;
}