Browse Source
Update `EnvironmentPostProcessorApplicationListener` so that it can either use values from `spring.factories` or use a factory interface. Closes gh-22529pull/22535/head
7 changed files with 537 additions and 86 deletions
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.Collections; |
||||
import java.util.List; |
||||
import java.util.function.Function; |
||||
|
||||
import org.springframework.boot.logging.DeferredLogFactory; |
||||
import org.springframework.core.io.support.SpringFactoriesLoader; |
||||
|
||||
/** |
||||
* Factory interface used by the {@link EnvironmentPostProcessorApplicationListener} to |
||||
* create the {@link EnvironmentPostProcessor} instances. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 2.4.0 |
||||
*/ |
||||
@FunctionalInterface |
||||
public interface EnvironmentPostProcessorsFactory { |
||||
|
||||
/** |
||||
* Create all requested {@link EnvironmentPostProcessor} instances. |
||||
* @param logFactory a deferred log factory |
||||
* @return the post processor instances |
||||
*/ |
||||
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory); |
||||
|
||||
/** |
||||
* Return a {@link EnvironmentPostProcessorsFactory} backed by |
||||
* {@code spring.factories}. |
||||
* @param classLoader the source class loader |
||||
* @return an {@link EnvironmentPostProcessorsFactory} instance |
||||
*/ |
||||
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) { |
||||
return new ReflectionEnvironmentPostProcessorsFactory( |
||||
SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader)); |
||||
} |
||||
|
||||
/** |
||||
* Return a {@link EnvironmentPostProcessorsFactory} that reflectively creates post |
||||
* processors from the given classes. |
||||
* @param classes the post processor classes |
||||
* @return an {@link EnvironmentPostProcessorsFactory} instance |
||||
*/ |
||||
static EnvironmentPostProcessorsFactory of(Class<?>... classes) { |
||||
return new ReflectionEnvironmentPostProcessorsFactory(classes); |
||||
} |
||||
|
||||
/** |
||||
* Return a {@link EnvironmentPostProcessorsFactory} that reflectively creates post |
||||
* processors from the given class names. |
||||
* @param classNames the post processor class names |
||||
* @return an {@link EnvironmentPostProcessorsFactory} instance |
||||
*/ |
||||
static EnvironmentPostProcessorsFactory of(String... classNames) { |
||||
return new ReflectionEnvironmentPostProcessorsFactory(classNames); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link EnvironmentPostProcessorsFactory} containing only a single post |
||||
* processor. |
||||
* @param factory the factory used to create the post processor |
||||
* @return an {@link EnvironmentPostProcessorsFactory} instance |
||||
*/ |
||||
static EnvironmentPostProcessorsFactory singleton(Function<DeferredLogFactory, EnvironmentPostProcessor> factory) { |
||||
return (logFactory) -> Collections.singletonList(factory.apply(logFactory)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.lang.reflect.Constructor; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
|
||||
import org.springframework.boot.logging.DeferredLogFactory; |
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
/** |
||||
* {@link EnvironmentPostProcessorsFactory} implementation that uses reflection to create |
||||
* instances. |
||||
* |
||||
* @author Phillip Webb |
||||
*/ |
||||
class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory { |
||||
|
||||
private final List<String> classNames; |
||||
|
||||
ReflectionEnvironmentPostProcessorsFactory(Class<?>... classes) { |
||||
this(Arrays.stream(classes).map(Class::getName).toArray(String[]::new)); |
||||
} |
||||
|
||||
ReflectionEnvironmentPostProcessorsFactory(String... classNames) { |
||||
this(Arrays.asList(classNames)); |
||||
} |
||||
|
||||
ReflectionEnvironmentPostProcessorsFactory(List<String> classNames) { |
||||
this.classNames = classNames; |
||||
} |
||||
|
||||
@Override |
||||
public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory) { |
||||
List<EnvironmentPostProcessor> postProcessors = new ArrayList<>(this.classNames.size()); |
||||
for (String className : this.classNames) { |
||||
try { |
||||
postProcessors.add(getEnvironmentPostProcessor(className, logFactory)); |
||||
} |
||||
catch (Throwable ex) { |
||||
throw new IllegalArgumentException("Unable to instantiate factory class [" + className |
||||
+ "] for factory type [" + EnvironmentPostProcessor.class.getName() + "]", ex); |
||||
} |
||||
} |
||||
AnnotationAwareOrderComparator.sort(postProcessors); |
||||
return postProcessors; |
||||
} |
||||
|
||||
private EnvironmentPostProcessor getEnvironmentPostProcessor(String className, DeferredLogFactory logFactory) |
||||
throws Exception { |
||||
Class<?> type = ClassUtils.forName(className, getClass().getClassLoader()); |
||||
Assert.isAssignable(EnvironmentPostProcessor.class, type); |
||||
Constructor<?>[] constructors = type.getDeclaredConstructors(); |
||||
for (Constructor<?> constructor : constructors) { |
||||
if (constructor.getParameterCount() == 1) { |
||||
Class<?> cls = constructor.getParameterTypes()[0]; |
||||
if (DeferredLogFactory.class.isAssignableFrom(cls)) { |
||||
return newInstance(constructor, logFactory); |
||||
} |
||||
if (Log.class.isAssignableFrom(cls)) { |
||||
return newInstance(constructor, logFactory.getLog(type)); |
||||
} |
||||
} |
||||
} |
||||
return (EnvironmentPostProcessor) ReflectionUtils.accessibleConstructor(type).newInstance(); |
||||
} |
||||
|
||||
private EnvironmentPostProcessor newInstance(Constructor<?> constructor, Object... initargs) throws Exception { |
||||
ReflectionUtils.makeAccessible(constructor); |
||||
return (EnvironmentPostProcessor) constructor.newInstance(initargs); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* Copyright 2012-2020 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 java.util.function.Supplier; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.logging.DeferredLogFactory; |
||||
import org.springframework.core.env.ConfigurableEnvironment; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link EnvironmentPostProcessorsFactory}. |
||||
* |
||||
* @author Phillip Webb |
||||
*/ |
||||
class EnvironmentPostProcessorsFactoryTests { |
||||
|
||||
private final DeferredLogFactory logFactory = Supplier::get; |
||||
|
||||
@Test |
||||
void fromSpringFactoriesReturnsFactory() { |
||||
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory.fromSpringFactories(null); |
||||
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory); |
||||
assertThat(processors).hasSizeGreaterThan(1); |
||||
} |
||||
|
||||
@Test |
||||
void ofClassesReturnsFactory() { |
||||
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory |
||||
.of(TestEnvironmentPostProcessor.class); |
||||
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory); |
||||
assertThat(processors).hasSize(1); |
||||
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void ofClassNamesReturnsFactory() { |
||||
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory |
||||
.of(TestEnvironmentPostProcessor.class.getName()); |
||||
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory); |
||||
assertThat(processors).hasSize(1); |
||||
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void singletonReturnsFactory() { |
||||
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory |
||||
.singleton(TestEnvironmentPostProcessor::new); |
||||
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory); |
||||
assertThat(processors).hasSize(1); |
||||
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor { |
||||
|
||||
TestEnvironmentPostProcessor(DeferredLogFactory logFactory) { |
||||
} |
||||
|
||||
@Override |
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,157 @@
@@ -0,0 +1,157 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.io.InputStream; |
||||
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.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.logging.DeferredLogFactory; |
||||
import org.springframework.core.env.ConfigurableEnvironment; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
|
||||
/** |
||||
* Tests for {@link ReflectionEnvironmentPostProcessorsFactory}. |
||||
* |
||||
* @author Phillip Webb |
||||
*/ |
||||
class ReflectionEnvironmentPostProcessorsFactoryTests { |
||||
|
||||
private final DeferredLogFactory logFactory = Supplier::get; |
||||
|
||||
@Test |
||||
void createWithClassesCreatesFactory() { |
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( |
||||
TestEnvironmentPostProcessor.class); |
||||
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void createWithClassNamesArrayCreatesFactory() { |
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( |
||||
TestEnvironmentPostProcessor.class.getName()); |
||||
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void createWithClassNamesListCreatesFactory() { |
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( |
||||
Arrays.asList(TestEnvironmentPostProcessor.class.getName())); |
||||
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void getEnvironmentPostProcessorsWhenHasDefaultConstructorCreatesPostProcessors() { |
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( |
||||
TestEnvironmentPostProcessor.class.getName()); |
||||
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void getEnvironmentPostProcessorsWhenHasLogFactoryConstructorCreatesPostProcessors() { |
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( |
||||
TestLogFactoryEnvironmentPostProcessor.class.getName()); |
||||
assertThatFactory(factory).createsSinglePostProcessor(TestLogFactoryEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void getEnvironmentPostProcessorsWhenHasLogConstructorCreatesPostProcessors() { |
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( |
||||
TestLogEnvironmentPostProcessor.class.getName()); |
||||
assertThatFactory(factory).createsSinglePostProcessor(TestLogEnvironmentPostProcessor.class); |
||||
} |
||||
|
||||
@Test |
||||
void getEnvironmentPostProcessorsWhenHasNoSuitableConstructorThrowsException() { |
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( |
||||
BadEnvironmentPostProcessor.class.getName()); |
||||
assertThatIllegalArgumentException().isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory)) |
||||
.withMessageContaining("Unable to instantiate"); |
||||
} |
||||
|
||||
private EnvironmentPostProcessorsFactoryAssert assertThatFactory(EnvironmentPostProcessorsFactory factory) { |
||||
return new EnvironmentPostProcessorsFactoryAssert(factory); |
||||
} |
||||
|
||||
class EnvironmentPostProcessorsFactoryAssert { |
||||
|
||||
private EnvironmentPostProcessorsFactory factory; |
||||
|
||||
EnvironmentPostProcessorsFactoryAssert(EnvironmentPostProcessorsFactory factory) { |
||||
this.factory = factory; |
||||
} |
||||
|
||||
void createsSinglePostProcessor(Class<?> expectedType) { |
||||
List<EnvironmentPostProcessor> processors = this.factory |
||||
.getEnvironmentPostProcessors(ReflectionEnvironmentPostProcessorsFactoryTests.this.logFactory); |
||||
assertThat(processors).hasSize(1); |
||||
assertThat(processors.get(0)).isInstanceOf(expectedType); |
||||
} |
||||
|
||||
} |
||||
|
||||
static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor { |
||||
|
||||
@Override |
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { |
||||
} |
||||
|
||||
} |
||||
|
||||
static class TestLogFactoryEnvironmentPostProcessor implements EnvironmentPostProcessor { |
||||
|
||||
TestLogFactoryEnvironmentPostProcessor(DeferredLogFactory logFactory) { |
||||
assertThat(logFactory).isNotNull(); |
||||
} |
||||
|
||||
@Override |
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { |
||||
} |
||||
|
||||
} |
||||
|
||||
static class TestLogEnvironmentPostProcessor implements EnvironmentPostProcessor { |
||||
|
||||
TestLogEnvironmentPostProcessor(Log log) { |
||||
assertThat(log).isNotNull(); |
||||
} |
||||
|
||||
@Override |
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { |
||||
} |
||||
|
||||
} |
||||
|
||||
static class BadEnvironmentPostProcessor implements EnvironmentPostProcessor { |
||||
|
||||
BadEnvironmentPostProcessor(InputStream inputStream) { |
||||
} |
||||
|
||||
@Override |
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue