Skip to content

Commit b32697b

Browse files
committed
Add support to @ClassPathExclusions for excluding packages
Closes gh-36120
1 parent cff26d9 commit b32697b

File tree

3 files changed

+66
-8
lines changed

3 files changed

+66
-8
lines changed

spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ClassPathExclusions.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 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.
@@ -25,6 +25,8 @@
2525

2626
import org.junit.jupiter.api.extension.ExtendWith;
2727

28+
import org.springframework.core.annotation.AliasFor;
29+
2830
/**
2931
* Annotation used to exclude entries from the classpath.
3032
*
@@ -37,13 +39,34 @@
3739
@ExtendWith(ModifiedClassPathExtension.class)
3840
public @interface ClassPathExclusions {
3941

42+
/**
43+
* Alias for {@code files}.
44+
* <p>
45+
* One or more Ant-style patterns that identify entries to be excluded from the class
46+
* path. Matching is performed against an entry's {@link File#getName() file name}.
47+
* For example, to exclude Hibernate Validator from the classpath,
48+
* {@code "hibernate-validator-*.jar"} can be used.
49+
* @return the exclusion patterns
50+
*/
51+
@AliasFor("files")
52+
String[] value() default {};
53+
4054
/**
4155
* One or more Ant-style patterns that identify entries to be excluded from the class
4256
* path. Matching is performed against an entry's {@link File#getName() file name}.
4357
* For example, to exclude Hibernate Validator from the classpath,
4458
* {@code "hibernate-validator-*.jar"} can be used.
4559
* @return the exclusion patterns
60+
* @since 3.2.0
61+
*/
62+
@AliasFor("value")
63+
String[] files() default {};
64+
65+
/**
66+
* One or more packages that should be excluded from the classpath.
67+
* @return the excluded packages
68+
* @since 3.2.0
4669
*/
47-
String[] value();
70+
String[] packages() default {};
4871

4972
}

spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathClassLoader.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.ArrayList;
2727
import java.util.Arrays;
2828
import java.util.Collection;
29+
import java.util.HashSet;
2930
import java.util.LinkedHashSet;
3031
import java.util.List;
3132
import java.util.Map;
@@ -56,6 +57,7 @@
5657
import org.springframework.core.annotation.MergedAnnotation;
5758
import org.springframework.core.annotation.MergedAnnotations;
5859
import org.springframework.util.AntPathMatcher;
60+
import org.springframework.util.ClassUtils;
5961
import org.springframework.util.ConcurrentReferenceHashMap;
6062
import org.springframework.util.ObjectUtils;
6163
import org.springframework.util.StringUtils;
@@ -74,10 +76,14 @@ final class ModifiedClassPathClassLoader extends URLClassLoader {
7476

7577
private static final int MAX_RESOLUTION_ATTEMPTS = 5;
7678

79+
private final Set<String> excludedPackages;
80+
7781
private final ClassLoader junitLoader;
7882

79-
ModifiedClassPathClassLoader(URL[] urls, ClassLoader parent, ClassLoader junitLoader) {
83+
ModifiedClassPathClassLoader(URL[] urls, Set<String> excludedPackages, ClassLoader parent,
84+
ClassLoader junitLoader) {
8085
super(urls, parent);
86+
this.excludedPackages = excludedPackages;
8187
this.junitLoader = junitLoader;
8288
}
8389

@@ -87,6 +93,10 @@ public Class<?> loadClass(String name) throws ClassNotFoundException {
8793
|| name.startsWith("io.netty.internal.tcnative")) {
8894
return Class.forName(name, false, this.junitLoader);
8995
}
96+
String packageName = ClassUtils.getPackageName(name);
97+
if (this.excludedPackages.contains(packageName)) {
98+
throw new ClassNotFoundException();
99+
}
90100
return super.loadClass(name);
91101
}
92102

@@ -130,7 +140,7 @@ private static ModifiedClassPathClassLoader compute(ClassLoader classLoader,
130140
.map((source) -> MergedAnnotations.from(source, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY))
131141
.toList();
132142
return new ModifiedClassPathClassLoader(processUrls(extractUrls(classLoader), annotations),
133-
classLoader.getParent(), classLoader);
143+
excludedPackages(annotations), classLoader.getParent(), classLoader);
134144
}
135145

136146
private static URL[] extractUrls(ClassLoader classLoader) {
@@ -269,6 +279,17 @@ private static List<Dependency> createDependencies(String[] allCoordinates) {
269279
return dependencies;
270280
}
271281

282+
private static Set<String> excludedPackages(List<MergedAnnotations> annotations) {
283+
Set<String> excludedPackages = new HashSet<>();
284+
for (MergedAnnotations candidate : annotations) {
285+
MergedAnnotation<ClassPathExclusions> annotation = candidate.get(ClassPathExclusions.class);
286+
if (annotation.isPresent()) {
287+
excludedPackages.addAll(Arrays.asList(annotation.getStringArray("packages")));
288+
}
289+
}
290+
return excludedPackages;
291+
}
292+
272293
/**
273294
* Filter for class path entries.
274295
*/

spring-boot-project/spring-boot-tools/spring-boot-test-support/src/test/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathExtensionExclusionsTests.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2023 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.
@@ -19,29 +19,43 @@
1919
import org.hamcrest.Matcher;
2020
import org.junit.jupiter.api.Test;
2121

22+
import org.springframework.util.ClassUtils;
23+
2224
import static org.assertj.core.api.Assertions.assertThat;
2325
import static org.hamcrest.Matchers.isA;
2426

2527
/**
2628
* Tests for {@link ModifiedClassPathExtension} excluding entries from the class path.
2729
*
2830
* @author Christoph Dreis
31+
* @author Andy Wilkinson
2932
*/
30-
@ClassPathExclusions("hibernate-validator-*.jar")
33+
@ClassPathExclusions(files = "hibernate-validator-*.jar", packages = "java.net.http")
3134
class ModifiedClassPathExtensionExclusionsTests {
3235

3336
private static final String EXCLUDED_RESOURCE = "META-INF/services/jakarta.validation.spi.ValidationProvider";
3437

3538
@Test
36-
void entriesAreFilteredFromTestClassClassLoader() {
39+
void fileExclusionsAreFilteredFromTestClassClassLoader() {
3740
assertThat(getClass().getClassLoader().getResource(EXCLUDED_RESOURCE)).isNull();
3841
}
3942

4043
@Test
41-
void entriesAreFilteredFromThreadContextClassLoader() {
44+
void fileExclusionsAreFilteredFromThreadContextClassLoader() {
4245
assertThat(Thread.currentThread().getContextClassLoader().getResource(EXCLUDED_RESOURCE)).isNull();
4346
}
4447

48+
@Test
49+
void packageExclusionsAreFilteredFromTestClassClassLoader() {
50+
assertThat(ClassUtils.isPresent("java.net.http.HttpClient", getClass().getClassLoader())).isFalse();
51+
}
52+
53+
@Test
54+
void packageExclusionsAreFilteredFromThreadContextClassLoader() {
55+
assertThat(ClassUtils.isPresent("java.net.http.HttpClient", Thread.currentThread().getContextClassLoader()))
56+
.isFalse();
57+
}
58+
4559
@Test
4660
void testsThatUseHamcrestWorkCorrectly() {
4761
Matcher<IllegalStateException> matcher = isA(IllegalStateException.class);

0 commit comments

Comments
 (0)