mirror of
https://gitee.com/dcren/initializr.git
synced 2025-09-19 01:58:16 +08:00
Disable @ProjectGenerationContext bean overriding by default
This commit extends ProjectGenerator so that the configuration of the ProjectGenerationContext can be externalized if necessary. By default, a context that does not allow bean overriding is provided. Closes gh-999
This commit is contained in:
@@ -24,6 +24,7 @@ import java.util.function.Supplier;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.ImportSelector;
|
import org.springframework.context.annotation.ImportSelector;
|
||||||
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
|
||||||
@@ -35,20 +36,62 @@ import org.springframework.core.type.AnnotationMetadata;
|
|||||||
*/
|
*/
|
||||||
public class ProjectGenerator {
|
public class ProjectGenerator {
|
||||||
|
|
||||||
private final Consumer<ProjectGenerationContext> projectGenerationContext;
|
private final Consumer<ProjectGenerationContext> contextConsumer;
|
||||||
|
|
||||||
|
private final Supplier<? extends ProjectGenerationContext> contextFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance with a customizer for the project generator application context.
|
* Create an instance with a customizer for the project generator application context
|
||||||
* @param projectGenerationContext a consumer of the project generation context before
|
* and a factory for the {@link ProjectGenerationContext}.
|
||||||
* it is refreshed.
|
* @param contextConsumer a consumer of the project generation context after
|
||||||
|
* contributors and the {@link ProjectDescription} have been registered but before it
|
||||||
|
* is refreshed
|
||||||
|
* @param contextFactory the factory to use to create {@link ProjectGenerationContext}
|
||||||
|
* instances
|
||||||
*/
|
*/
|
||||||
public ProjectGenerator(Consumer<ProjectGenerationContext> projectGenerationContext) {
|
public ProjectGenerator(Consumer<ProjectGenerationContext> contextConsumer,
|
||||||
this.projectGenerationContext = projectGenerationContext;
|
Supplier<? extends ProjectGenerationContext> contextFactory) {
|
||||||
|
this.contextConsumer = contextConsumer;
|
||||||
|
this.contextFactory = contextFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate project assets using the specified {@link ProjectAssetGenerator}. Invokes
|
* Create an instance with a customizer for the {@link ProjectGenerationContext} and a
|
||||||
* known {@link ProjectDescriptionCustomizer} if applicable prior to asset generation.
|
* default factory for the {@link ProjectGenerationContext} that disables bean
|
||||||
|
* definition overriding.
|
||||||
|
* @param contextConsumer a consumer of the project generation context after
|
||||||
|
* contributors and the {@link ProjectDescription} have been registered but before it
|
||||||
|
* is refreshed
|
||||||
|
* @see GenericApplicationContext#setAllowBeanDefinitionOverriding(boolean)
|
||||||
|
*/
|
||||||
|
public ProjectGenerator(Consumer<ProjectGenerationContext> contextConsumer) {
|
||||||
|
this(contextConsumer, defaultContextFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Supplier<ProjectGenerationContext> defaultContextFactory() {
|
||||||
|
return () -> {
|
||||||
|
ProjectGenerationContext context = new ProjectGenerationContext();
|
||||||
|
context.setAllowBeanDefinitionOverriding(false);
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate project assets using the specified {@link ProjectAssetGenerator} for the
|
||||||
|
* specified {@link ProjectDescription}.
|
||||||
|
* <p>
|
||||||
|
* Create a dedicated {@link ProjectGenerationContext} with the following
|
||||||
|
* characteristics:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@linkplain ProjectGenerationContext#setAllowBeanDefinitionOverriding(boolean)
|
||||||
|
* Bean overriding} disabled by default.</li>
|
||||||
|
* <li>Register a {@link ProjectDescription} bean based on the given
|
||||||
|
* {@code description} post-processed by available
|
||||||
|
* {@link ProjectDescriptionCustomizer} beans.</li>
|
||||||
|
* <li>Process all registered {@link ProjectGenerationConfiguration} classes.</li>
|
||||||
|
* </ul>
|
||||||
|
* Before the context is refreshed, it can be further customized using the customizer
|
||||||
|
* registered {@linkplain #ProjectGenerator(Consumer) on this instance}.
|
||||||
* @param description the description of the project to generate
|
* @param description the description of the project to generate
|
||||||
* @param projectAssetGenerator the {@link ProjectAssetGenerator} to invoke
|
* @param projectAssetGenerator the {@link ProjectAssetGenerator} to invoke
|
||||||
* @param <T> the type that gathers the project assets
|
* @param <T> the type that gathers the project assets
|
||||||
@@ -57,10 +100,10 @@ public class ProjectGenerator {
|
|||||||
*/
|
*/
|
||||||
public <T> T generate(ProjectDescription description, ProjectAssetGenerator<T> projectAssetGenerator)
|
public <T> T generate(ProjectDescription description, ProjectAssetGenerator<T> projectAssetGenerator)
|
||||||
throws ProjectGenerationException {
|
throws ProjectGenerationException {
|
||||||
try (ProjectGenerationContext context = new ProjectGenerationContext()) {
|
try (ProjectGenerationContext context = this.contextFactory.get()) {
|
||||||
context.registerBean(ProjectDescription.class, resolve(description, context));
|
context.registerBean(ProjectDescription.class, resolve(description, context));
|
||||||
context.register(CoreConfiguration.class);
|
context.register(CoreConfiguration.class);
|
||||||
this.projectGenerationContext.accept(context);
|
this.contextConsumer.accept(context);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
try {
|
try {
|
||||||
return projectAssetGenerator.generate(context);
|
return projectAssetGenerator.generate(context);
|
||||||
|
@@ -17,13 +17,17 @@
|
|||||||
package io.spring.initializr.generator.project;
|
package io.spring.initializr.generator.project;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionOverrideException;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.assertj.core.api.Assertions.entry;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
@@ -112,6 +116,28 @@ public class ProjectGeneratorTests {
|
|||||||
.isInstanceOf(ProjectGenerationException.class).hasCause(exception);
|
.isInstanceOf(ProjectGenerationException.class).hasCause(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateDoesNotAllowBeanDefinitionOverridingByDefault() {
|
||||||
|
ProjectGenerator generator = new ProjectGenerator((context) -> {
|
||||||
|
context.registerBean("testBean", String.class, () -> "test");
|
||||||
|
context.registerBean("testBean", String.class, () -> "duplicate");
|
||||||
|
});
|
||||||
|
ProjectAssetGenerator<?> assetGenerator = mock(ProjectAssetGenerator.class);
|
||||||
|
assertThatThrownBy(() -> generator.generate(new MutableProjectDescription(), assetGenerator))
|
||||||
|
.isInstanceOf(BeanDefinitionOverrideException.class).hasMessageContaining("testBean");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateCanBeConfiguredToAllowBeanDefinitionOverriding() {
|
||||||
|
ProjectGenerator generator = new ProjectGenerator((context) -> {
|
||||||
|
context.registerBean("testBean", String.class, () -> "test");
|
||||||
|
context.registerBean("testBean", String.class, () -> "duplicate");
|
||||||
|
}, ProjectGenerationContext::new);
|
||||||
|
Map<String, String> candidates = generator.generate(new MutableProjectDescription(),
|
||||||
|
(context) -> context.getBeansOfType(String.class));
|
||||||
|
assertThat(candidates).containsOnly(entry("testBean", "duplicate"));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Consumer<ProjectGenerationContext> mockContextInitializr() {
|
private Consumer<ProjectGenerationContext> mockContextInitializr() {
|
||||||
return mock(Consumer.class);
|
return mock(Consumer.class);
|
||||||
|
Reference in New Issue
Block a user