Commit 641dfbdf authored by Phillip Webb's avatar Phillip Webb

Merge branch '2.4.x' into 2.5.x

Fixes gh-27072
parents d079db1d 5088927f
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -48,7 +48,7 @@ public interface EnvironmentPostProcessorsFactory { ...@@ -48,7 +48,7 @@ public interface EnvironmentPostProcessorsFactory {
* @return an {@link EnvironmentPostProcessorsFactory} instance * @return an {@link EnvironmentPostProcessorsFactory} instance
*/ */
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) { static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
return new ReflectionEnvironmentPostProcessorsFactory( return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader)); SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
} }
...@@ -69,7 +69,19 @@ public interface EnvironmentPostProcessorsFactory { ...@@ -69,7 +69,19 @@ public interface EnvironmentPostProcessorsFactory {
* @return an {@link EnvironmentPostProcessorsFactory} instance * @return an {@link EnvironmentPostProcessorsFactory} instance
*/ */
static EnvironmentPostProcessorsFactory of(String... classNames) { static EnvironmentPostProcessorsFactory of(String... classNames) {
return new ReflectionEnvironmentPostProcessorsFactory(classNames); return of(null, classNames);
}
/**
* Return a {@link EnvironmentPostProcessorsFactory} that reflectively creates post
* processors from the given class names.
* @param classLoader the source class loader
* @param classNames the post processor class names
* @return an {@link EnvironmentPostProcessorsFactory} instance
* @since 2.4.8
*/
static EnvironmentPostProcessorsFactory of(ClassLoader classLoader, String... classNames) {
return new ReflectionEnvironmentPostProcessorsFactory(classLoader, classNames);
} }
} }
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.env; package org.springframework.boot.env;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
...@@ -35,17 +36,24 @@ import org.springframework.boot.util.Instantiator; ...@@ -35,17 +36,24 @@ import org.springframework.boot.util.Instantiator;
*/ */
class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory { class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory {
private final List<Class<?>> classes;
private ClassLoader classLoader;
private final List<String> classNames; private final List<String> classNames;
ReflectionEnvironmentPostProcessorsFactory(Class<?>... classes) { ReflectionEnvironmentPostProcessorsFactory(Class<?>... classes) {
this(Arrays.stream(classes).map(Class::getName).toArray(String[]::new)); this.classes = new ArrayList<>(Arrays.asList(classes));
this.classNames = null;
} }
ReflectionEnvironmentPostProcessorsFactory(String... classNames) { ReflectionEnvironmentPostProcessorsFactory(ClassLoader classLoader, String... classNames) {
this(Arrays.asList(classNames)); this(classLoader, Arrays.asList(classNames));
} }
ReflectionEnvironmentPostProcessorsFactory(List<String> classNames) { ReflectionEnvironmentPostProcessorsFactory(ClassLoader classLoader, List<String> classNames) {
this.classes = null;
this.classLoader = classLoader;
this.classNames = classNames; this.classNames = classNames;
} }
...@@ -60,7 +68,8 @@ class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProce ...@@ -60,7 +68,8 @@ class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProce
parameters.add(BootstrapContext.class, bootstrapContext); parameters.add(BootstrapContext.class, bootstrapContext);
parameters.add(BootstrapRegistry.class, bootstrapContext); parameters.add(BootstrapRegistry.class, bootstrapContext);
}); });
return instantiator.instantiate(this.classNames); return (this.classes != null) ? instantiator.instantiateTypes(this.classes)
: instantiator.instantiate(this.classLoader, this.classNames);
} }
} }
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package org.springframework.boot.util; package org.springframework.boot.util;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
...@@ -27,6 +26,9 @@ import java.util.List; ...@@ -27,6 +26,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -85,22 +87,48 @@ public class Instantiator<T> { ...@@ -85,22 +87,48 @@ public class Instantiator<T> {
* @return a list of instantiated instances * @return a list of instantiated instances
*/ */
public List<T> instantiate(Collection<String> names) { public List<T> instantiate(Collection<String> names) {
List<T> instances = new ArrayList<>(names.size()); return instantiate((ClassLoader) null, names);
for (String name : names) { }
instances.add(instantiate(name));
} /**
* Instantiate the given set of class name, injecting constructor arguments as
* necessary.
* @param classLoader the source classloader
* @param names the class names to instantiate
* @return a list of instantiated instances
* @since 2.4.8
*/
public List<T> instantiate(ClassLoader classLoader, Collection<String> names) {
Assert.notNull(names, "Names must not be null");
return instantiate(names.stream().map((name) -> TypeSupplier.forName(classLoader, name)));
}
/**
* Instantiate the given set of classes, injecting constructor arguments as necessary.
* @param types the types to instantiate
* @return a list of instantiated instances
* @since 2.4.8
*/
public List<T> instantiateTypes(Collection<Class<?>> types) {
Assert.notNull(types, "Types must not be null");
return instantiate(types.stream().map((type) -> TypeSupplier.forType(type)));
}
public List<T> instantiate(Stream<TypeSupplier> typeSuppliers) {
List<T> instances = typeSuppliers.map(this::instantiate).collect(Collectors.toList());
AnnotationAwareOrderComparator.sort(instances); AnnotationAwareOrderComparator.sort(instances);
return Collections.unmodifiableList(instances); return Collections.unmodifiableList(instances);
} }
private T instantiate(String name) { private T instantiate(TypeSupplier typeSupplier) {
try { try {
Class<?> type = ClassUtils.forName(name, null); Class<?> type = typeSupplier.get();
Assert.isAssignable(this.type, type); Assert.isAssignable(this.type, type);
return instantiate(type); return instantiate(type);
} }
catch (Throwable ex) { catch (Throwable ex) {
throw new IllegalArgumentException("Unable to instantiate " + this.type.getName() + " [" + name + "]", ex); throw new IllegalArgumentException(
"Unable to instantiate " + this.type.getName() + " [" + typeSupplier.getName() + "]", ex);
} }
} }
...@@ -160,4 +188,47 @@ public class Instantiator<T> { ...@@ -160,4 +188,47 @@ public class Instantiator<T> {
} }
/**
* {@link Supplier} that provides a class type.
*/
private interface TypeSupplier {
String getName();
Class<?> get() throws ClassNotFoundException;
static TypeSupplier forName(ClassLoader classLoader, String name) {
return new TypeSupplier() {
@Override
public String getName() {
return name;
}
@Override
public Class<?> get() throws ClassNotFoundException {
return ClassUtils.forName(name, classLoader);
}
};
}
static TypeSupplier forType(Class<?> type) {
return new TypeSupplier() {
@Override
public String getName() {
return type.getName();
}
@Override
public Class<?> get() throws ClassNotFoundException {
return type;
}
};
}
}
} }
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test; ...@@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.DefaultBootstrapContext; import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -67,6 +68,25 @@ class EnvironmentPostProcessorsFactoryTests { ...@@ -67,6 +68,25 @@ class EnvironmentPostProcessorsFactoryTests {
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class); assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
} }
@Test
void ofClassNamesWithClassLoaderReturnsFactory() {
OverridingClassLoader classLoader = new OverridingClassLoader(getClass().getClassLoader()) {
@Override
protected boolean isEligibleForOverriding(String className) {
return super.isEligibleForOverriding(className)
&& className.equals(TestEnvironmentPostProcessor.class.getName());
}
};
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory.of(classLoader,
TestEnvironmentPostProcessor.class.getName());
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory,
this.bootstrapContext);
assertThat(processors).hasSize(1);
assertThat(processors.get(0).getClass().getClassLoader()).isSameAs(classLoader);
}
static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor { static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor {
TestEnvironmentPostProcessor(DeferredLogFactory logFactory) { TestEnvironmentPostProcessor(DeferredLogFactory logFactory) {
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -28,6 +28,7 @@ import org.springframework.boot.BootstrapRegistry; ...@@ -28,6 +28,7 @@ import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.DefaultBootstrapContext; import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -53,49 +54,65 @@ class ReflectionEnvironmentPostProcessorsFactoryTests { ...@@ -53,49 +54,65 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
@Test @Test
void createWithClassNamesArrayCreatesFactory() { void createWithClassNamesArrayCreatesFactory() {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
TestEnvironmentPostProcessor.class.getName()); TestEnvironmentPostProcessor.class.getName());
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class); assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
} }
@Test @Test
void createWithClassNamesListCreatesFactory() { void createWithClassNamesListCreatesFactory() {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
Arrays.asList(TestEnvironmentPostProcessor.class.getName())); Arrays.asList(TestEnvironmentPostProcessor.class.getName()));
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class); assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
} }
@Test
void createWithClassNamesAndClassLoaderListCreatesFactory() {
OverridingClassLoader classLoader = new OverridingClassLoader(getClass().getClassLoader()) {
@Override
protected boolean isEligibleForOverriding(String className) {
return super.isEligibleForOverriding(className)
&& className.equals(TestEnvironmentPostProcessor.class.getName());
}
};
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(classLoader,
Arrays.asList(TestEnvironmentPostProcessor.class.getName()));
assertThatFactory(factory).createsSinglePostProcessorWithClassLoader(classLoader);
}
@Test @Test
void getEnvironmentPostProcessorsWhenHasDefaultConstructorCreatesPostProcessors() { void getEnvironmentPostProcessorsWhenHasDefaultConstructorCreatesPostProcessors() {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
TestEnvironmentPostProcessor.class.getName()); TestEnvironmentPostProcessor.class.getName());
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class); assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
} }
@Test @Test
void getEnvironmentPostProcessorsWhenHasLogFactoryConstructorCreatesPostProcessors() { void getEnvironmentPostProcessorsWhenHasLogFactoryConstructorCreatesPostProcessors() {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
TestLogFactoryEnvironmentPostProcessor.class.getName()); TestLogFactoryEnvironmentPostProcessor.class.getName());
assertThatFactory(factory).createsSinglePostProcessor(TestLogFactoryEnvironmentPostProcessor.class); assertThatFactory(factory).createsSinglePostProcessor(TestLogFactoryEnvironmentPostProcessor.class);
} }
@Test @Test
void getEnvironmentPostProcessorsWhenHasLogConstructorCreatesPostProcessors() { void getEnvironmentPostProcessorsWhenHasLogConstructorCreatesPostProcessors() {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
TestLogEnvironmentPostProcessor.class.getName()); TestLogEnvironmentPostProcessor.class.getName());
assertThatFactory(factory).createsSinglePostProcessor(TestLogEnvironmentPostProcessor.class); assertThatFactory(factory).createsSinglePostProcessor(TestLogEnvironmentPostProcessor.class);
} }
@Test @Test
void getEnvironmentPostProcessorsWhenHasBootstrapRegistryConstructorCreatesPostProcessors() { void getEnvironmentPostProcessorsWhenHasBootstrapRegistryConstructorCreatesPostProcessors() {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
TestBootstrapRegistryEnvironmentPostProcessor.class.getName()); TestBootstrapRegistryEnvironmentPostProcessor.class.getName());
assertThatFactory(factory).createsSinglePostProcessor(TestBootstrapRegistryEnvironmentPostProcessor.class); assertThatFactory(factory).createsSinglePostProcessor(TestBootstrapRegistryEnvironmentPostProcessor.class);
} }
@Test @Test
void getEnvironmentPostProcessorsWhenHasNoSuitableConstructorThrowsException() { void getEnvironmentPostProcessorsWhenHasNoSuitableConstructorThrowsException() {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory( ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
BadEnvironmentPostProcessor.class.getName()); BadEnvironmentPostProcessor.class.getName());
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapContext)) .isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapContext))
...@@ -115,11 +132,21 @@ class ReflectionEnvironmentPostProcessorsFactoryTests { ...@@ -115,11 +132,21 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
} }
void createsSinglePostProcessor(Class<?> expectedType) { void createsSinglePostProcessor(Class<?> expectedType) {
EnvironmentPostProcessor processor = getSingleProcessor();
assertThat(processor).isInstanceOf(expectedType);
}
void createsSinglePostProcessorWithClassLoader(OverridingClassLoader classLoader) {
EnvironmentPostProcessor processor = getSingleProcessor();
assertThat(processor.getClass().getClassLoader()).isSameAs(classLoader);
}
private EnvironmentPostProcessor getSingleProcessor() {
List<EnvironmentPostProcessor> processors = this.factory.getEnvironmentPostProcessors( List<EnvironmentPostProcessor> processors = this.factory.getEnvironmentPostProcessors(
ReflectionEnvironmentPostProcessorsFactoryTests.this.logFactory, ReflectionEnvironmentPostProcessorsFactoryTests.this.logFactory,
ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapContext); ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapContext);
assertThat(processors).hasSize(1); assertThat(processors).hasSize(1);
assertThat(processors.get(0)).isInstanceOf(expectedType); return processors.get(0);
} }
} }
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -23,6 +23,7 @@ import java.util.List; ...@@ -23,6 +23,7 @@ import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -76,6 +77,29 @@ class InstantiatorTests { ...@@ -76,6 +77,29 @@ class InstantiatorTests {
assertThat(instance.getParamC()).isEqualTo(this.paramC); assertThat(instance.getParamC()).isEqualTo(this.paramC);
} }
@Test
void instantiateTypesCreatesInstance() {
WithDefaultConstructor instance = createInstantiator(WithDefaultConstructor.class)
.instantiateTypes(Collections.singleton(WithDefaultConstructor.class)).get(0);
assertThat(instance).isInstanceOf(WithDefaultConstructor.class);
}
@Test
void instantiateWithClassLoaderCreatesInstance() {
OverridingClassLoader classLoader = new OverridingClassLoader(getClass().getClassLoader()) {
@Override
protected boolean isEligibleForOverriding(String className) {
return super.isEligibleForOverriding(className)
&& className.equals(WithDefaultConstructorSubclass.class.getName());
}
};
WithDefaultConstructor instance = createInstantiator(WithDefaultConstructor.class)
.instantiate(classLoader, Collections.singleton(WithDefaultConstructorSubclass.class.getName())).get(0);
assertThat(instance.getClass().getClassLoader()).isSameAs(classLoader);
}
@Test @Test
void createWhenWrongTypeThrowsException() { void createWhenWrongTypeThrowsException() {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
...@@ -96,7 +120,7 @@ class InstantiatorTests { ...@@ -96,7 +120,7 @@ class InstantiatorTests {
}); });
} }
static class WithDefaultConstructor { static class WithDefaultConstructorSubclass extends WithDefaultConstructor {
} }
......
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.util;
/**
* Simple public class with a default constructor.
*
* @author Phillip Webb
*/
public class WithDefaultConstructor {
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment