Skip to content

Commit 5088927

Browse files
committed
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
1 parent 6889d2a commit 5088927

File tree

7 files changed

+219
-30
lines changed

7 files changed

+219
-30
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@ List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory l
4848
* @return an {@link EnvironmentPostProcessorsFactory} instance
4949
*/
5050
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
51-
return new ReflectionEnvironmentPostProcessorsFactory(
51+
return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
5252
SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
5353
}
5454

@@ -69,7 +69,19 @@ static EnvironmentPostProcessorsFactory of(Class<?>... classes) {
6969
* @return an {@link EnvironmentPostProcessorsFactory} instance
7070
*/
7171
static EnvironmentPostProcessorsFactory of(String... classNames) {
72-
return new ReflectionEnvironmentPostProcessorsFactory(classNames);
72+
return of(null, classNames);
73+
}
74+
75+
/**
76+
* Return a {@link EnvironmentPostProcessorsFactory} that reflectively creates post
77+
* processors from the given class names.
78+
* @param classLoader the source class loader
79+
* @param classNames the post processor class names
80+
* @return an {@link EnvironmentPostProcessorsFactory} instance
81+
* @since 2.4.8
82+
*/
83+
static EnvironmentPostProcessorsFactory of(ClassLoader classLoader, String... classNames) {
84+
return new ReflectionEnvironmentPostProcessorsFactory(classLoader, classNames);
7385
}
7486

7587
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactory.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.env;
1818

19+
import java.util.ArrayList;
1920
import java.util.Arrays;
2021
import java.util.List;
2122

@@ -35,17 +36,24 @@
3536
*/
3637
class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory {
3738

39+
private final List<Class<?>> classes;
40+
41+
private ClassLoader classLoader;
42+
3843
private final List<String> classNames;
3944

4045
ReflectionEnvironmentPostProcessorsFactory(Class<?>... classes) {
41-
this(Arrays.stream(classes).map(Class::getName).toArray(String[]::new));
46+
this.classes = new ArrayList<>(Arrays.asList(classes));
47+
this.classNames = null;
4248
}
4349

44-
ReflectionEnvironmentPostProcessorsFactory(String... classNames) {
45-
this(Arrays.asList(classNames));
50+
ReflectionEnvironmentPostProcessorsFactory(ClassLoader classLoader, String... classNames) {
51+
this(classLoader, Arrays.asList(classNames));
4652
}
4753

48-
ReflectionEnvironmentPostProcessorsFactory(List<String> classNames) {
54+
ReflectionEnvironmentPostProcessorsFactory(ClassLoader classLoader, List<String> classNames) {
55+
this.classes = null;
56+
this.classLoader = classLoader;
4957
this.classNames = classNames;
5058
}
5159

@@ -60,7 +68,8 @@ public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFa
6068
parameters.add(BootstrapContext.class, bootstrapContext);
6169
parameters.add(BootstrapRegistry.class, bootstrapContext);
6270
});
63-
return instantiator.instantiate(this.classNames);
71+
return (this.classes != null) ? instantiator.instantiateTypes(this.classes)
72+
: instantiator.instantiate(this.classLoader, this.classNames);
6473
}
6574

6675
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/util/Instantiator.java

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package org.springframework.boot.util;
1818

1919
import java.lang.reflect.Constructor;
20-
import java.util.ArrayList;
2120
import java.util.Arrays;
2221
import java.util.Collection;
2322
import java.util.Collections;
@@ -27,6 +26,9 @@
2726
import java.util.Map;
2827
import java.util.function.Consumer;
2928
import java.util.function.Function;
29+
import java.util.function.Supplier;
30+
import java.util.stream.Collectors;
31+
import java.util.stream.Stream;
3032

3133
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
3234
import org.springframework.util.Assert;
@@ -85,22 +87,48 @@ public void add(Class<?> type, Function<Class<?>, Object> factory) {
8587
* @return a list of instantiated instances
8688
*/
8789
public List<T> instantiate(Collection<String> names) {
88-
List<T> instances = new ArrayList<>(names.size());
89-
for (String name : names) {
90-
instances.add(instantiate(name));
91-
}
90+
return instantiate((ClassLoader) null, names);
91+
}
92+
93+
/**
94+
* Instantiate the given set of class name, injecting constructor arguments as
95+
* necessary.
96+
* @param classLoader the source classloader
97+
* @param names the class names to instantiate
98+
* @return a list of instantiated instances
99+
* @since 2.4.8
100+
*/
101+
public List<T> instantiate(ClassLoader classLoader, Collection<String> names) {
102+
Assert.notNull(names, "Names must not be null");
103+
return instantiate(names.stream().map((name) -> TypeSupplier.forName(classLoader, name)));
104+
}
105+
106+
/**
107+
* Instantiate the given set of classes, injecting constructor arguments as necessary.
108+
* @param types the types to instantiate
109+
* @return a list of instantiated instances
110+
* @since 2.4.8
111+
*/
112+
public List<T> instantiateTypes(Collection<Class<?>> types) {
113+
Assert.notNull(types, "Types must not be null");
114+
return instantiate(types.stream().map((type) -> TypeSupplier.forType(type)));
115+
}
116+
117+
public List<T> instantiate(Stream<TypeSupplier> typeSuppliers) {
118+
List<T> instances = typeSuppliers.map(this::instantiate).collect(Collectors.toList());
92119
AnnotationAwareOrderComparator.sort(instances);
93120
return Collections.unmodifiableList(instances);
94121
}
95122

96-
private T instantiate(String name) {
123+
private T instantiate(TypeSupplier typeSupplier) {
97124
try {
98-
Class<?> type = ClassUtils.forName(name, null);
125+
Class<?> type = typeSupplier.get();
99126
Assert.isAssignable(this.type, type);
100127
return instantiate(type);
101128
}
102129
catch (Throwable ex) {
103-
throw new IllegalArgumentException("Unable to instantiate " + this.type.getName() + " [" + name + "]", ex);
130+
throw new IllegalArgumentException(
131+
"Unable to instantiate " + this.type.getName() + " [" + typeSupplier.getName() + "]", ex);
104132
}
105133
}
106134

@@ -160,4 +188,47 @@ public interface AvailableParameters {
160188

161189
}
162190

191+
/**
192+
* {@link Supplier} that provides a class type.
193+
*/
194+
private interface TypeSupplier {
195+
196+
String getName();
197+
198+
Class<?> get() throws ClassNotFoundException;
199+
200+
static TypeSupplier forName(ClassLoader classLoader, String name) {
201+
return new TypeSupplier() {
202+
203+
@Override
204+
public String getName() {
205+
return name;
206+
}
207+
208+
@Override
209+
public Class<?> get() throws ClassNotFoundException {
210+
return ClassUtils.forName(name, classLoader);
211+
}
212+
213+
};
214+
}
215+
216+
static TypeSupplier forType(Class<?> type) {
217+
return new TypeSupplier() {
218+
219+
@Override
220+
public String getName() {
221+
return type.getName();
222+
}
223+
224+
@Override
225+
public Class<?> get() throws ClassNotFoundException {
226+
return type;
227+
}
228+
229+
};
230+
}
231+
232+
}
233+
163234
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorsFactoryTests.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
import org.springframework.boot.DefaultBootstrapContext;
2525
import org.springframework.boot.SpringApplication;
2626
import org.springframework.boot.logging.DeferredLogFactory;
27+
import org.springframework.core.OverridingClassLoader;
2728
import org.springframework.core.env.ConfigurableEnvironment;
2829

2930
import static org.assertj.core.api.Assertions.assertThat;
@@ -67,6 +68,25 @@ void ofClassNamesReturnsFactory() {
6768
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
6869
}
6970

71+
@Test
72+
void ofClassNamesWithClassLoaderReturnsFactory() {
73+
OverridingClassLoader classLoader = new OverridingClassLoader(getClass().getClassLoader()) {
74+
75+
@Override
76+
protected boolean isEligibleForOverriding(String className) {
77+
return super.isEligibleForOverriding(className)
78+
&& className.equals(TestEnvironmentPostProcessor.class.getName());
79+
}
80+
81+
};
82+
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory.of(classLoader,
83+
TestEnvironmentPostProcessor.class.getName());
84+
List<EnvironmentPostProcessor> processors = factory.getEnvironmentPostProcessors(this.logFactory,
85+
this.bootstrapContext);
86+
assertThat(processors).hasSize(1);
87+
assertThat(processors.get(0).getClass().getClassLoader()).isSameAs(classLoader);
88+
}
89+
7090
static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor {
7191

7292
TestEnvironmentPostProcessor(DeferredLogFactory logFactory) {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactoryTests.java

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
2828
import org.springframework.boot.DefaultBootstrapContext;
2929
import org.springframework.boot.SpringApplication;
3030
import org.springframework.boot.logging.DeferredLogFactory;
31+
import org.springframework.core.OverridingClassLoader;
3132
import org.springframework.core.env.ConfigurableEnvironment;
3233

3334
import static org.assertj.core.api.Assertions.assertThat;
@@ -53,49 +54,65 @@ void createWithClassesCreatesFactory() {
5354

5455
@Test
5556
void createWithClassNamesArrayCreatesFactory() {
56-
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
57+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
5758
TestEnvironmentPostProcessor.class.getName());
5859
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
5960
}
6061

6162
@Test
6263
void createWithClassNamesListCreatesFactory() {
63-
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
64+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
6465
Arrays.asList(TestEnvironmentPostProcessor.class.getName()));
6566
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
6667
}
6768

69+
@Test
70+
void createWithClassNamesAndClassLoaderListCreatesFactory() {
71+
OverridingClassLoader classLoader = new OverridingClassLoader(getClass().getClassLoader()) {
72+
73+
@Override
74+
protected boolean isEligibleForOverriding(String className) {
75+
return super.isEligibleForOverriding(className)
76+
&& className.equals(TestEnvironmentPostProcessor.class.getName());
77+
}
78+
79+
};
80+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(classLoader,
81+
Arrays.asList(TestEnvironmentPostProcessor.class.getName()));
82+
assertThatFactory(factory).createsSinglePostProcessorWithClassLoader(classLoader);
83+
}
84+
6885
@Test
6986
void getEnvironmentPostProcessorsWhenHasDefaultConstructorCreatesPostProcessors() {
70-
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
87+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
7188
TestEnvironmentPostProcessor.class.getName());
7289
assertThatFactory(factory).createsSinglePostProcessor(TestEnvironmentPostProcessor.class);
7390
}
7491

7592
@Test
7693
void getEnvironmentPostProcessorsWhenHasLogFactoryConstructorCreatesPostProcessors() {
77-
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
94+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
7895
TestLogFactoryEnvironmentPostProcessor.class.getName());
7996
assertThatFactory(factory).createsSinglePostProcessor(TestLogFactoryEnvironmentPostProcessor.class);
8097
}
8198

8299
@Test
83100
void getEnvironmentPostProcessorsWhenHasLogConstructorCreatesPostProcessors() {
84-
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
101+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
85102
TestLogEnvironmentPostProcessor.class.getName());
86103
assertThatFactory(factory).createsSinglePostProcessor(TestLogEnvironmentPostProcessor.class);
87104
}
88105

89106
@Test
90107
void getEnvironmentPostProcessorsWhenHasBootstrapRegistryConstructorCreatesPostProcessors() {
91-
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
108+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
92109
TestBootstrapRegistryEnvironmentPostProcessor.class.getName());
93110
assertThatFactory(factory).createsSinglePostProcessor(TestBootstrapRegistryEnvironmentPostProcessor.class);
94111
}
95112

96113
@Test
97114
void getEnvironmentPostProcessorsWhenHasNoSuitableConstructorThrowsException() {
98-
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
115+
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(null,
99116
BadEnvironmentPostProcessor.class.getName());
100117
assertThatIllegalArgumentException()
101118
.isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapContext))
@@ -115,11 +132,21 @@ class EnvironmentPostProcessorsFactoryAssert {
115132
}
116133

117134
void createsSinglePostProcessor(Class<?> expectedType) {
135+
EnvironmentPostProcessor processor = getSingleProcessor();
136+
assertThat(processor).isInstanceOf(expectedType);
137+
}
138+
139+
void createsSinglePostProcessorWithClassLoader(OverridingClassLoader classLoader) {
140+
EnvironmentPostProcessor processor = getSingleProcessor();
141+
assertThat(processor.getClass().getClassLoader()).isSameAs(classLoader);
142+
}
143+
144+
private EnvironmentPostProcessor getSingleProcessor() {
118145
List<EnvironmentPostProcessor> processors = this.factory.getEnvironmentPostProcessors(
119146
ReflectionEnvironmentPostProcessorsFactoryTests.this.logFactory,
120147
ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapContext);
121148
assertThat(processors).hasSize(1);
122-
assertThat(processors.get(0)).isInstanceOf(expectedType);
149+
return processors.get(0);
123150
}
124151

125152
}

0 commit comments

Comments
 (0)