Pass ClassLoader to Instantiator
Update `Instantiator` so that it can accept a `ClassLoader` when creating instances and rework `EnvironmentPostProcessorsFactory` to use the new methods. Prior to this commit we would use the `ClassLoader` to get the class names from `SpringFactories` but not when actually creating the instances. Fixes gh-27043
This commit is contained in:
parent
6889d2adda
commit
5088927f80
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -48,7 +48,7 @@ public interface EnvironmentPostProcessorsFactory {
|
|||
* @return an {@link EnvironmentPostProcessorsFactory} instance
|
||||
*/
|
||||
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
|
||||
return new ReflectionEnvironmentPostProcessorsFactory(
|
||||
return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
|
||||
SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,19 @@ public interface EnvironmentPostProcessorsFactory {
|
|||
* @return an {@link EnvironmentPostProcessorsFactory} instance
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.env;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -35,17 +36,24 @@ import org.springframework.boot.util.Instantiator;
|
|||
*/
|
||||
class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory {
|
||||
|
||||
private final List<Class<?>> classes;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private final List<String> classNames;
|
||||
|
||||
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) {
|
||||
this(Arrays.asList(classNames));
|
||||
ReflectionEnvironmentPostProcessorsFactory(ClassLoader classLoader, String... 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;
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +68,8 @@ class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProce
|
|||
parameters.add(BootstrapContext.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.boot.util;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
|
@ -27,6 +26,9 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
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.util.Assert;
|
||||
|
|
@ -85,22 +87,48 @@ public class Instantiator<T> {
|
|||
* @return a list of instantiated instances
|
||||
*/
|
||||
public List<T> instantiate(Collection<String> names) {
|
||||
List<T> instances = new ArrayList<>(names.size());
|
||||
for (String name : names) {
|
||||
instances.add(instantiate(name));
|
||||
}
|
||||
return instantiate((ClassLoader) null, names);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return Collections.unmodifiableList(instances);
|
||||
}
|
||||
|
||||
private T instantiate(String name) {
|
||||
private T instantiate(TypeSupplier typeSupplier) {
|
||||
try {
|
||||
Class<?> type = ClassUtils.forName(name, null);
|
||||
Class<?> type = typeSupplier.get();
|
||||
Assert.isAssignable(this.type, type);
|
||||
return instantiate(type);
|
||||
}
|
||||
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> {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
@ -67,6 +68,25 @@ class EnvironmentPostProcessorsFactoryTests {
|
|||
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 {
|
||||
|
||||
TestEnvironmentPostProcessor(DeferredLogFactory logFactory) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -28,6 +28,7 @@ import org.springframework.boot.BootstrapRegistry;
|
|||
import org.springframework.boot.DefaultBootstrapContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
@ -53,49 +54,65 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
|
|||
|
||||
@Test
|
||||
void createWithClassNamesArrayCreatesFactory() {
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
|
||||
TestEnvironmentPostProcessor.class.getName());
|
||||
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithClassNamesListCreatesFactory() {
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
|
||||
Arrays.asList(TestEnvironmentPostProcessor.class.getName()));
|
||||
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
|
||||
void getEnvironmentPostProcessorsWhenHasDefaultConstructorCreatesPostProcessors() {
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
|
||||
TestEnvironmentPostProcessor.class.getName());
|
||||
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnvironmentPostProcessorsWhenHasLogFactoryConstructorCreatesPostProcessors() {
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
|
||||
TestLogFactoryEnvironmentPostProcessor.class.getName());
|
||||
assertThatFactory(factory).createsSinglePostProcessor(TestLogFactoryEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnvironmentPostProcessorsWhenHasLogConstructorCreatesPostProcessors() {
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
|
||||
TestLogEnvironmentPostProcessor.class.getName());
|
||||
assertThatFactory(factory).createsSinglePostProcessor(TestLogEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnvironmentPostProcessorsWhenHasBootstrapRegistryConstructorCreatesPostProcessors() {
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
|
||||
TestBootstrapRegistryEnvironmentPostProcessor.class.getName());
|
||||
assertThatFactory(factory).createsSinglePostProcessor(TestBootstrapRegistryEnvironmentPostProcessor.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnvironmentPostProcessorsWhenHasNoSuitableConstructorThrowsException() {
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
|
||||
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
|
||||
BadEnvironmentPostProcessor.class.getName());
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapContext))
|
||||
|
|
@ -115,11 +132,21 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
|
|||
}
|
||||
|
||||
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(
|
||||
ReflectionEnvironmentPostProcessorsFactoryTests.this.logFactory,
|
||||
ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapContext);
|
||||
assertThat(processors).hasSize(1);
|
||||
assertThat(processors.get(0)).isInstanceOf(expectedType);
|
||||
return processors.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
@ -76,6 +77,29 @@ class InstantiatorTests {
|
|||
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
|
||||
void createWhenWrongTypeThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
|
|
@ -96,7 +120,7 @@ class InstantiatorTests {
|
|||
});
|
||||
}
|
||||
|
||||
static class WithDefaultConstructor {
|
||||
static class WithDefaultConstructorSubclass extends WithDefaultConstructor {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue