Deprecate Bootstrapper

Deprecate the `Bootstrapper` interface entirely and provide a
`BootstrapRegistryInitializer` alternative so that people can migrate.

Unfortunately our previous attempt to fix the typo in the `Bootstrapper`
interface didn't provide us a way to remove the deprecated method
without impacting users. It was also problematic for people who were
implementing `Bootstrapper` rather than using a lambda since they needed
to introduce the deprecated method.

We unfortunately can't see a way to fix the original typo without
introducing a new interface.

Fixes gh-25735
pull/26153/head
Phillip Webb 4 years ago
parent ddf5c9f5d1
commit 35aeae5a4f

@ -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);
}

@ -24,7 +24,9 @@ package org.springframework.boot;
* @since 2.4.0 * @since 2.4.0
* @see SpringApplication#addBootstrapper(Bootstrapper) * @see SpringApplication#addBootstrapper(Bootstrapper)
* @see BootstrapRegistry * @see BootstrapRegistry
* @deprecated since 2.4.5 in favor of {@link BootstrapRegistryInitializer}
*/ */
@Deprecated
public interface Bootstrapper { public interface Bootstrapper {
/** /**

@ -236,7 +236,7 @@ public class SpringApplication {
private Map<String, Object> defaultProperties; private Map<String, Object> defaultProperties;
private List<Bootstrapper> bootstrappers; private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
private Set<String> additionalProfiles = Collections.emptySet(); private Set<String> additionalProfiles = Collections.emptySet();
@ -280,12 +280,22 @@ public class SpringApplication {
Assert.notNull(primarySources, "PrimarySources must not be null"); Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass(); this.mainApplicationClass = deduceMainApplicationClass();
} }
@SuppressWarnings("deprecation")
private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
ArrayList<BootstrapRegistryInitializer> 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() { private Class<?> deduceMainApplicationClass() {
try { try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
@ -349,7 +359,7 @@ public class SpringApplication {
private DefaultBootstrapContext createBootstrapContext() { private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> initializer.initialize(bootstrapContext)); this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext; return bootstrapContext;
} }
@ -1054,10 +1064,24 @@ public class SpringApplication {
* {@link BootstrapRegistry}. * {@link BootstrapRegistry}.
* @param bootstrapper the bootstraper * @param bootstrapper the bootstraper
* @since 2.4.0 * @since 2.4.0
* @deprecated since 2.4.5 in favor of
* {@link #addBootstrapRegistryInitializer(BootstrapRegistryInitializer)}
*/ */
@Deprecated
public void addBootstrapper(Bootstrapper bootstrapper) { public void addBootstrapper(Bootstrapper bootstrapper) {
Assert.notNull(bootstrapper, "Bootstrapper must not be null"); 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));
} }
/** /**

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,7 +31,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.ApplicationContextFactory; import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.Banner; import org.springframework.boot.Banner;
import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.Bootstrapper; import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType; import org.springframework.boot.WebApplicationType;
import org.springframework.boot.convert.ApplicationConversionService; import org.springframework.boot.convert.ApplicationConversionService;
@ -400,17 +400,33 @@ public class SpringApplicationBuilder {
} }
/** /**
* Adds a {@link Bootstrapper} that can be used to initialize the * Adds a {@link org.springframework.boot.Bootstrapper} that can be used to initialize
* {@link BootstrapRegistry}. * the {@link BootstrapRegistry}.
* @param bootstrapper the bootstraper * @param bootstrapper the bootstraper
* @return the current builder * @return the current builder
* @since 2.4.0 * @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); this.application.addBootstrapper(bootstrapper);
return this; 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. * Flag to control whether the application should be initialized lazily.
* @param lazyInitialization the flag to set. Defaults to false. * @param lazyInitialization the flag to set. Defaults to false.

@ -1219,10 +1219,10 @@ class SpringApplicationTests {
} }
@Test @Test
void addBootstrapper() { void addBootstrapRegistryInitializer() {
SpringApplication application = new SpringApplication(ExampleConfig.class); SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE); application.setWebApplicationType(WebApplicationType.NONE);
application.addBootstrapper( application.addBootstrapRegistryInitializer(
(bootstrapContext) -> bootstrapContext.register(String.class, InstanceSupplier.of("boot"))); (bootstrapContext) -> bootstrapContext.register(String.class, InstanceSupplier.of("boot")));
TestApplicationListener listener = new TestApplicationListener(); TestApplicationListener listener = new TestApplicationListener();
application.addListeners(listener); application.addListeners(listener);
@ -1235,10 +1235,10 @@ class SpringApplicationTests {
} }
@Test @Test
void addBootstrapperCanRegisterBeans() { void addBootstrapRegistryInitializerCanRegisterBeans() {
SpringApplication application = new SpringApplication(ExampleConfig.class); SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE); application.setWebApplicationType(WebApplicationType.NONE);
application.addBootstrapper((bootstrapContext) -> { application.addBootstrapRegistryInitializer((bootstrapContext) -> {
bootstrapContext.register(String.class, InstanceSupplier.of("boot")); bootstrapContext.register(String.class, InstanceSupplier.of("boot"));
bootstrapContext.addCloseListener((event) -> event.getApplicationContext().getBeanFactory() bootstrapContext.addCloseListener((event) -> event.getApplicationContext().getBeanFactory()
.registerSingleton("test", event.getBootstrapContext().get(String.class))); .registerSingleton("test", event.getBootstrapContext().get(String.class)));
@ -1248,6 +1248,7 @@ class SpringApplicationTests {
} }
@Test @Test
@Deprecated
void whenABootstrapperImplementsOnlyTheOldMethodThenItIsCalled() { void whenABootstrapperImplementsOnlyTheOldMethodThenItIsCalled() {
SpringApplication application = new SpringApplication(ExampleConfig.class); SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE); application.setWebApplicationType(WebApplicationType.NONE);
@ -1259,6 +1260,7 @@ class SpringApplicationTests {
} }
@Test @Test
@Deprecated
void whenABootstrapperImplementsTheOldMethodAndTheNewMethodThenOnlyTheNewMethodIsCalled() { void whenABootstrapperImplementsTheOldMethodAndTheNewMethodThenOnlyTheNewMethodIsCalled() {
SpringApplication application = new SpringApplication(ExampleConfig.class); SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE); application.setWebApplicationType(WebApplicationType.NONE);
@ -1743,18 +1745,19 @@ class SpringApplicationTests {
} }
@Deprecated
static class OnlyOldMethodTestBootstrapper implements Bootstrapper { static class OnlyOldMethodTestBootstrapper implements Bootstrapper {
private boolean intitialized; private boolean intitialized;
@Override @Override
@SuppressWarnings("deprecation")
public void intitialize(BootstrapRegistry registry) { public void intitialize(BootstrapRegistry registry) {
this.intitialized = true; this.intitialized = true;
} }
} }
@Deprecated
static class BothMethodsTestBootstrapper implements Bootstrapper { static class BothMethodsTestBootstrapper implements Bootstrapper {
private boolean intitialized; private boolean intitialized;
@ -1762,7 +1765,6 @@ class SpringApplicationTests {
private boolean initialized; private boolean initialized;
@Override @Override
@SuppressWarnings("deprecation")
public void intitialize(BootstrapRegistry registry) { public void intitialize(BootstrapRegistry registry) {
this.intitialized = true; this.intitialized = true;
} }

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -283,6 +283,7 @@ class SpringApplicationBuilderTests {
} }
@Test @Test
@Deprecated
void addBootstrapper() { void addBootstrapper() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class) SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class)
.web(WebApplicationType.NONE).addBootstrapper((context) -> context.addCloseListener( .web(WebApplicationType.NONE).addBootstrapper((context) -> context.addCloseListener(
@ -291,6 +292,15 @@ class SpringApplicationBuilderTests {
assertThat(this.context.getBean("test")).isEqualTo("spring"); 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");
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
static class ExampleConfig { static class ExampleConfig {

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 // SubversionClient that still has access to data provided in the
// application.properties file // application.properties file
SpringApplication application = new SpringApplication(SampleBootstrapRegistryApplication.class); SpringApplication application = new SpringApplication(SampleBootstrapRegistryApplication.class);
application.addBootstrapper(SubversionBootstrap.withCustomClient(MySubversionClient::new)); application.addBootstrapRegistryInitializer(SubversionBootstrap.withCustomClient(MySubversionClient::new));
application.run(args); application.run(args);
} }

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 java.util.function.Function;
import org.springframework.boot.BootstrapContext; 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}. * {@link SubversionClient}.
* *
* @author Phillip Webb * @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 * @param clientFactory the client factory
* @return a {@link Bootstrapper} instance * @return a {@link BootstrapRegistryInitializer} instance
*/ */
public static Bootstrapper withCustomClient(Function<SubversionServerCertificate, SubversionClient> clientFactory) { public static BootstrapRegistryInitializer withCustomClient(
Function<SubversionServerCertificate, SubversionClient> clientFactory) {
return (registry) -> registry.register(SubversionClient.class, return (registry) -> registry.register(SubversionClient.class,
(bootstrapContext) -> createSubversionClient(bootstrapContext, clientFactory)); (bootstrapContext) -> createSubversionClient(bootstrapContext, clientFactory));
} }

Loading…
Cancel
Save