Skip to content

Commit aaf118c

Browse files
committed
Fix TypeExcludeFiltersContextCustomer key
Update `TypeExcludeFiltersContextCustomer` to use filter instances as part of the key, rather than class references. In order to be used in tests, `TypeExcludeFilter` implementations must now implement valid `hashCode` and `equals` methods. Fixes gh-8125
1 parent 180ab2d commit aaf118c

File tree

10 files changed

+131
-86
lines changed

10 files changed

+131
-86
lines changed

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTypeExcludeFilter.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@
1616

1717
package org.springframework.boot.test.autoconfigure.data.mongo;
1818

19-
import java.io.IOException;
2019
import java.util.Collections;
2120
import java.util.Set;
2221

2322
import org.springframework.boot.context.TypeExcludeFilter;
2423
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
2524
import org.springframework.context.annotation.ComponentScan.Filter;
2625
import org.springframework.core.annotation.AnnotatedElementUtils;
27-
import org.springframework.core.type.classreading.MetadataReader;
28-
import org.springframework.core.type.classreading.MetadataReaderFactory;
2926

3027
/**
3128
* {@link TypeExcludeFilter} for {@link DataMongoTest @DataMongoTest}.
@@ -64,13 +61,12 @@ protected boolean isUseDefaultFilters() {
6461
}
6562

6663
@Override
67-
protected boolean defaultInclude(final MetadataReader metadataReader,
68-
final MetadataReaderFactory metadataReaderFactory) throws IOException {
69-
return false;
64+
protected Set<Class<?>> getDefaultIncludes() {
65+
return Collections.emptySet();
7066
}
7167

7268
@Override
73-
protected Set<Class<?>> getDefaultIncludes() {
69+
protected Set<Class<?>> getComponentIncludes() {
7470
return Collections.emptySet();
7571
}
7672

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/AnnotationCustomizableTypeExcludeFilter.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.core.type.classreading.MetadataReaderFactory;
2828
import org.springframework.core.type.filter.AnnotationTypeFilter;
2929
import org.springframework.core.type.filter.AssignableTypeFilter;
30+
import org.springframework.util.ObjectUtils;
3031

3132
/**
3233
* Abstract base class for a {@link TypeExcludeFilter} that can be customized using an
@@ -75,6 +76,11 @@ protected boolean defaultInclude(MetadataReader metadataReader,
7576
return true;
7677
}
7778
}
79+
for (Class<?> component : getComponentIncludes()) {
80+
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, component)) {
81+
return true;
82+
}
83+
}
7884
return false;
7985
}
8086

@@ -103,10 +109,50 @@ protected final boolean isTypeOrAnnotated(MetadataReader metadataReader,
103109

104110
protected abstract Set<Class<?>> getDefaultIncludes();
105111

112+
protected abstract Set<Class<?>> getComponentIncludes();
113+
106114
protected enum FilterType {
107115

108116
INCLUDE, EXCLUDE
109117

118+
}
119+
120+
@Override
121+
public int hashCode() {
122+
final int prime = 31;
123+
int result = 0;
124+
result = prime * result + ObjectUtils.hashCode(hasAnnotation());
125+
for (FilterType filterType : FilterType.values()) {
126+
result = prime * result
127+
+ ObjectUtils.nullSafeHashCode(getFilters(filterType));
128+
}
129+
result = prime * result + ObjectUtils.hashCode(isUseDefaultFilters());
130+
result = prime * result + ObjectUtils.nullSafeHashCode(getDefaultIncludes());
131+
result = prime * result + ObjectUtils.nullSafeHashCode(getComponentIncludes());
132+
return result;
133+
}
134+
135+
@Override
136+
public boolean equals(Object obj) {
137+
if (this == obj) {
138+
return true;
139+
}
140+
if (getClass() != obj.getClass()) {
141+
return false;
142+
}
143+
AnnotationCustomizableTypeExcludeFilter other = (AnnotationCustomizableTypeExcludeFilter) obj;
144+
boolean result = true;
145+
result &= hasAnnotation() == other.hasAnnotation();
146+
for (FilterType filterType : FilterType.values()) {
147+
result &= ObjectUtils.nullSafeEquals(getFilters(filterType),
148+
other.getFilters(filterType));
149+
}
150+
result &= isUseDefaultFilters() == other.isUseDefaultFilters();
151+
result &= ObjectUtils.nullSafeEquals(getDefaultIncludes(),
152+
other.getDefaultIncludes());
153+
result &= ObjectUtils.nullSafeEquals(getComponentIncludes(),
154+
other.getComponentIncludes());
155+
return result;
110156
};
111157

112158
}

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizer.java

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.IOException;
2020
import java.lang.reflect.Constructor;
21+
import java.util.Collections;
2122
import java.util.LinkedHashSet;
2223
import java.util.Set;
2324

@@ -40,48 +41,65 @@ class TypeExcludeFiltersContextCustomizer implements ContextCustomizer {
4041
private static final String EXCLUDE_FILTER_BEAN_NAME = TypeExcludeFilters.class
4142
.getName();
4243

43-
private final Class<?> testClass;
44-
45-
private final Set<Class<? extends TypeExcludeFilter>> filterClasses;
44+
private final Set<TypeExcludeFilter> filters;
4645

4746
TypeExcludeFiltersContextCustomizer(Class<?> testClass,
4847
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
49-
this.testClass = testClass;
50-
this.filterClasses = filterClasses;
48+
this.filters = instantiateTypeExcludeFilters(testClass, filterClasses);
49+
}
50+
51+
private Set<TypeExcludeFilter> instantiateTypeExcludeFilters(Class<?> testClass,
52+
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
53+
Set<TypeExcludeFilter> filters = new LinkedHashSet<TypeExcludeFilter>();
54+
for (Class<? extends TypeExcludeFilter> filterClass : filterClasses) {
55+
filters.add(instantiateTypeExcludeFilter(testClass, filterClass));
56+
}
57+
return Collections.unmodifiableSet(filters);
58+
}
59+
60+
private TypeExcludeFilter instantiateTypeExcludeFilter(Class<?> testClass,
61+
Class<?> filterClass) {
62+
try {
63+
Constructor<?> constructor = getTypeExcludeFilterConstructor(filterClass);
64+
ReflectionUtils.makeAccessible(constructor);
65+
if (constructor.getParameterTypes().length == 1) {
66+
return (TypeExcludeFilter) constructor.newInstance(testClass);
67+
}
68+
return (TypeExcludeFilter) constructor.newInstance();
69+
}
70+
catch (Exception ex) {
71+
throw new IllegalStateException("Unable to create filter for " + filterClass,
72+
ex);
73+
}
5174
}
5275

5376
@Override
5477
public int hashCode() {
55-
return this.filterClasses.hashCode();
78+
return this.filters.hashCode();
5679
}
5780

5881
@Override
5982
public boolean equals(Object obj) {
60-
return (obj != null && getClass().equals(obj.getClass()) && this.filterClasses
61-
.equals(((TypeExcludeFiltersContextCustomizer) obj).filterClasses));
83+
return (obj != null && getClass().equals(obj.getClass()) && this.filters
84+
.equals(((TypeExcludeFiltersContextCustomizer) obj).filters));
6285
}
6386

6487
@Override
6588
public void customizeContext(ConfigurableApplicationContext context,
6689
MergedContextConfiguration mergedContextConfiguration) {
67-
if (!this.filterClasses.isEmpty()) {
90+
if (!this.filters.isEmpty()) {
6891
context.getBeanFactory().registerSingleton(EXCLUDE_FILTER_BEAN_NAME,
6992
createDelegatingTypeExcludeFilter());
7093
}
7194
}
7295

7396
private TypeExcludeFilter createDelegatingTypeExcludeFilter() {
74-
final Set<TypeExcludeFilter> filters = new LinkedHashSet<TypeExcludeFilter>(
75-
this.filterClasses.size());
76-
for (Class<? extends TypeExcludeFilter> filterClass : this.filterClasses) {
77-
filters.add(createTypeExcludeFilter(filterClass));
78-
}
7997
return new TypeExcludeFilter() {
8098

8199
@Override
82100
public boolean match(MetadataReader metadataReader,
83101
MetadataReaderFactory metadataReaderFactory) throws IOException {
84-
for (TypeExcludeFilter filter : filters) {
102+
for (TypeExcludeFilter filter : TypeExcludeFiltersContextCustomizer.this.filters) {
85103
if (filter.match(metadataReader, metadataReaderFactory)) {
86104
return true;
87105
}
@@ -92,20 +110,6 @@ public boolean match(MetadataReader metadataReader,
92110
};
93111
}
94112

95-
private TypeExcludeFilter createTypeExcludeFilter(Class<?> type) {
96-
try {
97-
Constructor<?> constructor = getTypeExcludeFilterConstructor(type);
98-
ReflectionUtils.makeAccessible(constructor);
99-
if (constructor.getParameterTypes().length == 1) {
100-
return (TypeExcludeFilter) constructor.newInstance(this.testClass);
101-
}
102-
return (TypeExcludeFilter) constructor.newInstance();
103-
}
104-
catch (Exception ex) {
105-
throw new IllegalStateException("Unable to create filter for " + type, ex);
106-
}
107-
}
108-
109113
private Constructor<?> getTypeExcludeFilterConstructor(Class<?> type)
110114
throws NoSuchMethodException {
111115
try {

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcTypeExcludeFilter.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@
1616

1717
package org.springframework.boot.test.autoconfigure.jdbc;
1818

19-
import java.io.IOException;
2019
import java.util.Collections;
2120
import java.util.Set;
2221

2322
import org.springframework.boot.context.TypeExcludeFilter;
2423
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
2524
import org.springframework.context.annotation.ComponentScan;
2625
import org.springframework.core.annotation.AnnotatedElementUtils;
27-
import org.springframework.core.type.classreading.MetadataReader;
28-
import org.springframework.core.type.classreading.MetadataReaderFactory;
2926

3027
/**
3128
* {@link TypeExcludeFilter} for {@link JdbcTest @JdbcTest}.
@@ -63,13 +60,12 @@ protected boolean isUseDefaultFilters() {
6360
}
6461

6562
@Override
66-
protected boolean defaultInclude(MetadataReader metadataReader,
67-
MetadataReaderFactory metadataReaderFactory) throws IOException {
68-
return false;
63+
protected Set<Class<?>> getDefaultIncludes() {
64+
return Collections.emptySet();
6965
}
7066

7167
@Override
72-
protected Set<Class<?>> getDefaultIncludes() {
68+
protected Set<Class<?>> getComponentIncludes() {
7369
return Collections.emptySet();
7470
}
7571

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,9 @@ protected Set<Class<?>> getDefaultIncludes() {
7777
return DEFAULT_INCLUDES;
7878
}
7979

80+
@Override
81+
protected Set<Class<?>> getComponentIncludes() {
82+
return Collections.emptySet();
83+
}
84+
8085
}

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTypeExcludeFilter.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@
1616

1717
package org.springframework.boot.test.autoconfigure.orm.jpa;
1818

19-
import java.io.IOException;
2019
import java.util.Collections;
2120
import java.util.Set;
2221

2322
import org.springframework.boot.context.TypeExcludeFilter;
2423
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
2524
import org.springframework.context.annotation.ComponentScan.Filter;
2625
import org.springframework.core.annotation.AnnotatedElementUtils;
27-
import org.springframework.core.type.classreading.MetadataReader;
28-
import org.springframework.core.type.classreading.MetadataReaderFactory;
2926

3027
/**
3128
* {@link TypeExcludeFilter} for {@link DataJpaTest @DataJpaTest}.
@@ -63,13 +60,12 @@ protected boolean isUseDefaultFilters() {
6360
}
6461

6562
@Override
66-
protected boolean defaultInclude(MetadataReader metadataReader,
67-
MetadataReaderFactory metadataReaderFactory) throws IOException {
68-
return false;
63+
protected Set<Class<?>> getDefaultIncludes() {
64+
return Collections.emptySet();
6965
}
7066

7167
@Override
72-
protected Set<Class<?>> getDefaultIncludes() {
68+
protected Set<Class<?>> getComponentIncludes() {
7369
return Collections.emptySet();
7470
}
7571

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/client/RestClientExcludeFilter.java

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package org.springframework.boot.test.autoconfigure.web.client;
1818

19-
import java.io.IOException;
19+
import java.util.Arrays;
2020
import java.util.Collections;
2121
import java.util.LinkedHashSet;
2222
import java.util.Set;
@@ -26,8 +26,6 @@
2626
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
2727
import org.springframework.context.annotation.ComponentScan.Filter;
2828
import org.springframework.core.annotation.AnnotatedElementUtils;
29-
import org.springframework.core.type.classreading.MetadataReader;
30-
import org.springframework.core.type.classreading.MetadataReaderFactory;
3129
import org.springframework.util.ClassUtils;
3230

3331
/**
@@ -65,20 +63,6 @@ class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
6563
RestClientTest.class);
6664
}
6765

68-
@Override
69-
protected boolean defaultInclude(MetadataReader metadataReader,
70-
MetadataReaderFactory metadataReaderFactory) throws IOException {
71-
if (super.defaultInclude(metadataReader, metadataReaderFactory)) {
72-
return true;
73-
}
74-
for (Class<?> controller : this.annotation.components()) {
75-
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, controller)) {
76-
return true;
77-
}
78-
}
79-
return false;
80-
}
81-
8266
@Override
8367
protected boolean hasAnnotation() {
8468
return this.annotation != null;
@@ -105,4 +89,9 @@ protected Set<Class<?>> getDefaultIncludes() {
10589
return DEFAULT_INCLUDES;
10690
}
10791

92+
@Override
93+
protected Set<Class<?>> getComponentIncludes() {
94+
return new LinkedHashSet<Class<?>>(Arrays.asList(this.annotation.components()));
95+
}
96+
10897
}

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTypeExcludeFilter.java

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package org.springframework.boot.test.autoconfigure.web.servlet;
1818

19-
import java.io.IOException;
19+
import java.util.Arrays;
2020
import java.util.Collections;
2121
import java.util.LinkedHashSet;
2222
import java.util.Set;
@@ -29,8 +29,6 @@
2929
import org.springframework.boot.web.servlet.FilterRegistrationBean;
3030
import org.springframework.context.annotation.ComponentScan.Filter;
3131
import org.springframework.core.annotation.AnnotatedElementUtils;
32-
import org.springframework.core.type.classreading.MetadataReader;
33-
import org.springframework.core.type.classreading.MetadataReaderFactory;
3432
import org.springframework.stereotype.Controller;
3533
import org.springframework.util.ObjectUtils;
3634
import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -74,20 +72,6 @@ class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
7472
WebMvcTest.class);
7573
}
7674

77-
@Override
78-
protected boolean defaultInclude(MetadataReader metadataReader,
79-
MetadataReaderFactory metadataReaderFactory) throws IOException {
80-
if (super.defaultInclude(metadataReader, metadataReaderFactory)) {
81-
return true;
82-
}
83-
for (Class<?> controller : this.annotation.controllers()) {
84-
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, controller)) {
85-
return true;
86-
}
87-
}
88-
return false;
89-
}
90-
9175
@Override
9276
protected boolean hasAnnotation() {
9377
return this.annotation != null;
@@ -117,4 +101,9 @@ protected Set<Class<?>> getDefaultIncludes() {
117101
return DEFAULT_INCLUDES;
118102
}
119103

104+
@Override
105+
protected Set<Class<?>> getComponentIncludes() {
106+
return new LinkedHashSet<Class<?>>(Arrays.asList(this.annotation.controllers()));
107+
}
108+
120109
}

0 commit comments

Comments
 (0)