diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapRegistryInitializer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapRegistryInitializer.java new file mode 100644 index 0000000000..496341f005 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapRegistryInitializer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2021 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 + * + * https://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; + +/** + * Callback interface that can be used to initialize a {@link BootstrapRegistry} before it + * is used. + * + * @author Phillip Webb + * @since 2.4.5 + * @see SpringApplication#addBootstrapRegistryInitializer(BootstrapRegistryInitializer) + * @see BootstrapRegistry + */ +@FunctionalInterface +public interface BootstrapRegistryInitializer { + + /** + * Initialize the given {@link BootstrapRegistry} with any required registrations. + * @param registry the registry to initialize + */ + void initialize(BootstrapRegistry registry); + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Bootstrapper.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Bootstrapper.java index dca0d7713c..ac73b59199 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Bootstrapper.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Bootstrapper.java @@ -24,7 +24,9 @@ package org.springframework.boot; * @since 2.4.0 * @see SpringApplication#addBootstrapper(Bootstrapper) * @see BootstrapRegistry + * @deprecated since 2.4.5 in favor of {@link BootstrapRegistryInitializer} */ +@Deprecated public interface Bootstrapper { /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index d23ff29470..f4ca8084f0 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -236,7 +236,7 @@ public class SpringApplication { private Map defaultProperties; - private List bootstrappers; + private List bootstrapRegistryInitializers; private Set additionalProfiles = Collections.emptySet(); @@ -282,12 +282,22 @@ public class SpringApplication { Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); - this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); + this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); } + @SuppressWarnings("deprecation") + private List getBootstrapRegistryInitializersFromSpringFactories() { + ArrayList initializers = new ArrayList<>(); + getSpringFactoriesInstances(Bootstrapper.class).stream() + .map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize)) + .forEach(initializers::add); + initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); + return initializers; + } + private Class deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); @@ -351,7 +361,7 @@ public class SpringApplication { private DefaultBootstrapContext createBootstrapContext() { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); - this.bootstrappers.forEach((initializer) -> initializer.initialize(bootstrapContext)); + this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; } @@ -1046,10 +1056,24 @@ public class SpringApplication { * {@link BootstrapRegistry}. * @param bootstrapper the bootstraper * @since 2.4.0 + * @deprecated since 2.4.5 in favor of + * {@link #addBootstrapRegistryInitializer(BootstrapRegistryInitializer)} */ + @Deprecated public void addBootstrapper(Bootstrapper bootstrapper) { Assert.notNull(bootstrapper, "Bootstrapper must not be null"); - this.bootstrappers.add(bootstrapper); + this.bootstrapRegistryInitializers.add(bootstrapper::initialize); + } + + /** + * Adds {@link BootstrapRegistryInitializer} instances that can be used to initialize + * the {@link BootstrapRegistry}. + * @param bootstrapRegistryInitializer the bootstrap registry initializer to add + * @since 2.4.5 + */ + public void addBootstrapRegistryInitializer(BootstrapRegistryInitializer bootstrapRegistryInitializer) { + Assert.notNull(bootstrapRegistryInitializer, "BootstrapRegistryInitializer must not be null"); + this.bootstrapRegistryInitializers.addAll(Arrays.asList(bootstrapRegistryInitializer)); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java index 443652f7b1..c75b5d3204 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java @@ -31,7 +31,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.boot.ApplicationContextFactory; import org.springframework.boot.Banner; import org.springframework.boot.BootstrapRegistry; -import org.springframework.boot.Bootstrapper; +import org.springframework.boot.BootstrapRegistryInitializer; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.convert.ApplicationConversionService; @@ -400,17 +400,33 @@ public class SpringApplicationBuilder { } /** - * Adds a {@link Bootstrapper} that can be used to initialize the - * {@link BootstrapRegistry}. + * Adds a {@link org.springframework.boot.Bootstrapper} that can be used to initialize + * the {@link BootstrapRegistry}. * @param bootstrapper the bootstraper * @return the current builder * @since 2.4.0 + * @deprecated since 2.4.5 in favor of + * {@link #addBootstrapRegistryInitializer(BootstrapRegistryInitializer)} */ - public SpringApplicationBuilder addBootstrapper(Bootstrapper bootstrapper) { + @Deprecated + public SpringApplicationBuilder addBootstrapper(org.springframework.boot.Bootstrapper bootstrapper) { this.application.addBootstrapper(bootstrapper); return this; } + /** + * Adds {@link BootstrapRegistryInitializer} instances that can be used to initialize + * the {@link BootstrapRegistry}. + * @param bootstrapRegistryInitializer the bootstrap registry initializer to add + * @return the current builder + * @since 2.4.5 + */ + public SpringApplicationBuilder addBootstrapRegistryInitializer( + BootstrapRegistryInitializer bootstrapRegistryInitializer) { + this.application.addBootstrapRegistryInitializer(bootstrapRegistryInitializer); + return this; + } + /** * Flag to control whether the application should be initialized lazily. * @param lazyInitialization the flag to set. Defaults to false. diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index cb8f6d3eb8..8b8d437746 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -1219,10 +1219,10 @@ class SpringApplicationTests { } @Test - void addBootstrapper() { + void addBootstrapRegistryInitializer() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); - application.addBootstrapper( + application.addBootstrapRegistryInitializer( (bootstrapContext) -> bootstrapContext.register(String.class, InstanceSupplier.of("boot"))); TestApplicationListener listener = new TestApplicationListener(); application.addListeners(listener); @@ -1235,10 +1235,10 @@ class SpringApplicationTests { } @Test - void addBootstrapperCanRegisterBeans() { + void addBootstrapRegistryInitializerCanRegisterBeans() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); - application.addBootstrapper((bootstrapContext) -> { + application.addBootstrapRegistryInitializer((bootstrapContext) -> { bootstrapContext.register(String.class, InstanceSupplier.of("boot")); bootstrapContext.addCloseListener((event) -> event.getApplicationContext().getBeanFactory() .registerSingleton("test", event.getBootstrapContext().get(String.class))); @@ -1248,6 +1248,7 @@ class SpringApplicationTests { } @Test + @Deprecated void whenABootstrapperImplementsOnlyTheOldMethodThenItIsCalled() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); @@ -1259,6 +1260,7 @@ class SpringApplicationTests { } @Test + @Deprecated void whenABootstrapperImplementsTheOldMethodAndTheNewMethodThenOnlyTheNewMethodIsCalled() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); @@ -1758,18 +1760,19 @@ class SpringApplicationTests { } + @Deprecated static class OnlyOldMethodTestBootstrapper implements Bootstrapper { private boolean intitialized; @Override - @SuppressWarnings("deprecation") public void intitialize(BootstrapRegistry registry) { this.intitialized = true; } } + @Deprecated static class BothMethodsTestBootstrapper implements Bootstrapper { private boolean intitialized; @@ -1777,7 +1780,6 @@ class SpringApplicationTests { private boolean initialized; @Override - @SuppressWarnings("deprecation") public void intitialize(BootstrapRegistry registry) { this.intitialized = true; } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java index 634d2a337a..86616b1662 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java @@ -283,6 +283,7 @@ class SpringApplicationBuilderTests { } @Test + @Deprecated void addBootstrapper() { SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class) .web(WebApplicationType.NONE).addBootstrapper((context) -> context.addCloseListener( @@ -291,6 +292,15 @@ class SpringApplicationBuilderTests { assertThat(this.context.getBean("test")).isEqualTo("spring"); } + @Test + void addBootstrapRegistryInitializer() { + SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class) + .web(WebApplicationType.NONE).addBootstrapRegistryInitializer((context) -> context.addCloseListener( + (event) -> event.getApplicationContext().getBeanFactory().registerSingleton("test", "spring"))); + this.context = application.run(); + assertThat(this.context.getBean("test")).isEqualTo("spring"); + } + @Test void setEnvironmentPrefix() { SpringApplicationBuilder builder = new SpringApplicationBuilder(ExampleConfig.class).environmentPrefix("test"); diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplication.java index 0b41f26005..3f1b89ca5f 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplication.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -29,7 +29,7 @@ public class SampleBootstrapRegistryApplication { // SubversionClient that still has access to data provided in the // application.properties file SpringApplication application = new SpringApplication(SampleBootstrapRegistryApplication.class); - application.addBootstrapper(SubversionBootstrap.withCustomClient(MySubversionClient::new)); + application.addBootstrapRegistryInitializer(SubversionBootstrap.withCustomClient(MySubversionClient::new)); application.run(args); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionBootstrap.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionBootstrap.java index 8617c36fe5..911a08ffcf 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionBootstrap.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionBootstrap.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -19,10 +19,10 @@ package smoketest.bootstrapregistry.external.svn; import java.util.function.Function; import org.springframework.boot.BootstrapContext; -import org.springframework.boot.Bootstrapper; +import org.springframework.boot.BootstrapRegistryInitializer; /** - * Allows the user to register a {@link Bootstrapper} with a custom + * Allows the user to register a {@link BootstrapRegistryInitializer} with a custom * {@link SubversionClient}. * * @author Phillip Webb @@ -33,11 +33,12 @@ public final class SubversionBootstrap { } /** - * Return a {@link Bootstrapper} for the given client factory. + * Return a {@link BootstrapRegistryInitializer} for the given client factory. * @param clientFactory the client factory - * @return a {@link Bootstrapper} instance + * @return a {@link BootstrapRegistryInitializer} instance */ - public static Bootstrapper withCustomClient(Function clientFactory) { + public static BootstrapRegistryInitializer withCustomClient( + Function clientFactory) { return (registry) -> registry.register(SubversionClient.class, (bootstrapContext) -> createSubversionClient(bootstrapContext, clientFactory)); }