diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java index 00328e129a..b97fe44325 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -64,7 +64,7 @@ public class ConditionEvaluationReportLoggingListener this(LogLevel.DEBUG); } - public ConditionEvaluationReportLoggingListener(LogLevel logLevelForReport) { + private ConditionEvaluationReportLoggingListener(LogLevel logLevelForReport) { Assert.isTrue(isInfoOrDebug(logLevelForReport), "LogLevel must be INFO or DEBUG"); this.logLevelForReport = logLevelForReport; } @@ -73,6 +73,18 @@ public class ConditionEvaluationReportLoggingListener return LogLevel.INFO.equals(logLevelForReport) || LogLevel.DEBUG.equals(logLevelForReport); } + /** + * Static factory method that creates a + * {@link ConditionEvaluationReportLoggingListener} which logs the report at the + * specified log level. + * @param logLevelForReport the log level to log the report at + * @return a {@link ConditionEvaluationReportLoggingListener} instance. + * @since 3.0.0 + */ + public static ConditionEvaluationReportLoggingListener forLogLevel(LogLevel logLevelForReport) { + return new ConditionEvaluationReportLoggingListener(logLevelForReport); + } + public LogLevel getLogLevelForReport() { return this.logLevelForReport; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationWithoutJpaTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationWithoutJpaTests.java index 1b0328c5a8..a98511fc4d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationWithoutJpaTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationWithoutJpaTests.java @@ -54,7 +54,7 @@ class BatchAutoConfigurationWithoutJpaTests { void jdbcWithDefaultSettings() { this.contextRunner.withUserConfiguration(DefaultConfiguration.class, EmbeddedDataSourceConfiguration.class) .withPropertyValues("spring.datasource.generate-unique-name=true") - .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO)).run((context) -> { + .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)).run((context) -> { assertThat(context).hasSingleBean(JobLauncher.class); assertThat(context).hasSingleBean(JobExplorer.class); assertThat(context).hasSingleBean(JobRepository.class); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java index d986541b78..02a67adc3e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 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. @@ -123,8 +123,8 @@ class ConditionEvaluationReportLoggingListenerTests { @Test void listenerWithInfoLevelShouldLogAtInfo(CapturedOutput output) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - ConditionEvaluationReportLoggingListener initializer = new ConditionEvaluationReportLoggingListener( - LogLevel.INFO); + ConditionEvaluationReportLoggingListener initializer = ConditionEvaluationReportLoggingListener + .forLogLevel(LogLevel.INFO); initializer.initialize(context); context.register(Config.class); context.refresh(); @@ -135,7 +135,7 @@ class ConditionEvaluationReportLoggingListenerTests { @Test void listenerSupportsOnlyInfoAndDebug() { assertThatIllegalArgumentException() - .isThrownBy(() -> new ConditionEvaluationReportLoggingListener(LogLevel.TRACE)) + .isThrownBy(() -> ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.TRACE)) .withMessageContaining("LogLevel must be INFO or DEBUG"); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfigurationTests.java index d40130f181..3e5c4d6f8b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfigurationTests.java @@ -70,7 +70,7 @@ class SqlInitializationAutoConfigurationTests { @Test void whenConnectionFactoryIsAvailableAndModeIsNeverThenInitializerIsNotAutoConfigured() { this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class)) - .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO)) + .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) .withPropertyValues("spring.sql.init.mode:never") .run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class)); } diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java index 06cd43a194..914d36f0ac 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -72,8 +72,8 @@ public final class RemoteSpringApplication { private Collection> getListeners() { List> listeners = new ArrayList<>(); listeners.add(new AnsiOutputApplicationListener()); - listeners.add(new EnvironmentPostProcessorApplicationListener( - EnvironmentPostProcessorsFactory.of(ConfigDataEnvironmentPostProcessor.class))); + listeners.add(EnvironmentPostProcessorApplicationListener + .with(EnvironmentPostProcessorsFactory.of(ConfigDataEnvironmentPostProcessor.class))); listeners.add(new LoggingApplicationListener()); listeners.add(new RemoteUrlPropertyExtractor()); return listeners; diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.java index 08d2ba709a..c40da156cd 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -28,7 +28,7 @@ class MyConditionEvaluationReportingTests { void autoConfigTest() { // @formatter:off new ApplicationContextRunner() - .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO)) + .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) .run((context) -> { // Test something... }); diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt index 9e5d729cec..2e3678c790 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt @@ -28,7 +28,7 @@ class MyConditionEvaluationReportingTests { @Test fun autoConfigTest() { ApplicationContextRunner() - .withInitializer(ConditionEvaluationReportLoggingListener(LogLevel.INFO)) + .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) .run { context: AssertableApplicationContext? -> } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/mock/MockSpringFactoriesLoader.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/mock/MockSpringFactoriesLoader.java new file mode 100644 index 0000000000..86a3dd9a3f --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/mock/MockSpringFactoriesLoader.java @@ -0,0 +1,130 @@ +/* + * Copyright 2012-2022 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.testsupport.mock; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.springframework.core.io.support.SpringFactoriesLoader; + +/** + * Simple mock {@link SpringFactoriesLoader} implementation that can be used for testing + * purposes. + * + * @author Phillip Webb + * @since 3.0.0 + */ +public class MockSpringFactoriesLoader extends SpringFactoriesLoader { + + private final AtomicInteger sequence = new AtomicInteger(); + + private final Map> factories; + + private final Map implementations = new HashMap<>(); + + /** + * Create a new {@link MockSpringFactoriesLoader} instance with the default + * classloader. + */ + public MockSpringFactoriesLoader() { + this(null); + } + + /** + * Create a new {@link MockSpringFactoriesLoader} instance with the given classloader. + * @param classLoader the classloader to use + */ + public MockSpringFactoriesLoader(ClassLoader classLoader) { + this(classLoader, new LinkedHashMap<>()); + } + + protected MockSpringFactoriesLoader(ClassLoader classLoader, Map> factories) { + super(classLoader, factories); + this.factories = factories; + } + + @Override + @SuppressWarnings("unchecked") + protected T instantiateFactory(String implementationName, Class type, ArgumentResolver argumentResolver, + FailureHandler failureHandler) { + if (implementationName.startsWith("!")) { + Object implementation = this.implementations.get(implementationName); + if (implementation != null) { + return (T) implementation; + } + } + return super.instantiateFactory(implementationName, type, argumentResolver, failureHandler); + } + + /** + * Add factory implementations to this instance. + * @param factoryType the factory type class + * @param factoryImplementations the implementation classes + * @param the factory type + */ + @SafeVarargs + public final void add(Class factoryType, Class... factoryImplementations) { + for (Class factoryImplementation : factoryImplementations) { + add(factoryType.getName(), factoryImplementation.getName()); + } + } + + /** + * Add factory implementations to this instance. + * @param factoryType the factory type class name + * @param factoryImplementations the implementation class names + */ + public void add(String factoryType, String... factoryImplementations) { + List implementations = this.factories.computeIfAbsent(factoryType, (key) -> new ArrayList<>()); + for (String factoryImplementation : factoryImplementations) { + implementations.add(factoryImplementation); + } + } + + /** + * Add factory instances to this instance. + * @param factoryType the factory type class + * @param factoryInstances the implementation instances to add + * @param the factory type + */ + @SuppressWarnings("unchecked") + public void addInstance(Class factoryType, T... factoryInstances) { + addInstance(factoryType.getName(), factoryInstances); + } + + /** + * Add factory instances to this instance. + * @param factoryType the factory type class name + * @param factoryInstance the implementation instances to add + * @param the factory instance type + */ + @SuppressWarnings("unchecked") + public void addInstance(String factoryType, T... factoryInstance) { + List implementations = this.factories.computeIfAbsent(factoryType, (key) -> new ArrayList<>()); + for (T factoryImplementation : factoryInstance) { + String reference = "!" + factoryType + ":" + factoryImplementation.getClass().getName() + + this.sequence.getAndIncrement(); + implementations.add(reference); + this.implementations.put(reference, factoryImplementation); + } + } + +} 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 664ca34913..0b6689476c 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 @@ -16,7 +16,6 @@ package org.springframework.boot; -import java.lang.reflect.Constructor; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -33,7 +32,6 @@ import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeanUtils; import org.springframework.beans.CachedIntrospectionResults; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -420,42 +418,21 @@ public class SpringApplication { } private SpringApplicationRunListeners getRunListeners(String[] args) { - Class[] types = new Class[] { SpringApplication.class, String[].class }; + SpringFactoriesLoader.ArgumentResolver argumentResolver = SpringFactoriesLoader.ArgumentResolver + .of(SpringApplication.class, this).and(String[].class, args); return new SpringApplicationRunListeners(logger, - getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), + getSpringFactoriesInstances(SpringApplicationRunListener.class, argumentResolver), this.applicationStartup); } private Collection getSpringFactoriesInstances(Class type) { - return getSpringFactoriesInstances(type, new Class[] {}); + return getSpringFactoriesInstances(type, null); } - private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) { + private Collection getSpringFactoriesInstances(Class type, + SpringFactoriesLoader.ArgumentResolver argumentResolver) { ClassLoader classLoader = getClassLoader(); - // Use names and ensure unique to protect against duplicates - Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); - List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); - AnnotationAwareOrderComparator.sort(instances); - return instances; - } - - @SuppressWarnings("unchecked") - private List createSpringFactoriesInstances(Class type, Class[] parameterTypes, - ClassLoader classLoader, Object[] args, Set names) { - List instances = new ArrayList<>(names.size()); - for (String name : names) { - try { - Class instanceClass = ClassUtils.forName(name, classLoader); - Assert.isAssignable(type, instanceClass); - Constructor constructor = instanceClass.getDeclaredConstructor(parameterTypes); - T instance = (T) BeanUtils.instantiateClass(constructor, args); - instances.add(instance); - } - catch (Throwable ex) { - throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); - } - } - return instances; + return SpringFactoriesLoader.forDefaultResourceLocation(classLoader).load(type, argumentResolver); } private ConfigurableEnvironment getOrCreateEnvironment() { @@ -806,8 +783,9 @@ public class SpringApplication { private Collection getExceptionReporters(ConfigurableApplicationContext context) { try { - return getSpringFactoriesInstances(SpringBootExceptionReporter.class, - new Class[] { ConfigurableApplicationContext.class }, context); + SpringFactoriesLoader.ArgumentResolver argumentResolver = SpringFactoriesLoader.ArgumentResolver + .of(ConfigurableApplicationContext.class, context); + return getSpringFactoriesInstances(SpringBootExceptionReporter.class, argumentResolver); } catch (Throwable ex) { return Collections.emptyList(); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java index a2ce0a443b..8a14f1e462 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java @@ -29,6 +29,7 @@ import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcesso import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.json.JsonParser; import org.springframework.boot.json.JsonParserFactory; +import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.Ordered; import org.springframework.core.env.CommandLinePropertySource; import org.springframework.core.env.ConfigurableEnvironment; @@ -101,10 +102,11 @@ public class CloudFoundryVcapEnvironmentPostProcessor implements EnvironmentPost /** * Create a new {@link CloudFoundryVcapEnvironmentPostProcessor} instance. - * @param logger the logger to use + * @param logFactory the log factory to use + * @since 3.0 */ - public CloudFoundryVcapEnvironmentPostProcessor(Log logger) { - this.logger = logger; + public CloudFoundryVcapEnvironmentPostProcessor(DeferredLogFactory logFactory) { + this.logger = logFactory.getLog(CloudFoundryVcapEnvironmentPostProcessor.class); } public void setOrder(int order) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java index a9d9f4112e..68e6961594 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java @@ -41,6 +41,7 @@ import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.log.LogMessage; import org.springframework.util.StringUtils; @@ -148,13 +149,15 @@ class ConfigDataEnvironment { this.additionalProfiles = additionalProfiles; this.environmentUpdateListener = (environmentUpdateListener != null) ? environmentUpdateListener : ConfigDataEnvironmentUpdateListener.NONE; - this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, resourceLoader.getClassLoader()); + this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, + SpringFactoriesLoader.forDefaultResourceLocation()); this.contributors = createContributors(binder); } protected ConfigDataLocationResolvers createConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, Binder binder, ResourceLoader resourceLoader) { - return new ConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader); + return new ConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader, + SpringFactoriesLoader.forDefaultResourceLocation(resourceLoader.getClassLoader())); } private ConfigDataEnvironmentContributors createContributors(Binder binder) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java index 6f72282011..5825c9dfa4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java @@ -70,7 +70,7 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces this(logFactory, bootstrapContext, null); } - public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory, + private ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, ConfigDataEnvironmentUpdateListener environmentUpdateListener) { this.logFactory = logFactory; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java index 2eafb12776..e0f3f31a0b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -18,8 +18,6 @@ package org.springframework.boot.context.config; import java.io.IOException; -import org.apache.commons.logging.Log; - import org.springframework.boot.BootstrapContext; import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.ConfigurableBootstrapContext; @@ -30,8 +28,7 @@ import org.springframework.boot.logging.DeferredLogFactory; * {@link ConfigDataResource}. Implementations should be added as {@code spring.factories} * entries. The following constructor parameter types are supported: *
    - *
  • {@link Log} or {@link DeferredLogFactory} - if the loader needs deferred - * logging
  • + *
  • {@link DeferredLogFactory} - if the loader needs deferred logging
  • *
  • {@link ConfigurableBootstrapContext} - A bootstrap context that can be used to * store objects that may be expensive to create, or need to be shared * ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).
  • diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java index c9636ed4cc..1f93a0b2be 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -27,7 +27,6 @@ import org.springframework.boot.BootstrapContext; import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.ConfigurableBootstrapContext; import org.springframework.boot.logging.DeferredLogFactory; -import org.springframework.boot.util.Instantiator; import org.springframework.core.ResolvableType; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.log.LogMessage; @@ -43,7 +42,8 @@ class ConfigDataLoaders { private final Log logger; - private final List> loaders; + @SuppressWarnings("rawtypes") + private final List loaders; private final List> resourceTypes; @@ -51,37 +51,20 @@ class ConfigDataLoaders { * Create a new {@link ConfigDataLoaders} instance. * @param logFactory the deferred log factory * @param bootstrapContext the bootstrap context - * @param classLoader the class loader used when loading + * @param springFactoriesLoader the loader to use */ ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, - ClassLoader classLoader) { - this(logFactory, bootstrapContext, classLoader, - SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, classLoader)); - } - - /** - * Create a new {@link ConfigDataLoaders} instance. - * @param logFactory the deferred log factory - * @param bootstrapContext the bootstrap context - * @param classLoader the class loader used when loading - * @param names the {@link ConfigDataLoader} class names instantiate - */ - ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, - ClassLoader classLoader, List names) { + SpringFactoriesLoader springFactoriesLoader) { this.logger = logFactory.getLog(getClass()); - Instantiator> instantiator = new Instantiator<>(ConfigDataLoader.class, - (availableParameters) -> { - availableParameters.add(Log.class, logFactory::getLog); - availableParameters.add(DeferredLogFactory.class, logFactory); - availableParameters.add(ConfigurableBootstrapContext.class, bootstrapContext); - availableParameters.add(BootstrapContext.class, bootstrapContext); - availableParameters.add(BootstrapRegistry.class, bootstrapContext); - }); - this.loaders = instantiator.instantiate(classLoader, names); + SpringFactoriesLoader.ArgumentResolver argumentResolver = SpringFactoriesLoader.ArgumentResolver + .of(DeferredLogFactory.class, logFactory).and(ConfigurableBootstrapContext.class, bootstrapContext) + .and(BootstrapContext.class, bootstrapContext).and(BootstrapRegistry.class, bootstrapContext); + this.loaders = springFactoriesLoader.load(ConfigDataLoader.class, argumentResolver); this.resourceTypes = getResourceTypes(this.loaders); } - private List> getResourceTypes(List> loaders) { + @SuppressWarnings("rawtypes") + private List> getResourceTypes(List loaders) { List> resourceTypes = new ArrayList<>(loaders.size()); for (ConfigDataLoader loader : loaders) { resourceTypes.add(getResourceType(loader)); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java index 08bfa76821..9f081f734d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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,8 +19,6 @@ package org.springframework.boot.context.config; import java.util.Collections; import java.util.List; -import org.apache.commons.logging.Log; - import org.springframework.boot.BootstrapContext; import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.ConfigurableBootstrapContext; @@ -37,8 +35,7 @@ import org.springframework.core.io.ResourceLoader; * {@code spring.factories} entries. The following constructor parameter types are * supported: *
      - *
    • {@link Log} or {@link DeferredLogFactory} - if the resolver needs deferred - * logging
    • + *
    • {@link DeferredLogFactory} - if the resolver needs deferred logging
    • *
    • {@link Binder} - if the resolver needs to obtain values from the initial * {@link Environment}
    • *
    • {@link ResourceLoader} - if the resolver needs a resource loader
    • diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java index bfdc0ac5fe..c48f404c43 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -28,7 +28,6 @@ import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.ConfigurableBootstrapContext; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.logging.DeferredLogFactory; -import org.springframework.boot.util.Instantiator; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.SpringFactoriesLoader; @@ -50,37 +49,19 @@ class ConfigDataLocationResolvers { * @param bootstrapContext the bootstrap context * @param binder a binder providing values from the initial {@link Environment} * @param resourceLoader {@link ResourceLoader} to load resource locations + * @param springFactoriesLoader to load {@link ConfigDataLocationResolver} instances */ ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, - Binder binder, ResourceLoader resourceLoader) { - this(logFactory, bootstrapContext, binder, resourceLoader, SpringFactoriesLoader - .loadFactoryNames(ConfigDataLocationResolver.class, resourceLoader.getClassLoader())); + Binder binder, ResourceLoader resourceLoader, SpringFactoriesLoader springFactoriesLoader) { + SpringFactoriesLoader.ArgumentResolver argumentResolver = SpringFactoriesLoader.ArgumentResolver + .of(DeferredLogFactory.class, logFactory).and(Binder.class, binder) + .and(ResourceLoader.class, resourceLoader).and(ConfigurableBootstrapContext.class, bootstrapContext) + .and(BootstrapContext.class, bootstrapContext).and(BootstrapRegistry.class, bootstrapContext); + this.resolvers = reorder(springFactoriesLoader.load(ConfigDataLocationResolver.class, argumentResolver)); } - /** - * Create a new {@link ConfigDataLocationResolvers} instance. - * @param logFactory a {@link DeferredLogFactory} used to inject {@link Log} instances - * @param bootstrapContext the bootstrap context - * @param binder {@link Binder} providing values from the initial {@link Environment} - * @param resourceLoader {@link ResourceLoader} to load resource locations - * @param names the {@link ConfigDataLocationResolver} class names - */ - ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, - Binder binder, ResourceLoader resourceLoader, List names) { - Instantiator> instantiator = new Instantiator<>(ConfigDataLocationResolver.class, - (availableParameters) -> { - availableParameters.add(Log.class, logFactory::getLog); - availableParameters.add(DeferredLogFactory.class, logFactory); - availableParameters.add(Binder.class, binder); - availableParameters.add(ResourceLoader.class, resourceLoader); - availableParameters.add(ConfigurableBootstrapContext.class, bootstrapContext); - availableParameters.add(BootstrapContext.class, bootstrapContext); - availableParameters.add(BootstrapRegistry.class, bootstrapContext); - }); - this.resolvers = reorder(instantiator.instantiate(resourceLoader.getClassLoader(), names)); - } - - private List> reorder(List> resolvers) { + @SuppressWarnings("rawtypes") + private List> reorder(List resolvers) { List> reordered = new ArrayList<>(resolvers.size()); StandardConfigDataLocationResolver resourceResolver = null; for (ConfigDataLocationResolver resolver : resolvers) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java index a10f2d9049..10743184f3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -35,6 +35,7 @@ import org.apache.commons.logging.Log; import org.springframework.boot.context.config.LocationResourceLoader.ResourceType; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.env.PropertySourceLoader; +import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; import org.springframework.core.io.ClassPathResource; @@ -79,12 +80,13 @@ public class StandardConfigDataLocationResolver /** * Create a new {@link StandardConfigDataLocationResolver} instance. - * @param logger the logger to use + * @param logFactory the factory for loggers to use * @param binder a binder backed by the initial {@link Environment} * @param resourceLoader a {@link ResourceLoader} used to load resources */ - public StandardConfigDataLocationResolver(Log logger, Binder binder, ResourceLoader resourceLoader) { - this.logger = logger; + public StandardConfigDataLocationResolver(DeferredLogFactory logFactory, Binder binder, + ResourceLoader resourceLoader) { + this.logger = logFactory.getLog(StandardConfigDataLocationResolver.class); this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()); this.configNames = getConfigNames(binder); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java index d493c47894..6af6c73a71 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java @@ -25,12 +25,12 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.boot.SpringBootExceptionReporter; -import org.springframework.boot.util.Instantiator; -import org.springframework.boot.util.Instantiator.FailureHandler; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.core.io.support.SpringFactoriesLoader; +import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver; +import org.springframework.core.io.support.SpringFactoriesLoader.FailureHandler; import org.springframework.core.log.LogMessage; import org.springframework.util.StringUtils; @@ -51,41 +51,30 @@ final class FailureAnalyzers implements SpringBootExceptionReporter { private static final Log logger = LogFactory.getLog(FailureAnalyzers.class); - private final ClassLoader classLoader; + private final SpringFactoriesLoader springFactoriesLoader; private final List analyzers; - FailureAnalyzers(ConfigurableApplicationContext context) { - this(context, SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, getClassLoader(context))); + public FailureAnalyzers(ConfigurableApplicationContext context) { + this(context, + SpringFactoriesLoader.forDefaultResourceLocation((context != null) ? context.getClassLoader() : null)); } - FailureAnalyzers(ConfigurableApplicationContext context, List classNames) { - this.classLoader = getClassLoader(context); - this.analyzers = loadFailureAnalyzers(classNames, context); + FailureAnalyzers(ConfigurableApplicationContext context, SpringFactoriesLoader springFactoriesLoader) { + this.springFactoriesLoader = springFactoriesLoader; + this.analyzers = loadFailureAnalyzers(context, this.springFactoriesLoader); } - private static ClassLoader getClassLoader(ConfigurableApplicationContext context) { - return (context != null) ? context.getClassLoader() : null; - } - - private List loadFailureAnalyzers(List classNames, - ConfigurableApplicationContext context) { - Instantiator instantiator = new Instantiator<>(FailureAnalyzer.class, - (availableParameters) -> { - if (context != null) { - availableParameters.add(BeanFactory.class, context.getBeanFactory()); - availableParameters.add(Environment.class, context.getEnvironment()); - } - }, new LoggingInstantiationFailureHandler()); - List analyzers = instantiator.instantiate(this.classLoader, classNames); - return handleAwareAnalyzers(analyzers, context); - } - - private List handleAwareAnalyzers(List analyzers, - ConfigurableApplicationContext context) { + private static List loadFailureAnalyzers(ConfigurableApplicationContext context, + SpringFactoriesLoader springFactoriesLoader) { + ArgumentResolver argumentResolver = (context != null) ? ArgumentResolver + .of(BeanFactory.class, context.getBeanFactory()).and(Environment.class, context.getEnvironment()) + : null; + List analyzers = springFactoriesLoader.load(FailureAnalyzer.class, argumentResolver, + FailureHandler.logging(logger)); List awareAnalyzers = analyzers.stream() .filter((analyzer) -> analyzer instanceof BeanFactoryAware || analyzer instanceof EnvironmentAware) - .collect(Collectors.toList()); + .toList(); if (!awareAnalyzers.isEmpty()) { String awareAnalyzerNames = StringUtils.collectionToCommaDelimitedString(awareAnalyzers.stream() .map((analyzer) -> analyzer.getClass().getName()).collect(Collectors.toList())); @@ -115,7 +104,7 @@ final class FailureAnalyzers implements SpringBootExceptionReporter { @Override public boolean reportException(Throwable failure) { FailureAnalysis analysis = analyze(failure, this.analyzers); - return report(analysis, this.classLoader); + return report(analysis); } private FailureAnalysis analyze(Throwable failure, List analyzers) { @@ -133,9 +122,8 @@ final class FailureAnalyzers implements SpringBootExceptionReporter { return null; } - private boolean report(FailureAnalysis analysis, ClassLoader classLoader) { - List reporters = SpringFactoriesLoader.loadFactories(FailureAnalysisReporter.class, - classLoader); + private boolean report(FailureAnalysis analysis) { + List reporters = this.springFactoriesLoader.load(FailureAnalysisReporter.class); if (analysis == null || reporters.isEmpty()) { return false; } @@ -145,13 +133,4 @@ final class FailureAnalyzers implements SpringBootExceptionReporter { return true; } - static class LoggingInstantiationFailureHandler implements FailureHandler { - - @Override - public void handleFailure(Class type, String implementationName, Throwable failure) { - logger.trace(LogMessage.format("Skipping %s: %s", implementationName, failure.getMessage())); - } - - } - } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java index 74362f49cd..56c2f180f0 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -16,8 +16,6 @@ package org.springframework.boot.env; -import org.apache.commons.logging.Log; - import org.springframework.boot.BootstrapContext; import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.ConfigurableBootstrapContext; @@ -42,8 +40,6 @@ import org.springframework.core.env.Environment; *
    • {@link DeferredLogFactory} - A factory that can be used to create loggers with * output deferred until the application has been fully prepared (allowing the environment * itself to configure logging levels).
    • - *
    • {@link Log} - A log with output deferred until the application has been fully - * prepared (allowing the environment itself to configure logging levels).
    • *
    • {@link ConfigurableBootstrapContext} - A bootstrap context that can be used to * store objects that may be expensive to create, or need to be shared * ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).
    • diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java index 366584760c..16edcfd989 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -56,7 +56,7 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica * {@link EnvironmentPostProcessor} classes loaded via {@code spring.factories}. */ public EnvironmentPostProcessorApplicationListener() { - this((classLoader) -> EnvironmentPostProcessorsFactory.fromSpringFactories(classLoader), new DeferredLogs()); + this((classLoader) -> EnvironmentPostProcessorsFactory.fromSpringFactories(classLoader)); } /** @@ -64,14 +64,21 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica * processors created by the given factory. * @param postProcessorsFactory the post processors factory */ - public EnvironmentPostProcessorApplicationListener(EnvironmentPostProcessorsFactory postProcessorsFactory) { - this((classloader) -> postProcessorsFactory, new DeferredLogs()); + private EnvironmentPostProcessorApplicationListener( + Function postProcessorsFactory) { + this.postProcessorsFactory = postProcessorsFactory; + this.deferredLogs = new DeferredLogs(); } - EnvironmentPostProcessorApplicationListener( - Function postProcessorsFactory, DeferredLogs deferredLogs) { - this.postProcessorsFactory = postProcessorsFactory; - this.deferredLogs = deferredLogs; + /** + * Factory method that creates an {@link EnvironmentPostProcessorApplicationListener} + * with a specific {@link EnvironmentPostProcessorsFactory}. + * @param postProcessorsFactory the environment post processor factory + * @return an {@link EnvironmentPostProcessorApplicationListener} instance + */ + public static EnvironmentPostProcessorApplicationListener with( + EnvironmentPostProcessorsFactory postProcessorsFactory) { + return new EnvironmentPostProcessorApplicationListener((classloader) -> postProcessorsFactory); } @Override diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java index 0f49d98a18..36f3d1bbbe 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -48,8 +48,8 @@ public interface EnvironmentPostProcessorsFactory { * @return an {@link EnvironmentPostProcessorsFactory} instance */ static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) { - return new ReflectionEnvironmentPostProcessorsFactory(classLoader, - SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader)); + return new SpringFactoriesEnvironmentPostProcessorsFactory( + SpringFactoriesLoader.forDefaultResourceLocation(classLoader)); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessor.java index a74443704d..b53c5af325 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessor.java @@ -19,6 +19,7 @@ package org.springframework.boot.env; import org.apache.commons.logging.Log; import org.springframework.boot.SpringApplication; +import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; @@ -39,10 +40,11 @@ public class RandomValuePropertySourceEnvironmentPostProcessor implements Enviro /** * Create a new {@link RandomValuePropertySourceEnvironmentPostProcessor} instance. - * @param logger the logger to use + * @param logFactory the log factory to use + * @since 3.0.0 */ - public RandomValuePropertySourceEnvironmentPostProcessor(Log logger) { - this.logger = logger; + public RandomValuePropertySourceEnvironmentPostProcessor(DeferredLogFactory logFactory) { + this.logger = logFactory.getLog(RandomValuePropertySourceEnvironmentPostProcessor.class); } @Override diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/SpringFactoriesEnvironmentPostProcessorsFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/SpringFactoriesEnvironmentPostProcessorsFactory.java new file mode 100644 index 0000000000..19879020a1 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/SpringFactoriesEnvironmentPostProcessorsFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2022 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.env; + +import java.util.List; + +import org.springframework.boot.BootstrapContext; +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.ConfigurableBootstrapContext; +import org.springframework.boot.logging.DeferredLogFactory; +import org.springframework.core.io.support.SpringFactoriesLoader; + +/** + * An {@link EnvironmentPostProcessorsFactory} that uses {@link SpringFactoriesLoader}. + * + * @author Andy Wilkinson + */ +class SpringFactoriesEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory { + + private final SpringFactoriesLoader loader; + + SpringFactoriesEnvironmentPostProcessorsFactory(SpringFactoriesLoader loader) { + this.loader = loader; + } + + @Override + public List getEnvironmentPostProcessors(DeferredLogFactory logFactory, + ConfigurableBootstrapContext bootstrapContext) { + SpringFactoriesLoader.ArgumentResolver argumentResolver = SpringFactoriesLoader.ArgumentResolver + .of(DeferredLogFactory.class, logFactory).and(ConfigurableBootstrapContext.class, bootstrapContext) + .and(BootstrapContext.class, bootstrapContext).and(BootstrapRegistry.class, bootstrapContext); + return this.loader.load(EnvironmentPostProcessor.class, argumentResolver); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.java index ecaa8f8f27..ae9d002f9b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.java @@ -33,7 +33,6 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.boot.util.Instantiator; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; @@ -151,10 +150,10 @@ public class DatabaseInitializationDependencyConfigurer implements ImportBeanDef } private List getDetectors(ConfigurableListableBeanFactory beanFactory, Class type) { - List names = SpringFactoriesLoader.loadFactoryNames(type, beanFactory.getBeanClassLoader()); - Instantiator instantiator = new Instantiator<>(type, - (availableParameters) -> availableParameters.add(Environment.class, this.environment)); - return instantiator.instantiate(beanFactory.getBeanClassLoader(), names); + SpringFactoriesLoader.ArgumentResolver argumentResolver = SpringFactoriesLoader.ArgumentResolver + .of(Environment.class, this.environment); + return SpringFactoriesLoader.forDefaultResourceLocation(beanFactory.getBeanClassLoader()).load(type, + argumentResolver); } private static BeanDefinition getBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/cloudfoundry/CloudFoundryVcapEnvironmentPostProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/cloudfoundry/CloudFoundryVcapEnvironmentPostProcessorTests.java index 2bd5762c04..debb9fb39a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/cloudfoundry/CloudFoundryVcapEnvironmentPostProcessorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/cloudfoundry/CloudFoundryVcapEnvironmentPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -16,7 +16,8 @@ package org.springframework.boot.cloud.cloudfoundry; -import org.apache.commons.logging.LogFactory; +import java.util.function.Supplier; + import org.junit.jupiter.api.Test; import org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor; @@ -35,7 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; class CloudFoundryVcapEnvironmentPostProcessorTests { private final CloudFoundryVcapEnvironmentPostProcessor initializer = new CloudFoundryVcapEnvironmentPostProcessor( - LogFactory.getLog(getClass())); + Supplier::get); private final ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java index 53fabcdacc..1e11c86e4b 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java @@ -39,6 +39,7 @@ import org.springframework.boot.context.properties.bind.BindException; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockPropertySource; @@ -86,9 +87,10 @@ class ConfigDataEnvironmentContributorsTests { this.environment = new MockEnvironment(); this.binder = Binder.get(this.environment); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, new DefaultResourceLoader(getClass().getClassLoader())); + this.binder, new DefaultResourceLoader(getClass().getClassLoader()), + SpringFactoriesLoader.forDefaultResourceLocation(getClass().getClassLoader())); ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, - getClass().getClassLoader()); + SpringFactoriesLoader.forDefaultResourceLocation()); this.importer = new ConfigDataImporter(this.logFactory, ConfigDataNotFoundAction.FAIL, resolvers, loaders); this.activationContext = new ConfigDataActivationContext(CloudPlatform.KUBERNETES, null); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java index 3bd6ad6a98..fdf0901319 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -16,13 +16,14 @@ package org.springframework.boot.context.config; +import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.function.Supplier; -import org.apache.commons.logging.Log; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.BootstrapContext; import org.springframework.boot.BootstrapRegistry; @@ -30,6 +31,7 @@ import org.springframework.boot.BootstrapRegistry.InstanceSupplier; import org.springframework.boot.ConfigurableBootstrapContext; import org.springframework.boot.DefaultBootstrapContext; import org.springframework.boot.logging.DeferredLogFactory; +import org.springframework.boot.testsupport.mock.MockSpringFactoriesLoader; import org.springframework.core.env.PropertySource; import org.springframework.mock.env.MockPropertySource; @@ -51,16 +53,15 @@ class ConfigDataLoadersTests { private ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class); - @Test - void createWhenLoaderHasLogParameterInjectsLog() { - new ConfigDataLoaders(this.logFactory, this.bootstrapContext, null, - Arrays.asList(LoggingConfigDataLoader.class.getName())); - } + @TempDir + private File tempDir; @Test void createWhenLoaderHasDeferredLogFactoryParameterInjectsDeferredLogFactory() { - ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, null, - Arrays.asList(DeferredLogFactoryConfigDataLoader.class.getName())); + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLoader.class, DeferredLogFactoryConfigDataLoader.class); + ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, + springFactoriesLoader); assertThat(loaders).extracting("loaders").asList() .satisfies(this::containsValidDeferredLogFactoryConfigDataLoader); } @@ -73,43 +74,52 @@ class ConfigDataLoadersTests { @Test void createWhenLoaderHasBootstrapParametersInjectsBootstrapContext() { - new ConfigDataLoaders(this.logFactory, this.bootstrapContext, null, - Arrays.asList(BootstrappingConfigDataLoader.class.getName())); + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLoader.class, BootstrappingConfigDataLoader.class); + new ConfigDataLoaders(this.logFactory, this.bootstrapContext, springFactoriesLoader); assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot"); } @Test void loadWhenSingleLoaderSupportsLocationReturnsLoadedConfigData() throws Exception { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLoader.class, TestConfigDataLoader.class); + ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, + springFactoriesLoader); TestConfigDataResource location = new TestConfigDataResource("test"); - ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, null, - Arrays.asList(TestConfigDataLoader.class.getName())); ConfigData loaded = loaders.load(this.context, location); assertThat(getLoader(loaded)).isInstanceOf(TestConfigDataLoader.class); } @Test void loadWhenMultipleLoadersSupportLocationThrowsException() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLoader.class, AnotherConfigDataLoader.class, TestConfigDataLoader.class); + ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, + springFactoriesLoader); TestConfigDataResource location = new TestConfigDataResource("test"); - ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, null, - Arrays.asList(LoggingConfigDataLoader.class.getName(), TestConfigDataLoader.class.getName())); assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location)) .withMessageContaining("Multiple loaders found for resource 'test'"); } @Test void loadWhenNoLoaderSupportsLocationThrowsException() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLoader.class, NonLoadableConfigDataLoader.class); + ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, + springFactoriesLoader); TestConfigDataResource location = new TestConfigDataResource("test"); - ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, null, - Arrays.asList(NonLoadableConfigDataLoader.class.getName())); assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location)) .withMessage("No loader found for resource 'test'"); } @Test void loadWhenGenericTypeDoesNotMatchSkipsLoader() throws Exception { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLoader.class, OtherConfigDataLoader.class, SpecificConfigDataLoader.class); + ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, + springFactoriesLoader); TestConfigDataResource location = new TestConfigDataResource("test"); - ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, null, - Arrays.asList(OtherConfigDataLoader.class.getName(), SpecificConfigDataLoader.class.getName())); ConfigData loaded = loaders.load(this.context, location); assertThat(getLoader(loaded)).isInstanceOf(SpecificConfigDataLoader.class); } @@ -145,19 +155,6 @@ class ConfigDataLoadersTests { } - static class LoggingConfigDataLoader implements ConfigDataLoader { - - LoggingConfigDataLoader(Log log) { - assertThat(log).isNotNull(); - } - - @Override - public ConfigData load(ConfigDataLoaderContext context, ConfigDataResource resource) throws IOException { - throw new AssertionError("Unexpected call"); - } - - } - static class DeferredLogFactoryConfigDataLoader implements ConfigDataLoader { private final DeferredLogFactory logFactory; @@ -205,6 +202,15 @@ class ConfigDataLoadersTests { } + static class AnotherConfigDataLoader implements ConfigDataLoader { + + @Override + public ConfigData load(ConfigDataLoaderContext context, ConfigDataResource resource) throws IOException { + return createConfigData(this, resource); + } + + } + static class NonLoadableConfigDataLoader extends TestConfigDataLoader { @Override diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java index 169101a8d6..173d74cea8 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -16,16 +16,14 @@ package org.springframework.boot.context.config; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; +import java.io.File; import java.util.Collections; import java.util.List; import java.util.function.Supplier; -import org.apache.commons.logging.Log; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -36,14 +34,13 @@ import org.springframework.boot.ConfigurableBootstrapContext; import org.springframework.boot.DefaultBootstrapContext; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.logging.DeferredLogFactory; +import org.springframework.boot.testsupport.mock.MockSpringFactoriesLoader; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.ResourceLoader; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** * Tests for {@link ConfigDataLocationResolvers}. @@ -67,23 +64,27 @@ class ConfigDataLocationResolversTests { @Mock private Profiles profiles; - private ResourceLoader resourceLoader = new DefaultResourceLoader(); + @TempDir + private File tempDir; @Test - void createWhenInjectingLogAndDeferredLogFactoryCreatesResolver() { + void createWhenInjectingDeferredLogFactoryCreatesResolver() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, TestLogResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, Collections.singletonList(TestLogResolver.class.getName())); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); assertThat(resolvers.getResolvers()).hasSize(1); assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(TestLogResolver.class); TestLogResolver resolver = (TestLogResolver) resolvers.getResolvers().get(0); assertThat(resolver.getDeferredLogFactory()).isSameAs(this.logFactory); - assertThat(resolver.getLog()).isNotNull(); } @Test void createWhenInjectingBinderCreatesResolver() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, TestBoundResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, Collections.singletonList(TestBoundResolver.class.getName())); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); assertThat(resolvers.getResolvers()).hasSize(1); assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(TestBoundResolver.class); assertThat(((TestBoundResolver) resolvers.getResolvers().get(0)).getBinder()).isSameAs(this.binder); @@ -91,35 +92,30 @@ class ConfigDataLocationResolversTests { @Test void createWhenNotInjectingBinderCreatesResolver() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, TestResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, Collections.singletonList(TestResolver.class.getName())); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); assertThat(resolvers.getResolvers()).hasSize(1); assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(TestResolver.class); } @Test void createWhenResolverHasBootstrapParametersInjectsBootstrapContext() { - new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, this.binder, this.resourceLoader, - Collections.singletonList(TestBootstrappingResolver.class.getName())); + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, TestBootstrappingResolver.class); + new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, this.binder, + new DefaultResourceLoader(), springFactoriesLoader); assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot"); } - @Test - void createWhenNameIsNotConfigDataLocationResolverThrowsException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, this.binder, - this.resourceLoader, Collections.singletonList(InputStream.class.getName()))) - .withMessageContaining("Unable to instantiate").havingCause().withMessageContaining("not assignable"); - } - @Test void createOrdersResolvers() { - List names = new ArrayList<>(); - names.add(TestResolver.class.getName()); - names.add(LowestTestResolver.class.getName()); - names.add(HighestTestResolver.class.getName()); + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, TestResolver.class, LowestTestResolver.class, + HighestTestResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, names); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(HighestTestResolver.class); assertThat(resolvers.getResolvers().get(1)).isExactlyInstanceOf(TestResolver.class); assertThat(resolvers.getResolvers().get(2)).isExactlyInstanceOf(LowestTestResolver.class); @@ -127,9 +123,11 @@ class ConfigDataLocationResolversTests { @Test void resolveResolvesUsingFirstSupportedResolver() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, LowestTestResolver.class, + HighestTestResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, - Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName())); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); ConfigDataLocation location = ConfigDataLocation.of("LowestTestResolver:test"); List resolved = resolvers.resolve(this.context, location, null); assertThat(resolved).hasSize(1); @@ -141,9 +139,11 @@ class ConfigDataLocationResolversTests { @Test void resolveWhenProfileMergesResolvedLocations() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, LowestTestResolver.class, + HighestTestResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, - Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName())); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); ConfigDataLocation location = ConfigDataLocation.of("LowestTestResolver:test"); List resolved = resolvers.resolve(this.context, location, this.profiles); assertThat(resolved).hasSize(2); @@ -159,9 +159,11 @@ class ConfigDataLocationResolversTests { @Test void resolveWhenNoResolverThrowsException() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, LowestTestResolver.class, + HighestTestResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, - Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName())); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); ConfigDataLocation location = ConfigDataLocation.of("Missing:test"); assertThatExceptionOfType(UnsupportedConfigDataLocationException.class) .isThrownBy(() -> resolvers.resolve(this.context, location, null)) @@ -170,8 +172,10 @@ class ConfigDataLocationResolversTests { @Test void resolveWhenOptional() { + MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); + springFactoriesLoader.add(ConfigDataLocationResolver.class, OptionalResourceTestResolver.class); ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, - this.binder, this.resourceLoader, Arrays.asList(OptionalResourceTestResolver.class.getName())); + this.binder, new DefaultResourceLoader(), springFactoriesLoader); ConfigDataLocation location = ConfigDataLocation.of("OptionalResourceTestResolver:test"); List resolved = resolvers.resolve(this.context, location, null); assertThat(resolved.get(0).getResource().isOptional()).isTrue(); @@ -185,7 +189,7 @@ class ConfigDataLocationResolversTests { this(false); } - TestResolver(boolean optionalResource) { + private TestResolver(boolean optionalResource) { this.optionalResource = optionalResource; } @@ -214,21 +218,14 @@ class ConfigDataLocationResolversTests { private final DeferredLogFactory deferredLogFactory; - private final Log log; - - TestLogResolver(DeferredLogFactory deferredLogFactory, Log log) { + TestLogResolver(DeferredLogFactory deferredLogFactory) { this.deferredLogFactory = deferredLogFactory; - this.log = log; } DeferredLogFactory getDeferredLogFactory() { return this.deferredLogFactory; } - Log getLog() { - return this.log; - } - } static class TestBoundResolver extends TestResolver { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java index 37e5679ac4..a9f61afaa9 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/StandardConfigDataLocationResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.env.PropertiesPropertySourceLoader; -import org.springframework.boot.logging.DeferredLog; +import org.springframework.boot.logging.DeferredLogs; import org.springframework.core.env.PropertySource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.DefaultResourceLoader; @@ -60,7 +60,7 @@ class StandardConfigDataLocationResolverTests { void setup() { this.environment = new MockEnvironment(); this.environmentBinder = Binder.get(this.environment); - this.resolver = new StandardConfigDataLocationResolver(new DeferredLog(), this.environmentBinder, + this.resolver = new StandardConfigDataLocationResolver(new DeferredLogs(), this.environmentBinder, this.resourceLoader); } @@ -115,8 +115,8 @@ class StandardConfigDataLocationResolverTests { void createWhenConfigNameHasWildcardThrowsException() { this.environment.setProperty("spring.config.name", "*/application"); assertThatIllegalStateException() - .isThrownBy( - () -> new StandardConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader)) + .isThrownBy(() -> new StandardConfigDataLocationResolver(new DeferredLogs(), this.environmentBinder, + this.resourceLoader)) .withMessageStartingWith("Config name '").withMessageEndingWith("' cannot contain '*'"); } @@ -131,7 +131,8 @@ class StandardConfigDataLocationResolverTests { void resolveWhenLocationIsWildcardDirectoriesRestrictsToOneLevelDeep() { ConfigDataLocation location = ConfigDataLocation.of("file:src/test/resources/config/*/"); this.environment.setProperty("spring.config.name", "testproperties"); - this.resolver = new StandardConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader); + this.resolver = new StandardConfigDataLocationResolver(new DeferredLogs(), this.environmentBinder, + this.resourceLoader); List locations = this.resolver.resolve(this.context, location); assertThat(locations.size()).isEqualTo(3); assertThat(locations).extracting(Object::toString) @@ -144,7 +145,8 @@ class StandardConfigDataLocationResolverTests { void resolveWhenLocationIsWildcardDirectoriesSortsAlphabeticallyBasedOnAbsolutePath() { ConfigDataLocation location = ConfigDataLocation.of("file:src/test/resources/config/*/"); this.environment.setProperty("spring.config.name", "testproperties"); - this.resolver = new StandardConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader); + this.resolver = new StandardConfigDataLocationResolver(new DeferredLogs(), this.environmentBinder, + this.resourceLoader); List locations = this.resolver.resolve(this.context, location); assertThat(locations).extracting(Object::toString).containsExactly( filePath("src", "test", "resources", "config", "0-empty", "testproperties.properties"), @@ -175,7 +177,7 @@ class StandardConfigDataLocationResolverTests { void resolveWhenLocationIsRelativeAndFileResolves() { this.environment.setProperty("spring.config.name", "other"); ConfigDataLocation location = ConfigDataLocation.of("other.properties"); - this.resolver = new StandardConfigDataLocationResolver(new DeferredLog(), this.environmentBinder, + this.resolver = new StandardConfigDataLocationResolver(new DeferredLogs(), this.environmentBinder, this.resourceLoader); StandardConfigDataReference parentReference = new StandardConfigDataReference( ConfigDataLocation.of("classpath:configdata/properties/application.properties"), null, @@ -194,7 +196,7 @@ class StandardConfigDataLocationResolverTests { void resolveWhenLocationIsRelativeAndDirectoryResolves() { this.environment.setProperty("spring.config.name", "testproperties"); ConfigDataLocation location = ConfigDataLocation.of("nested/3-third/"); - this.resolver = new StandardConfigDataLocationResolver(new DeferredLog(), this.environmentBinder, + this.resolver = new StandardConfigDataLocationResolver(new DeferredLogs(), this.environmentBinder, this.resourceLoader); StandardConfigDataReference parentReference = new StandardConfigDataReference( ConfigDataLocation.of("optional:classpath:configdata/"), null, "classpath:config/specific", null, diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java index f45931ccdd..2870a73190 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java @@ -16,14 +16,13 @@ package org.springframework.boot.diagnostics; -import java.util.Arrays; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.boot.testsupport.mock.MockSpringFactoriesLoader; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.context.EnvironmentAware; @@ -58,15 +57,14 @@ class FailureAnalyzersTests { @Test void analyzersAreLoadedAndCalled() { RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, BasicFailureAnalyzer.class.getName(), BasicFailureAnalyzer.class.getName()); + analyzeAndReport(failure, BasicFailureAnalyzer.class, StandardAwareFailureAnalyzer.class); then(failureAnalyzer).should(times(2)).analyze(failure); } @Test void analyzerIsConstructedWithBeanFactory(CapturedOutput output) { RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, BasicFailureAnalyzer.class.getName(), - BeanFactoryConstructorFailureAnalyzer.class.getName()); + analyzeAndReport(failure, BasicFailureAnalyzer.class, BeanFactoryConstructorFailureAnalyzer.class); then(failureAnalyzer).should(times(2)).analyze(failure); assertThat(output).doesNotContain("implement BeanFactoryAware or EnvironmentAware"); } @@ -74,8 +72,7 @@ class FailureAnalyzersTests { @Test void analyzerIsConstructedWithEnvironment(CapturedOutput output) { RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, BasicFailureAnalyzer.class.getName(), - EnvironmentConstructorFailureAnalyzer.class.getName()); + analyzeAndReport(failure, BasicFailureAnalyzer.class, EnvironmentConstructorFailureAnalyzer.class); then(failureAnalyzer).should(times(2)).analyze(failure); assertThat(output).doesNotContain("implement BeanFactoryAware or EnvironmentAware"); } @@ -83,7 +80,7 @@ class FailureAnalyzersTests { @Test void beanFactoryIsInjectedIntoBeanFactoryAwareFailureAnalyzers(CapturedOutput output) { RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, BasicFailureAnalyzer.class.getName(), StandardAwareFailureAnalyzer.class.getName()); + analyzeAndReport(failure, BasicFailureAnalyzer.class, StandardAwareFailureAnalyzer.class); then(failureAnalyzer).should().setBeanFactory(same(this.context.getBeanFactory())); assertThat(output).contains("FailureAnalyzers [" + StandardAwareFailureAnalyzer.class.getName() + "] implement BeanFactoryAware or EnvironmentAware."); @@ -92,41 +89,37 @@ class FailureAnalyzersTests { @Test void environmentIsInjectedIntoEnvironmentAwareFailureAnalyzers() { RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, BasicFailureAnalyzer.class.getName(), StandardAwareFailureAnalyzer.class.getName()); + analyzeAndReport(failure, BasicFailureAnalyzer.class, StandardAwareFailureAnalyzer.class); then(failureAnalyzer).should().setEnvironment(same(this.context.getEnvironment())); } @Test void analyzerThatFailsDuringInitializationDoesNotPreventOtherAnalyzersFromBeingCalled() { RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, BrokenInitializationFailureAnalyzer.class.getName(), - BasicFailureAnalyzer.class.getName()); + analyzeAndReport(failure, BrokenInitializationFailureAnalyzer.class, BasicFailureAnalyzer.class); then(failureAnalyzer).should().analyze(failure); } @Test void analyzerThatFailsDuringAnalysisDoesNotPreventOtherAnalyzersFromBeingCalled() { RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, BrokenAnalysisFailureAnalyzer.class.getName(), BasicFailureAnalyzer.class.getName()); + analyzeAndReport(failure, BrokenAnalysisFailureAnalyzer.class, BasicFailureAnalyzer.class); then(failureAnalyzer).should().analyze(failure); } - @Test - void createWithNullContextSkipsAwareAnalyzers() { - RuntimeException failure = new RuntimeException(); - analyzeAndReport(failure, (AnnotationConfigApplicationContext) null, BasicFailureAnalyzer.class.getName(), - BeanFactoryConstructorFailureAnalyzer.class.getName(), - EnvironmentConstructorFailureAnalyzer.class.getName(), StandardAwareFailureAnalyzer.class.getName()); - then(failureAnalyzer).should().analyze(failure); - } - - private void analyzeAndReport(Throwable failure, String... factoryNames) { - analyzeAndReport(failure, this.context, factoryNames); + @SafeVarargs + private void analyzeAndReport(Throwable failure, Class... failureAnalyzerClasses) { + analyzeAndReport(failure, this.context, failureAnalyzerClasses); } + @SafeVarargs private void analyzeAndReport(Throwable failure, AnnotationConfigApplicationContext context, - String... factoryNames) { - new FailureAnalyzers(context, Arrays.asList(factoryNames)).reportException(failure); + Class... failureAnalyzerClasses) { + MockSpringFactoriesLoader loader = new MockSpringFactoriesLoader(); + for (Class failureAnalyzerClass : failureAnalyzerClasses) { + loader.add(FailureAnalyzer.class, failureAnalyzerClass); + } + new FailureAnalyzers(context, loader).reportException(failure); } static class BasicFailureAnalyzer implements FailureAnalyzer { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java index cd17935818..ef21aea8da 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java @@ -17,7 +17,9 @@ package org.springframework.boot.env; import java.util.List; +import java.util.function.Function; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.BootstrapRegistry; @@ -32,6 +34,7 @@ import org.springframework.boot.logging.DeferredLogs; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.then; @@ -49,9 +52,15 @@ class EnvironmentPostProcessorApplicationListenerTests { private DefaultBootstrapContext bootstrapContext = spy(new DefaultBootstrapContext()); - private EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener( - (classLoader) -> EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class), - this.deferredLogs); + private EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener(); + + @BeforeEach + void setup() { + ReflectionTestUtils.setField(this.listener, "deferredLogs", this.deferredLogs); + ReflectionTestUtils.setField(this.listener, "postProcessorsFactory", + (Function) ( + classLoader) -> EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class)); + } @Test void createUsesSpringFactories() { @@ -61,8 +70,8 @@ class EnvironmentPostProcessorApplicationListenerTests { @Test void createWhenHasFactoryUsesFactory() { - EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener( - EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class)); + EnvironmentPostProcessorApplicationListener listener = EnvironmentPostProcessorApplicationListener + .with(EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class)); List postProcessors = listener.getEnvironmentPostProcessors(null, this.bootstrapContext); assertThat(postProcessors).hasSize(1); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessorTests.java index 154569254e..f90a20c2c1 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -16,7 +16,8 @@ package org.springframework.boot.env; -import org.apache.commons.logging.LogFactory; +import java.util.function.Supplier; + import org.junit.jupiter.api.Test; import org.springframework.boot.SpringApplication; @@ -34,7 +35,7 @@ import static org.mockito.Mockito.mock; class RandomValuePropertySourceEnvironmentPostProcessorTests { private RandomValuePropertySourceEnvironmentPostProcessor postProcessor = new RandomValuePropertySourceEnvironmentPostProcessor( - LogFactory.getLog(getClass())); + Supplier::get); @Test void getOrderIsBeforeConfigData() { diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index c244d9f1b3..72de8b3105 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -59,4 +59,5 @@ +